Using NSUserDefaults to store default values at build time - ios

I am storing a small dictionary of default values which a user can modify later, but probably will only ever be changed once. NSUserDefaults.standardUserDefaults seems like the right place to store such a thing, my question is: Is there a way to store values at build time instead of runtime? This code seems like it should be unnecessary.
if !NSUserDefaults.standardUserDefaults().dictionaryForKey(default) {
NSUserDefaults.standardUserDefaults().setObject(defaultDictionary, forKey: "default")
}
Or, is there a better alternative I should be considering instead?

Register the default values as described in the documentation
Registering Your App’s Default Preferences
At launch time, an app should register default values for any
preferences that it expects to be present and valid. When you request
the value of a preference that has never been set, the methods of the
NSUserDefaults class return default values that are appropriate for
the data type. For numerical scalar values, this typically means
returning 0, but for strings and other objects it means returning nil.
If these standard default values are not appropriate for your app, you
can register your own default values using the registerDefaults:
method. This method places your custom default values in the
NSRegistrationDomain domain, which causes them to be returned when a
preference is not explicitly set.
When calling the registerDefaults: method, you must provide a
dictionary of all the default values you need to register. Listing 2-1
shows an example where an iOS app registers its default values early
in the launch cycle. You can register default values at any time, of
course, but should always register them before attempting to retrieve
any preference values.
Listing 2-1 Registering default preference values
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Register the preference defaults early.
NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:#"CacheDataAgressively"];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
// Other initialization...
}
When registering default values for scalar types, use an NSNumber object to specify the value for the
number. If you want to register a preference whose value is a URL, use
the archivedDataWithRootObject: method of NSKeyedArchiver to encode
the URL in an NSData object first. Although you can use a similar
technique for other types of objects, you should avoid doing so when a
simpler option is available.

To store values at build time, I would make a .plist or a .json file and pre-populate that with the values that you want to store that can then be accessed at runtime.

Related

NSUserDefaults registerDefaults to set initial BOOL - does existence of BOOL value return YES

