NSUserDefaults and registerDefault, a standard behavior? - cocoa

I need to store a numeric parameter in NSUserDefault, this parameter must be as default 1
So i write this code :
NSNumber *one = [NSNumber numberWithInt:100];
NSUserDefaults *def = [NSUserDefaults standardUserDefaults];
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
one,#"my-param",
nil];
[def registerDefaults:dict];
This parameter has a bind over a checkbox in IB so i can check that it's correctly set and when i start my app i see checkbox with state "on".
On the other side i must check this value programmatically so... i do something like :
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSInteger my_param = [defaults integerForKey:#"my-param"];
I excepted that since the value is not set by the user this function return value that i set as default (in my case "1") but with my surprise i found that if value has never been set by user it returns 0 ... as you can understand this's terrible :P because now i can't understand if this "0" is obtained by user choice of this's a consequence of a non-set value... how can i write code to manage this situation ?

To store the numeric values in NSUserDefaults you can simply directly use as follows:
To set the integer Value in NSUserDefault use as follows below:
NSInteger lInteger = 10;
[[NSUserDefaults standardUserDefaults] setInteger:lInteger forKey:#"integerkey"];
[[NSUserDefaults standardUserDefaults] synchronize];
And to use that NSInteger value anywhere in your project use as below:
NSInteger linteger = [[NSUserDefaults standardUserDefaults] integerForKey:#"integerkey"];
And i didnt understand your question exactly, but anyhow i think this above code may help you.

Related

NSUserDefaults: how to get only the keys I've set

All of the methods to get keys from NSUserDefaults return heaps of keys from domains other than the app itself (e.g., NSGlobalDomain). I just want the keys and values that my app has set. This is useful for debugging and verifying that there are no orphaned keys, etc.
I could ignore the keys that aren't mine (if I know all of them -- during development I may have set keys I'm no longer using), but there might be a collision of keys in other domains and I'll not see my app's value.
Other discussions suggest looking at the dictionary file associated with the app, but that's not very elegant.
How can I get only my app's keys form NSUserdefaults?
Elegant approach
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
NSDictionary *dict = [defaults persistentDomainForName:bundleIdentifier];
File approach:
NSString *bundleIdentifier = [[NSBundle mainBundle] objectForInfoDictionaryKey:#"CFBundleIdentifier"];
NSString *path = [NSString stringWithFormat:#"~/Library/Preferences/%#.plist",bundleIdentifier];
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[path stringByExpandingTildeInPath]];
NSArray *keys = [dict allKeys];
Tested with sandboxing.
Marek's code updated to Swift 5:
guard let bundleIdentifier = Bundle.main.bundleIdentifier else { return }
let dict = UserDefaults.standard.persistentDomain(forName: bundleIdentifier)

Can you save the position of a node in an sks file

I'm trying to create a SKScene file where we create empty nodes at the places where our players and other objects will be placed. The problem is that the positions all have to be set in code. Is it possible to set the position in the sks file instead?
I did notice there is a Position label in the node inspector, but that doesn't appear to have any effect.
Write data with NSUserDefaults:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSDictionary *nodeDict = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithFloat:myNode.position.x], #"posX",
[NSNumber numberWithFloat:myNode.position.y], #"posY",
nil];
[defaults setObject: nodeDict forKey:#"nodeDict"];
[defaults synchronize];
Read data with NSUserDefaults:
NSDictionary * nodeDict = [[NSUserDefaults standardUserDefaults] objectForKey:#"nodeDict"];
float posX = [[nodeDict objectForKey:#"posX"] floatValue];
float posY = [[nodeDict objectForKey:#"posY"] floatValue];
Obviously you can write a lot more data than the example above. If you have several nodes, create a dictionary for each one and store them all in an array. Write the array to NSUserDefaults.
Keep in mind that you can only store objects in a dictionary and not primitives like int, float, etc... That is why you have to convert them into NSNumber.

How to save integer from Singleton in an NSUserDefault to use after app is relaunched in Xcode 5?

I have a Singleton that keeps track of my "score" over all my ViewControllers, and that works great. But I want to save my "score" from my Singleton in an NSUserDefault so I can use it after the app has been relaunched. My state preservation works for everything, but keeping the current score. My Singleton looses its data when the app is closed and I understand NSUserDefault saves the data between launches. I save my "score" from my Singleton to "integer" in my NSUserDefault. I then try to display an image that is named 0.png, 1.png, 2.png and so on until 10.png. But this image is displayed as 0.png every time.
(I really hope my question isn't stupid. I have read tutorials and tried different things for a week, and this is my first question here.)
Please show me where my code goes wrong.
viewcontroller.m
//Save Singleton score to NSUserDefault integer
[super viewDidLoad];
optionsshared = [Singleton sharedSingletonManager];
integer = optionsshared.score;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:integer forKey:#"integer"];
[defaults synchronize];
// Set Image
ScoreImg.image = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png", integer]];
This code works:
//Set Singleton to number from NSUserDefault in case app was terminated
optionsshared = [Singleton sharedSingletonManager];
NSUserDefaults *getObj = [NSUserDefaults standardUserDefaults];
int Number = [((NSNumber*)[getObj objectForKey:#"stars"])intValue];
optionsshared.score = Number;
[super viewDidLoad];
//Set image from number in NSUserDefaults
ScoreImg.image = [UIImage imageNamed:[NSString stringWithFormat:#"%d.png", Number]];
}

avoid duplicate results on Core Data fetch

I have a subclass of the CoreDataTableViewController (subclass of UITAbleViewController dome by the people on Stanford done to link CoreData and TableViews). On this Class, I want to perform a fecth, sorting by an attribute called "definition" and the code which executes it is the following:
- (void)setupFetchedResultsController{
NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:self.entity];
request.propertiesToFetch=[NSArray arrayWithObject:#"definition"];
request.returnsDistinctResults=YES;
NSPredicate *predicate1 = [NSPredicate predicateWithFormat:#"%K != nil", #"definition"];
NSPredicate *predicate2 = [NSPredicate predicateWithFormat:#"%K != ''", #"definition"];
NSPredicate *predicate3= [NSPredicate predicateWithFormat:#"%K contains[cd] %#", #"definition", self.seachBar.text];
NSArray *prepredicateArray;
if ([self.seachBar.text length]) {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2, predicate3,nil];
}else {
prepredicateArray = [NSArray arrayWithObjects:predicate1, predicate2,nil];
}
request.predicate=[NSCompoundPredicate andPredicateWithSubpredicates:prepredicateArray];
request.sortDescriptors = [NSArray arrayWithObject:[NSSortDescriptor sortDescriptorWithKey:#"definition" ascending:YES ]];
self.fetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:request
managedObjectContext:self.managedObjectContext
sectionNameKeyPath:nil
cacheName:nil];
[self performFetch];
}
If I understood it correctly, setting request.returnsDistinctResults=YES; should avoid fetching duplicates. However it doesn't work and I'm seeing duplicates of this attribute's value.
Is there something I'm missing there? I'd appreciate some pointings there. Thank you in advance.
EDIT: If anyone is having the same issue here, after applying David's answer the resulting fetchedResultsController is just a NSDIctionary with object with only the requested value, which for displaying only purposes is quite fine. One thing I've done in cellForRowAtIndexPath in order to display the results on the cell label is:
Before:
HNMR *hnmr = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text=hnmr.definition;
After:
cell.textLabel.text=[[self.fetchedResultsController objectAtIndexPath:indexPath] valueForKey:#"definition"];
From the documentation of returnsDistinctResults:
This value is only used if a value has been set for propertiesToFetch.
From the documentation of propertiesToFetch:
This value is only used if resultType is set to NSDictionaryResultType.
From the documentation of resultType:
The default value is NSManagedObjectResultType.
This all tells me that the propertiesToFetch is ignored because you haven't set the resultType yourself and the default it to return managed objects instead of dictionaries. Since the propertiesToFetch is ignored the returnsDistinctResults is ignored as well and thus you are still getting duplicates.
Try setting the result type to return dictionaries instead of managed objects.
request.resultType = NSDictionaryResultType;
In addition to David Rönnqvist answer I suggest a useful link (with a sample) on selecting distinct values with Core Data:
core-data-how-to-do-a-select-distinct
Hope that helps.

Reading a plist

I'm trying to read ~/Library/Preferences/com.apple.mail.plist (on Snow Leopard) to get the email address and other information to enter into the about dialog. I'm using the following code, which is obviously wrong:
NSBundle* bundle;
bundle = [NSBundle mainBundle];
NSString *plistPath = [bundle pathForResource:#"~/Library/Preferences/com.apple.mail.plist" ofType:#"plist"];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSString *item = [plistData valueForKeyPath:#"MailAccounts.Item 2.AccountName"];
NSLog(#"Result = %#", item);
Moreover, the value I need to read is MailAcounts -> Item 2 -> AccountName and I am not sure I am doing this correctly (due to the space in the Item 2 key).
I tried reading Apple's developer guide to plist files but no help there.
How can I read a plist and extract the values as an NSString?
Thanks.
The first level is an array, so you need to use "MailAccounts.AccountName" and treat it as NSArray*:
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];
NSDictionary *plistData = [NSDictionary dictionaryWithContentsOfFile:plistPath];
NSArray *item = [plistData valueForKeyPath:#"MailAccounts.AccountName"];
NSLog(#"Account: %#", [item objectAtIndex:2]);
Alternatively you can go by keys and pull the array from "MailAccounts" first using valueForKey: (which will yield NSArray*) and then objectAtIndex: to get the dictionary of that particular account (useful if you need more than the name).
Two things:
You don't want or need to use NSBundle to get the path to the file. The file lies outside of the app bundle. So you should just have
NSString *plistPath = #"~/Library/Preferences/com.apple.mail.plist";
You have to expand the tilde in the path to the user directory. NSString has a method for this. Use something like
NSString *plistPath = [#"~/Library/Preferences/com.apple.mail.plist" stringByExpandingTildeInPath];

Resources