220

I'm working on a small iPhone app, and I am using NSUserDefaults as my data persistence. It only has to keep track of a few things, such as some names and some numbers so I figure I might as well keep it simple.

I found this page for some reference, but I don't think it can answer my question. Basically, I want to be able to check if a value (or a key) already exists in the NSUserDefaults and then do something accordingly.

Some examples: The app starts up, if this is the first time it starts up it outputs an alert saying welcome. To tell if this is first time it has opened it reads the UserDefaults and checks.

Example 2: It says, "Hello [Name]", where Name is something you have entered. If you have opened the app and there is no name, it should say "Hello World." I need to check if you have entered a name already and act accordingly. The name would be stored in NSUserDefaults.

Some help here? I'd really appreciate it!

TheNeil
  • 3,321
  • 2
  • 27
  • 52
Ethan Mick
  • 9,517
  • 14
  • 58
  • 74

11 Answers11

402

objectForKey: will return nil if it doesn't exist.

dreamlax
  • 93,976
  • 29
  • 161
  • 209
jspcal
  • 50,847
  • 7
  • 72
  • 76
  • 1
    I don't think you can store a primitive data type in the NSUserDefaults. – kender Oct 16 '11 at 18:55
  • 15
    Apple's docs say that "If a boolean value is associated with defaultName in the user defaults, that value is returned. Otherwise, NO is returned." I don't think the above answer is correct for BOOLs, you can't determine if it's defined NO or doesn't exist. I think you'd have to use `– dictionaryRepresentation` and check for the key. – zekel Feb 13 '12 at 20:13
  • 44
    @zekel Rather than guessing, I tested this (on iOS 5.1.1), and it definitely detected whether or not a BOOL was present, independent of what the value of said BOOL might be. "objectForKey" returned nil when the BOOL was not present because it had never been set. – DataGraham Jul 30 '12 at 16:27
  • 3
    I third the notion, I just tested this and it can tell if the object exists, even if it's a BOOL set to NO. – Sonny Parlin Mar 20 '13 at 19:06
  • 9
    If you have a BOOL and test it with boolForKey, then @zekel is right, you get YES or NOT. If you test it with objectForKey (as the answer suggests) you get nil if the key is not set. – Giuseppe Garassino Jul 11 '13 at 15:31
  • 2
    This no longer works at least as of iOS 6.1 Simulator. objectForKey returns the same value if it's not present and if it is present with a BOOL of NO. i.jameelkhan's solution does work – lschult2 Aug 01 '13 at 00:31
  • My guess would be that `-boolForKey:` will return `NO` both when the key does not exist, and when it exists but is set to `NO`. `objectForKey:`, on the other hand, should return an `NSNumber` instance (containing the boolean value YES or NO) if the key exists, or `nil` otherwise. Need to confirm this, though... – Nicolas Miari Aug 21 '13 at 14:24
  • 1
    @NicolasMiari It doesn't work any more. `-objectForKey:` returns valid object even if no value have ever been set for the corresponding key. – tonytony Nov 12 '13 at 09:25
  • @tonytony Well, that is annoying and plain inconsistent with the interface of `NSDictionary`! (which I guess `NSUserDefaults` is not, but still...) – Nicolas Miari Nov 12 '13 at 09:28
  • @NicolasMiari Nevermind, I fixed silly threading error and now it seems to work as expected. – tonytony Nov 12 '13 at 17:59
  • 1
    This works even for Xcode 6.2, iOS 8.1 and `BOOL`. But you have to explicitly check for `nil`. – Dirk Müller Jan 27 '15 at 13:03
  • There is an exception regarding *stringForKey*: If you use iOS *7.1* it will return *"(null)"* as a string representation while in iOS *8.1+* it will return *nil* - it can easily breaks your algorithm so pay attention. – OhadM Aug 31 '15 at 12:27
  • 1
    TFM says `nil` if no value exists. https://developer.apple.com/library/ios/documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/#//apple_ref/occ/instm/NSUserDefaults/objectForKey: `objectForKey:` *Returns the object associated with the first occurrence of the specified default.* `Return Value` *The object associated with the specified key, or `nil` if the key was not found.* – ta.speot.is Nov 14 '15 at 09:19
  • It's working in Objective C even with a BOOL. – Sabrina Dec 06 '21 at 16:54