I have this NSUserDefaults setup code
NSDictionary *appDefaults = [NSDictionary
dictionaryWithObject:[NSNumber numberWithBool:NO] forKey:#"DidBuyInAppPurchase"];
[[NSUserDefaults standardUserDefaults] registerDefaults:appDefaults];
I check later like so:
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"DidBuyInAppPurchase"] == YES){//some code}
Does this mean that a BOOL exists for the key or that the key was set to YES?
The odd behavior was that I wrote code in viewDidLoad that set the BOOL to YES just to test. Prior to doing this, the if-statement did not proceed. Now it does even when I remove the code that sets standardUserDefaults to YES is removed. Even if I remove the app and build and run on the device, the code proceeds.
This would indicate that the if check compares the YES to whether a boolean value exists there or not, and somehow the YES is persisting on app removal as well. However, the initial run of the app should have caught this too since a value existed. I checked this SO question and it seems that the if check should check for a YES value and not existence of a value.
This is for an IAP - when user purchases, the boolean is set to YES to show that it has been purchased, and if so, different viewControllers are presented to the user. How can I set this up properly so that setting to YES performs properly in if checks?
EDIT: Seems like this was in issue in Simulator on iOS 8 - the default not deleting from the Application domain. Not sure if this is the case here on the device in iOS 9 or I'm just using NSUserDefaults incorrectly.
When you use the -boolForKey: method, it internally does 2 things. First, it looks up an object for the key and second, it tries to unwrap a BOOL from an NSNumber object (returning that BOOL).
If that NSNumber is not present, the return will be "NO". If that NSNumber is present, the boolean the object is wrapping will be returned.
To check for the reason NSUserDefaults returned NO, you may call -objectForKey: with the same key and simply test the existence of an object.
Apple states, that the method you use to import predefined settings won't save anything, it just adds the dictionary's entries to the in-memory defaults. I have not seen your whole code on my small display... -registerDefaults:

Working with Integer values for NSUserDefaults

I am trying to create some settings for my application. One of which is a sort setting which can be 0,1,2. At present I have an enum to handle this:
typedef NS_OPTIONS(NSInteger, SearchSort) {
SearchSortDefaultBestMatched = 0,
SearchSortDistance = 1,
SearchSortHighestRated = 2
};
When a value is selected from a picker I update the [NSUserDefaults standardDefaults] setInteger:
However, when a user first runs the app there is no default set. What I am finding is that if check the [[NSUserDefaults standardUserDefaults] integerForKey:...]; this returns 0. Which would be incorrect. Is this the correct process to store this value/check if the defaults has a value? Ideally I would want nil instead of 0 as 0 would potentially be an option.
Unless the proposed solution is to of course store the values as 1,2,3 and translate back and forth.
NSUserDefaults has a method registerDefaults for this very purpose. you give it a dictionary of "default default" key/value pairs. Those become the initial values that you get when you ask for a given key that has not been set.
I always add my initial default values in the +initialize class method for my app delegate. That method gets called before the system alloc/inits the app delegate, so it gets called before any of your app's custom code (Aside from the code in main) gets called.
EDIT:
As #AdamEberbach suggests in his answer, you can also use objectForKey: instead of integerForKey. That will return nil if no value is stored, or an NSNumber (who's value may be zero) if a value has been stored.
Why not use objectForKey: instead, if you get a valid object then you can cast to NSNumber and get the integerValue: - else you get a nil.

Valueforkey in NSUserDefaults

I am new to IOS programming and am trying to modify some code a developer wrote for me. I'm having problems in the following code
NSUserDefaults *pref=[NSUserDefaults standardUserDefaults];
NSString *strUrl=[pref valueForKey:#"HistoryUrl"];
if (strUrl.length>0)
{
newUrl=strUrl;
}
else
{
newUrl=#"http://www.google.com";
}
The HistoryUrl parameter seems to have the value 'http://www.yahoo.com' stored in it. I've looked everywhere and searched the net on how to replace this value to google's address. I have even gone through all the code in XCode and can't find where historyurl is declared:
Where is HistoryUrl declared?
How can it be modified?
Thanks in advance!
#"HistoryUrl" is an NSString* containing the string HistoryURL. Thats' how you write an NSString* with fixed data.
pref is an object representing the user's preferences.
The user's preference contain multiple key - value pairs. For example there might be a key named "HistoryUrl" which might have some value.
The valueForKey: method reads the value that is stored under the key "HistoryUrl" and stores it into strUrl. If there is no key named "HistoryUrl" then the result will be nil. (The use of valueForKey: is strange, because it is not a method of NSUserDefaults itself; typically one would use objectForKey:)
The following code checks whether the value read has any characters in it (length is roughly speaking the number of characters); if there are any characters then newUrl is set to that value; if there were no characters then newUrl is set to the NSString* "http:/www.google.com".
So someone at some time has stored a value under the name "HistoryUrl" into the application's preference file. You remove that value by calling
[pref removeObjectForKey:#"HistoryUrl"]
Or, since you don't seem to want anything other than "google", remove all the code and just write NSString* newUrl = #"http://www.google.com" if that's what you want.
HistoryURL is an arbitrary key and it is being used in your code to retrieve a value from NSUserDefaults. At some stage in your code, you will want to use setObject:forKey: to update the value stored in NSUesrDefaults. You will also need to call synchronize to save the new value after it has been set.
Where is HistoryUrl declared?
It's not. #"HistoryUrl" is just a string. Read up on NSUserDefaults to learn how it all works, but in a nutshell the defaults system is like an associative array (also known as a dictionary or map), where you can key/value pairs. In this case, #"HistoryUrl" is the key, and #"http://www.yahoo.com" is the value. You can make up whatever keys you want for storing your values.
How can it be modified?
Do you want to modify the key, or the value associated with the key? If the former, just make up a different key and use it. If the latter, use the methods of NSUserDefaults to set a different value:
NSUserDefaults *pref=[NSUserDefaults standardUserDefaults];
[pref setObject:#"http://google.com" forKey:#"HistoryUrl"];
[pref synchronize];
Note: the -synchronize call isn't strictly necessary, as the system will generally write your change eventually. But a lot of people like to call it whenever they make a change to the defaults system.

Directly add to array in defaults

Is it okay to call this?
[[[NSUserDefaults standardUserDefaults] objectForKey:#"searchEnginesOrder"] addObject:#"eBay"];
I have a UITableView whose cells I want to load directly from the defaults, and I also want to modify those defaults. I'm wondering if the above line actually has any effect (when I NSLog the default's array searchEngineOrder it's null, I'm wondering if it's because the above code isn't actually adding to the defaults.
Generally speaking, that line of code would crash. User defaults returns immutable objects so calling addObject: will throw an exception.
You also shouldn't rely on changes made to returned objects being backed into the data store - this isn't user defaults specific, it goes for any API which doesn't document it as supported.
You should be separating your logic between your working data and your stored data with defined modifications and save points. Ensure that you mutable copy the data you extract from user defaults. You should also use registerDefaults: to setup initial values so you don't need to check for existence.
NSUserDefaults returns immutable objects. You have to create mutable copies in order to edit the values.
NSMutableDictionary *subSettings;
subSettings = [[[NSUserDefaults standardUserDefaults]
objectForKey:YOUR_SUBMAP_KEY] mutableCopy];
[subSettings setObject:YOUR_NEW_VALUE forKey:YOUR_VALUE_KEY];
[[NSUserDefaults standardUserDefaults]
setObject:subSettings forKey:YOUR_SUBMAP_KEY];
[subSettings release];

NSUserDefaults behaving erratically

Can I know how exactly NSUserDefaults works?
I'm using it to maintain user info like username.
In one controller I set :
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:#"xyz" forKey:#"username"];
and in another one I retrieve it as :
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
username = [prefs stringForKey:#"username"];
It works sometimes, but sometimes the setobject doesn't set anything ( username = [prefs stringForKey:#"username"]; gives me nil. Sometimes it works fine. I thought this was a persistent storage so I'm not sure what's happening. This is in simulator as I haven't got the chance to test it on a phone yet.
This is what Mac Developer Library is saying about NSUserDefaults
The NSUserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an application to customize its behavior to match a user’s preferences. For example, you can allow users to determine what units of measurement your application displays or how often documents are automatically saved. Applications record such preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults since they’re commonly used to determine an application’s default state at startup or the way it acts by default.
At runtime, you use an NSUserDefaults object to read the defaults that your application uses from a user’s defaults database. NSUserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default value. The synchronize method, which is automatically invoked at periodic intervals, keeps the in-memory cache in sync with a user’s defaults database.
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData. For more details, see Preferences and Settings Programming Guide.
Values returned from NSUserDefaults are immutable, even if you set a mutable object as the value. For example, if you set a mutable string as the value for "MyStringDefault", the string you later retrieve using stringForKey: will be immutable.
A defaults database is created automatically for each user. The NSUserDefaults class does not currently support per-host preferences. To do this, you must use the CFPreferences API (see Preferences Utilities Reference). However, NSUserDefaults correctly reads per-host preferences, so you can safely mix CFPreferences code with NSUserDefaults code.
If your application supports managed environments, you can use an NSUserDefaults object to determine which preferences are managed by an administrator for the benefit of the user. Managed environments correspond to computer labs or classrooms where an administrator or teacher may want to configure the systems in a particular way. In these situations, the teacher can establish a set of default preferences and force those preferences on users. If a preference is managed in this manner, applications should prevent users from editing that preference by disabling any appropriate controls.
Would u mind trying this.
[[NSUserDefaults standardUserDefaults] setObject:#"xyz" forKey:#"username"];
than doing like the way u are doing .
And Check the Value by using
NSLog(#"%#",[NSUserDefaults standardUserDefaults] objectForKey:#"username"]);

Resources