NSUserDefaults behaving erratically - ios

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"]);

Related

NSUserDefaults sometimes returns wrong value

I save the user profile picture ID of a user as a string in NSUserDefaults e.g. #"12". When I do that, I call the synchronize method immediately.
When I read this value from NSUserDefaults, it returns #"12" in maybe 99% of the time. But sometimes, it returns a different value (which I cannot find due to the rarity of the event, but suspect it is either nil or some default value (?)).
The code I use to write/read is very simple:
NSUserDefaults* userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:#"12" forKey:#"photoID"];
[userDefaults synchronize];
NSString* photoID=[userDefaults objectForKey:#"photoID"];
I know the value sometimes returned is incorrect because the app at the time behaves as if the value was different (i.e. user contacts are notified the profile picture has changed).
And when that happens, the next call to objectForKey returns the correct value, so user contacts received another notification the profile picture has changed again.
NSSynchronize is not guaranteed to succeed and returns a BOOL.
Note that syncrhonize simply writes the data to disk but NSUserDefaults keeps the data in memory as well. Calling it after every write is probably not needed, though I've done it myself and many examples on the net do that.
From the Apple documentation:
use this method only if you cannot wait for the automatic synchronization (for example, if your application is about to exit) or if you want to update the user defaults to what is on disk even though you have not made any changes.
Disclaimer: speculation
It's possible that the act of failed synchronize is causing objectForKey to fail. The class knows the data it has may be wrong and so returns nil instead. This is more likely if you are calling synchronize successively and excessively or otherwise doing IO intensive operations.

Using NSUserDefaults to store default values at build time

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.

Alternative to using a Singleton or AppDelegate for global data storage

I have an app that needs access to a set of user data that can't be stored on disk. My typical approach in the past would be to create a singleton to hold this data with a concurrent queue for each property to make data reads/writes thread safe.
What I am wondering is if there is a way to do this without the use of Singletons or storing a reference to my user data in my AppDelegate.
NSUserDefaults sounds like what you're looking for. According to Apple's documentation, "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." You can use NSUserDefaults in a global manner.
To do this, you need to first create an NSUserDefaults object:
NSUserDefaults *myAppDefaults = [NSUserDefaults myAppDefaults];
To save data to the defaults system, you would do something similar to the line below:
[standardDefaults setObject:#"Smith" forKey:#"lastName"];
Finally, you can retrieve your data from the defaults system by storing it in a variable. The line below shows how to set an NSString to be the value you originally stored:
NSString *lastName = [standardDefaults stringForKey:#"lastName"];

If once value assigned to a variable in objective c while application install then how to access that value at every time of opening an application?

Hi i am new to objective C.
If once value assigned to a variable in objective c while application install then how to access that value at every time of opening an application? And i tried with extern, static. Each will assign and sets the value at first time. If i rerun the application in emulator, it is not taking that last assigned value.
If anything is possible other than File system storage ? Is it possible with static or extern variables.
Scenario:
While install "extern int test" is assigned to 10 then it changed to 20. While accessing from another class, test reflects 20. If i rerun the app, "test" is showing "10"
But i want to access the last assigned the value (like static in java)
Thanks for any help !
Use NSUserDefaults to set the value in AppDelegate :
NSUserDefaults defaults = [NSUserDefaults standardUserDefaults];
[defaults setInteger:20 forKey:#"abc"];
[defaults synchronize];
TO get the value again, Use this method :
NSUserDefaults defaults = [NSUserDefaults standardUserDefaults];
var = [defaults integerForKey:#"abc"];
After closing the app the data stored in variables do not persist (of all classes), so you need to use NSUserDefaults (in your case)
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs.
At runtime, you use an NSUserDefaults object to read the defaults that your application uses from a user’s defaults database.
user's defaults database persists when app is killed.
Use NSUSerDefaults value will remain saved until application is deleted
[[NSUserDefaults standardUserDefaults] setValue:obj1 forKey:key1];
[[NSUserDefaults standardUserDefaults] synchronize];
and access value like
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
id obj = [defaults objectForKey:key1];
Because all variables of Objective C program are stored in memory, none of them survives application termination. If you need the value to be available after your app restarts, you have several options:
Use the file system - this one is the most straightforward thing to do. Cocoa provides APIs for writing strings to files in a single go, so your code would be short and simple.
Use user defaults - This API helps you persist values for reuse in an organized way.
Use keychain APIs - This API lets you save small amounts of data that must be encrypted. This is probably an overkill in your scenario.
All the variables are store in Memory once will restart your application the memory is released by ARC and allocated by new one memory address
Can user FileSystem or NSUserDefaults in objective c

Best way to store user information for my iOS app

What kind of database do you suggest? I want to store user email, username, password, and a couple other random pieces of information. It doesn't have to be fancy. Just a simple database. Are there any free options?
The user information needs to be stored in the keychain to keep it secure.
Any other information could be stored in any one of:
User defaults NSUserDefaults
File on disk (maybe a plist)
Database Core Data (technically just a file on disk)
Which you choose depends on what the data is, how much there is and what kind of access you need to it.
If your data is small and chosen by the user as some kind of setting then user defaults makes sense and is the lowest cost for you to implement.
To use a database, check out Core Data intro.
Wain is right but I think as you want to store small amount of data for further use, the most efficient ways is to use NSUserDefault.
NSUserDefault stores data in NSDictionary type things.
I think this is the step you have to take:
1- check if data exists. I mean if user selected the number if the last run of your app. So in viewDidLoad method:
NSMutableDictionary *userDefaultDataDictionary = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:ALL_DATA_KEY] mutableCopy];
if (userDefaultDataDictionary) {
// so the dictionary exists, which means user has entered the number in previous app run
// and you can read it from the NSDictionaty:
if(userDefaultDataDictionary[LABLE_KEY]){
//and store it
}
}
2 - you can implement some method like syncronize to store data in NSUserDefault every time something has been changed.
- (void) synchronize
{
NSMutableDictionary *dictionaryForUserDefault = [[[NSUserDefaults standardUserDefaults] dictionaryForKey:ALL_DATA_KEY] mutableCopy];
if(!dictionaryForUserDefault)
dictionaryForUserDefault = [[NSMutableDictionary alloc] init];
dictionaryForUserDefault[LABLE_KEY] = //data you want to store
[[NSUserDefaults standardUserDefaults] setObject:dictionaryForUserDefault forKey:ALL_DATA_KEY];
[[NSUserDefaults standardUserDefaults] synchronize];
}
P.S. and don't forget to #define your keys for your dictionary:
#define LABLE_KEY #"Lables"
#define ALL_DATA_KEY #"AllData"
Store it in a plist. If you're talking about data pertaining to one or a few users, that's probably the easy thing. here is a simple example.
Since you say database, store in Sqlite. There's some provided stuff for it already in xcode.
The entire database is contained in one file, which can be moved around if you need to.
Here is some more information on how to use one in your app.

Resources