Using NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];,
I use calls like BOOL boolFromPrefs = [defaults boolForKey:#"theBoolKey"]; To get a saved BOOL value.
If the key is not found, NO is returned (default behaviour of boolForKey).
But... NO can be a saved setting. Same thing for intForKey
So how can I test if a key exists before trying to get its value ?
Do it the right way and register default values.
NSDictionary *userDefaultsDefaults = #{
#"SomeKey": #NO,
#"AnotherKey": #"FooBar",
#"NumberKey": #0,
};
[NSUserDefaults.standardUserDefaults registerDefaults:userDefaultsDefaults];
do this before you use anything from NSUserDefaults. The beginning of application:didFinishLaunchingWithOptions: is a safe place.
You have to register the defaults each time the app launches. NSUserDefaults only stores values that have been explicitly set.
If you use default values you don't have to use a check for a "isFirstLaunch" key, like suggested in other answers.
This will help you when you roll out an update and you want to change the default value for a NSUserDefaults item.
Check if the object exists before conversion to a BOOL.
if ([defaults objectForKey:#"theBoolKey"] != nil) {
boolFromPrefs = [defaults boolForKey:#"theBoolKey"];
} else {
boolFromPrefs = DEFAULT_BOOL_VALUE;
}
I did it this way:
NSUserDefaults *prefs = NSUserDefaults.standardUserDefaults;
if ([[prefs dictionaryRepresentation].allKeys containsObject:#"yourKey"]) {
float yourValue = [prefs floatForKey:#"yourKey"];
}
You can test by using objectForKey: and if that is nil then it is not set. All boolForKey does it takes the NSNumber returned if any and returns a BOOL value.
I would recommend setting default values for any key that your application might use. You could do this in the application:didFinishLaunchingWithOptions: method. That way you will know that each value has been set.
Hint, set a key called "defaultsSet" to YES so that you only do this once. Also, remember to call [[NSUserDefaults standardUserDefaults] synchronize] to save the values.
Swift Example based on MarkPowell's answer
if (NSUserDefaults.standardUserDefaults().objectForKey("SomeBoolSetting") == nil) {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "SomeBoolSetting")
println("Bool WAS nil")
} else {
var boolValue:Bool? = NSUserDefaults.standardUserDefaults().boolForKey("SomeBoolSetting")
println("Bool WAS NOT nil \(boolValue)")
}
swift version of #Tim Autin 's answer:
if contains(NSUserDefaults.standardUserDefaults().dictionaryRepresentation().keys.array, "chuiser_cook_orders_dirty") {
println("exist")
}
Dont Complicate it :
if([NSUserDefaults.standardUserDefaults objectForKey:#"yourBoolKey"])
{
// Object Already Stored in User defaults
}
else
{
//Object not stored
}
Related
I'm working in a book app, and I want to use a page marker to help the user remember where he stopped reading. So I have added a bar button with an image ("mark.png"). A mark view will be added to the chapter view when it is tapped, and if it is been tapped again the mark will be removed from the superView. I'm using this code:
- (void)showMark {
if (![markView superView]) {
[chapterOne addSubView:markView];
}
else {
[markView removeFromSuperView];
}
}
It is working fine but ever time I exit the app and rerun again the mark view is gone, so how can I keep it?
I found some forums talking about the NSUserDefaults to save actions but I really don't know how to use it with my code. Any help will be appreciated.
You can't use NSUserDefaults to save entire views, but you can save the parmeters that would help determine where the bookmark should be set.
For example if you are basing the book mark by a page number you could save the page to the NSUserDefaults when the user leaves the view controller.
Example:
[[NSUserDefaults standardUserDefaults] setInteger:23 forKey:#"bookMarkPage"];
[[NSUserDefaults standardUserDefaults] synchronize];
When the user comes back the the view controller you can check if there is a bookmark:
if ([[NSUserDefaults standardUserDefaults] objectForKey:#"bookMarkPage"] != nil) {
int pageNumber = [[NSUserDefaults standardUserDefaults] objectForKey:#"bookMarkPage"];
[self setBookmarkForPage:pageNumber];
}
Possible bookmark construction method:
- (void) setBookmarkForPage:(Int)pageNumber {
// run through the logic of placing the bookmark on the correct page
}
You can use whatever parameters you need to determine where to place the book mark. When a user originally places the bookmark what parameters you use to figure out where to place the bookmark? Try to use the same logic for when a user first places the bookmark.
I don't know exactly what you want to save, but you can just about any kind of data with NSUserDefaults, like this:
[[NSUserDefaults standardUserDefaults] setInteger:123 forKey:#"CurrentPageNumber"];
When you have set all the values you need, save them:
[[NSUserDefaults standardUserDefaults] synchronize];
Then when app opens, check to see if the value is set. If it is draw your marker.
if ([defaults valueForKey:#"CurrentPageNumber"] != nil) {
int pageNumber = [defaults valueForKey:#"CurrentPageNumber"]
if (pageNumber == 1) {
[chapterOne addSubView:markView];
}
else {
[markView removeFromSuperView];
}
}
The other answers state great ways to work around the issue. Just to clarify, UIView or any of the derivatives are not supported for NSUserDefaults. NSUserDefaults allows just primitive object types (NSString, NSNumber, NSArray, and NSDictionary). There might be one or two I missed. But UIView or UIViewController object types can't be saved in NSUserDefaults.
I have a couple of images that I would like to display the first time a user runs the app to show the user how to effectively use the app. What is the best method to do this sort of thing?
Thanks
The easiest way is to set a flag that says you have shown the app instructions. You can store them in user defaults.
So you would put something like this in your app delegate.
static NSString* const kAppHasShownStartupScreen = #"kAppHasShownStartupScreen";
BOOL hasShownStartup = [[NSUserDefaults standardUserDefaults] boolForKey:kAppHasShownStartupScreen];
if(hasShownStartup)
{
window.rootViewController = //your normal startup view controller
}
else
{
window.rootViewController = //your new view controller with instructions
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:kAppHasShownStartupScreen];
}
Create some boolean values to store in NSUserDefaults or in Core Data that represent whether or not the user has viewed the 'tutorial'. Show the images by loading them into UIImageView's and adding them as subviews if the flag is false. Set the flag to true after they have viewed the images.
You can accomplish this functionality by using a BOOL value in NSUserDefaults:
#define appdb ((NSUserDefaults *)[NSUserDefaults standardUserDefaults])
if(![appdb boolForKey:#"applicationHasRunBefore"]) {
[appdb setBool:YES forKey:#"applicationHasRunBefore"];
[appdb synchronize];
...
// Do the tutorial
}
I load my data from NSUserDefaults and it works well.
But I'm a little concerned that the first time the app will be fun and there is no saved data something strange might happen. Is there a way to check that I am not getting back nil values or some kind of standard query to see what you are getting back is a valid answer?
Would putting a
ctCountry.isoCountryName = [prefs objectForKey:#"ctCountry.isoCountryName"];
if (ctCountry.isoCountryName == nil)
{
//code to handle noobject returned
}
be a valid and solid way to handle this kinda of work?
Current Code. No checks for data validity.
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
ctCountry.isoCountryName = [prefs objectForKey:#"ctCountry.isoCountryName"];
ctCountry.isoCountryCode = [prefs objectForKey:#"ctCountry.isoCountryCode"];
ctCountry.isoDialingCode = [prefs objectForKey:#"ctCountry.isoDialingCode"];
Thanks
-Code
Yes, checking for nil works. Better yet, you could register default values by passing a dictionary of the default preferences to -[NSUserDefaults registerDefaults] at every app launch.
Yes. It is not possible to store nil, so the only time ctCountry.isoCountryName == nil is when the corresponding preference does not exist.
When the rootViewController of my application is loaded, I want to be able to check whether or not the users login credentials have been saved to NSUserDefaults.
Basically, when the user loads the application and he/she doesn't have her login credentials saved, a modalAlertView will be pushed and the user will be able to save their credentials appropriately. This saves those UITextField strings to a respective NSUserDefault object. However, is it possible, that when this saving is done, I can create an NSUserDefault object which is a boolean and change the value to a yes?
Meaning that the boolean is already set to no, and when the user saves their login credentials, it also changes the boolean to a yes?
You can set your boolean by using:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"logged_in"];
[[NSUserDefaults standardUserDefaults] synchronize];
and read it by using this code:
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"logged_in"]) {
[self displayLogin];
} else {
[self displayMainScreen];
}
There is a method in NSUserDefaults called registerDefaults:. You use this method to set your application's "default defaults." Basically, you create an NSDictionary containing your default keys and values (in your case a NO for a "saved credentials" key), and you register it using registerDefaults:. This if often done in app delegate's + (void)initialize method to ensure that your defaults are registered before they are needed. These values only get used if your app hasn't replaced them. In other words, they won't be used unless the key you're looking for isn't in the Application Domain, i.e., the user defaults read from the user's .plist file.
On the other hand, you could just check for login credentials and pop up an alert if they're missing. This eliminates the need to keep your boolean value synchronized with the login credentials. If you later provide a "delete login credentials" capability, you won't have to remember to set the boolean back to NO. If your login credentials are saved in user's defaults, you'd do this:
NSString *userID = [[NSUserDefaults standardUserDefaults] stringForKey:#"userID"];
NSString *password = [[NSUserDefaults standardUserDefaults] stringForKey:#"password"];
if (userID != nil && password != nil) {
// Code to log user in
} else {
// Code to pop up an alert
}
This solution, suggested by Henrik P. Hessel:
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"logged_in"]) {
[self displayLogin];
} else {
[self displayMainScreen];
}
would work in your case, but bear in mind that you shouldn't check with this code if some key is set at all, because if it actually is set and set to "NO", you would still get a result as if was not set at all. Instead I would use
if([[NSUserDefaults standardUserDefaults] objectForKey:#"logged_in"] == nil) {
//Do something
}
You don't need to first set it to NO, instead you may check if a key has been set at all. If not, and if your app determines the credentials are complete, just create it and set it to YES.
Check my answer to another question to see how I usually do this.
in Swift 3
let Defaults = UserDefaults.standard()()
let exists = Defaults.bool(forKey: "isLoggedIn")
if !exists {
displayLoginScreen()
} else {
print("value exists..do something")
displayMainScreen()
}
alternatively we can use object(forKey:) then cast it to bool
if let exists = Defaults.object(forKey: "isLoggedIn"), let isLoggedIn = exists.boolValue where isLoggedIn == true {
displayMainScreen()
} else {
displayLoginScreen()
}
Swift 2.3
To store values
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "logged_in")
For fetching values
if NSUserDefaults.standardUserDefaults().boolForKey("logged_in") == true {
dashboardScreen()
}else {
loginScreen()
}
Swift 3.0
To store values
UserDefaults.standard().setBool(true, forKey: "logged_in")
to retrieve values
UserDefaults.standard().bool(forKey: "logged_in"){
dashboardScreen()
}else {
loginScreen()
}
I've just added a settings bundle to my app and am having trouble reading the bool settings. I know that upon launch of the app, the settings are not read unless the user actually enters them - and that's what I am trying to capture.
However, my code is simply capturing if the answer is NO OR they havent been set. I need to find out if they've been set, THEN set answers!
setting code:
BOOL playSound;
BOOL playVibrate;
//test for some defaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if (![prefs boolForKey:#"pref_sound"]) {
playSound = YES;
playVibrate = YES;
} else {
playSound = [prefs boolForKey:#"pref_sound"];
playVibrate = [prefs boolForKey:#"pref_vibrate"];
}
if (playSound) {
//do stuff
}
the problem is, if the user sets the settings to "NO", the code then changes both vibrate AND sound to yes - which is meant to be the capture for NOT setting....
any ideas?
First of all you have a bug in your if conditional. You're checking the boolean value itself which, according to the boolForKey: documentation, will return a NO if it's not set yet. So boolForKey: is not the right way to do that.
Here's two other ideas.
Consider using another setting with another key to specify whether your settings have been initialized. Check it when you launch your app, or when you first read a setting. Initialize if needed. For instance:
- (void) initializeUserDefaults {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (nil == [defaults objectForKey:#"initialized_defaults"]) {
[defaults setBool:YES forKey:#"pref_sound"];
[defaults setBool:YES forKey:#"pref_vibrate"];
[defaults setObject:#"dummy_value" forKey:#"initialized_defaults"];
}
}
A simpler solution (but I'm not sure if this would work) would be to change your conditional to read:
if (![prefs objectForKey:#"pref_sound"]) {
Again I don't know if this will do what I imagine it will, but I imagine that objectForKey: will return the underlying boxed boolean object, if it's there, or nil.
If you add a new setting in a new version of your app, you don't want to leave your new settings uninitialized and you don't want to stomp your users' existing settings. This second method makes that effortless, and in the first method it's easier to screw up. But the first method also gathers all your settings in one place so you can see how it's supposed to work, which I like.
I am not sure what ADC docs would consider a best practice though. To find that out, I'd suggest you look at any code samples referenced from the NSUserDefaults class reference.
maybe the best idea is to register some defaults for the NSUserDefaults using
(void)registerDefaults:(NSDictionary *)dictionary
see Apple Documentation at
https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/index.html#//apple_ref/doc/uid/20000318-SW5
To come back to your original problem: in the past I was using NSUserDefaults.dictionaryRepresentation in order to check the existence of a key:
BOOL playSound;
BOOL playVibrate;
//test for some defaults
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
if (nil == [prefs dictionaryRepresentation]["pref_sound"]) {
playSound = YES;
playVibrate = YES;
} else {
playSound = [prefs boolForKey:#"pref_sound"];
playVibrate = [prefs boolForKey:#"pref_vibrate"];
}
if (playSound) {
//do stuff
}
Hope this works for you.
Kevin Conner's solution works for me, but there's another thing I think I should point out.
My app has a preference for handedness with right-handed as the default. When I hit run in Xcode, use the app to change my preference to left-handed, and then run it a second time, my preference is still right-handed.
It seems that running in Xcode erases settings. I decided to manually quit the app from inside the simulator, open it again from inside, and then I saw my new preferences.