109

As mentioned above it wont work for primitive types where 0/NO could be a valid value. I am using this code.

NSUserDefaults *defaults= [NSUserDefaults standardUserDefaults];
if([[[defaults dictionaryRepresentation] allKeys] containsObject:@"mykey"]){

    NSLog(@"mykey found");
}
i.jameelkhan
  • 1,215
  • 1
  • 9
  • 10
  • This is the correct answer when dealing with primitives like `BOOL`. It will accurately distinguish between `NO` and not set, unlike `objectForKey:`. – devios1 Oct 26 '15 at 21:48
  • @devios1 - If the key is missing, `objectForKey:` will return `nil` regardless of the programmer's intent to eventually store a `Bool` or any other data type. When a primitive is present, `objectForKey:` does not return `nil` even if the key is associated with a primitive value. – Ted Hopp Feb 12 '17 at 07:34
  • This is the right answer : obviously, the accepted answer is wrong since objectForKey confuses 0 with nil, so it can't work. Tested successfully from iOS 4.3 to 10.2.1 – Chrysotribax Mar 22 '17 at 17:38
  • I know this is old, but having only just now needed this information, I need to point out that the 'containsObject:' reference means just that: the object. NOT the key. IOW, in your header file if you've defined: #define kMyKey @"myKey" the 'containsObject' is not looking for 'kMyKey', it's looking for 'myKey'. Using 'kMyKey' will always return 'NO.' – Bill Norman Jul 19 '19 at 14:20
  • 1
    This is the slowest, and most cumbersome way to do this. you convert a whole .plist structure into an NSDictionary, just to find a single key? better use the NSUserDefaults controller standard machinery. – Motti Shneor Apr 01 '21 at 19:07
61

The objectForKey: method will return nil if the value does not exist. Here's a simple IF / THEN test that will tell you if the value is nil:

if([[NSUserDefaults standardUserDefaults] objectForKey:@"YOUR_KEY"] != nil) {
    ...
}
dreamlax
  • 93,976
  • 29
  • 161
  • 209
mirap
  • 1,266
  • 12
  • 23
8

Swift 3 / 4:

Here is a simple extension for Int/Double/Float/Bool key-value types that mimic the Optional-return behavior of the other types accessed through UserDefaults.

(Edit Aug 30 2018: Updated with more efficient syntax from Leo's suggestion.)

extension UserDefaults {
    /// Convenience method to wrap the built-in .integer(forKey:) method in an optional returning nil if the key doesn't exist.
    func integerOptional(forKey: String) -> Int? {
        return self.object(forKey: forKey) as? Int
    }
    /// Convenience method to wrap the built-in .double(forKey:) method in an optional returning nil if the key doesn't exist.
    func doubleOptional(forKey: String) -> Double? {
        return self.object(forKey: forKey) as? Double
    }
    /// Convenience method to wrap the built-in .float(forKey:) method in an optional returning nil if the key doesn't exist.
    func floatOptional(forKey: String) -> Float? {
        return self.object(forKey: forKey) as? Float
    }
    /// Convenience method to wrap the built-in .bool(forKey:) method in an optional returning nil if the key doesn't exist.
    func boolOptional(forKey: String) -> Bool? {
        return self.object(forKey: forKey) as? Bool
    }
}

They are now more consistent alongside the other built-in get methods (string, data, etc.). Just use the get methods in place of the old ones.

let AppDefaults = UserDefaults.standard

// assuming the key "Test" does not exist...

// old:
print(AppDefaults.integer(forKey: "Test")) // == 0
// new:
print(AppDefaults.integerOptional(forKey: "Test")) // == nil
stef
  • 952
  • 10
  • 15
5

Extend UserDefaults once to don't copy-paste this solution:

extension UserDefaults {

    func hasValue(forKey key: String) -> Bool {
        return nil != object(forKey: key)
    }
}

// Example
UserDefaults.standard.hasValue(forKey: "username")
mbelsky
  • 6,093
  • 2
  • 26
  • 34
4

"objectForKey will return nil if it doesn't exist." It will also return nil if it does exist and it is either an integer or a boolean with a value of zero (i.e. FALSE or NO for the boolean).

I've tested this in the simulator for both 5.1 and 6.1. This means that you cannot really test for either integers or booleans having been set by asking for "the object". You can get away with this for integers if you don't mind treating "not set" as if it were "set to zero".

The people who already tested this appear to have been fooled by the false negative aspect, i.e. testing this by seeing if objectForKey returns nil when you know the key hasn't been set but failing to notice that it also returns nil if the key has been set but has been set to NO.

For my own problem, that sent me here, I just ended up changing the semantics of my boolean so that my desired default was in congruence with the value being set to NO. If that's not an option, you'll need to store as something other than a boolean and make sure that you can tell the difference between YES, NO, and "not set."

JamesKVL
  • 57
  • 1
  • 1
  • I've confirmed this, but there is an easy solution; just use the new object literals or a boxed expression. `@0` instead of `0`, `@NO` instead of `NO`, or simply `@(variable)`. [Read about them here.](http://clang.llvm.org/docs/ObjectiveCLiterals.html) – kaka Jul 11 '13 at 10:14
  • 2
    A bit late, but for the benefit of newbies: this is incorrect. object(forKey) on UserDefault values of integers set to 0, and Bools set to false, will correctly return non-nil. If you use bool(forKey) to test if a value is set you can run into problems (because if the value is set to False, bool(forKey) will return 'false', even though you are expecting 'true'.) – thecloud_of_unknowing Jan 21 '17 at 12:02
3

I just went through this, and all of your answers helped me toward a good solution, for me. I resisted going the route suggested by, just because I found it hard to read and comprehend.

Here's what I did. I had a BOOL being carried around in a variable called "_talkative".

When I set my default (NSUserDefaults) object, I set it as an object, as I could then test to see if it was nil:

//converting BOOL to an object so we can check on nil
[defaults setObject:@(_talkative) forKey:@"talkative"];

Then when I went to see if it existed, I used:

if ([defaults objectForKey:@"talkative"]!=nil )
  {

Then I used the object as a BOOL:

if ([defaults boolForKey:@"talkative"]) {
 ...

This seems to work in my case. It just made more visual sense to me.

james Burns
  • 864
  • 2
  • 9
  • 22
3

Try this little crumpet:

-(void)saveUserSettings{
NSNumber*   value;

value = [NSNumber numberWithFloat:self.sensativity];
[[NSUserDefaults standardUserDefaults] setObject:value forKey:@"sensativity"];
}
-(void)loadUserSettings{
    NSNumber*   value;
    value = [[NSUserDefaults standardUserDefaults] objectForKey:@"sensativity"];
    if(value == nil){
        self.sensativity = 4.0;
    }else{
        self.sensativity = [value floatValue];
    }
}

Treat everything as an object. Seems to work for me.

pepelkod
  • 56
  • 3
3

Swift version to get Bool?

NSUserDefaults.standardUserDefaults().objectForKey(DefaultsIsGiver) as? Bool
Ben
  • 3,832
  • 1
  • 29
  • 30
  • 1
    Why not use `boolForKey`? `NSUserDefaults.standardUserDefaults().boolForKey(DefaultsIsGiver)` – JAL Mar 17 '16 at 22:44
  • 2
    `boolForKey` will return `Bool` and not `Bool?` , so if the key is not there you will get `false` and not `nil` – Ben Mar 17 '16 at 22:56
1

In Swift3, I have used in this way

var hasAddedGeofencesAtleastOnce: Bool {
    get {
        return UserDefaults.standard.object(forKey: "hasAddedGeofencesAtleastOnce") != nil
    }
}

The answer is great if you are to use that multiple times.

I hope it helps :)

Nikhil Manapure
  • 3,748
  • 2
  • 30
  • 55
-1

Swift 3.0

if NSUserDefaults.standardUserDefaults().dictionaryRepresentation().contains({ $0.0 == "Your_Comparison_Key" }){
                    result = NSUserDefaults.standardUserDefaults().objectForKey(self.ticketDetail.ticket_id) as! String
                }
Kiran Jasvanee
  • 6,362
  • 1
  • 36
  • 52