0

I am trying to see if a user has made a purchase by retrieving the NSUserDefaults value for each product identifier. I save the value when a product is purchased and it is getting set correctly.

The key-value pair show up correctly when calling this:

NSLog(@"%@", [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]);

As this:

"com.COMPANY.PRODUCTIDENTIFIER" = 1;

But when I try to actually validate that specific key-value it always returns null:

    for (NSString * productIdentifier in _productIdentifiers) {
        NSLog(@"ProductIdentifier: %@",productIdentifier);
        NSLog(@"Defaults Value: %@",[[NSUserDefaults standardUserDefaults] valueForKey:productIdentifier]);
        NSLog(@"Defaults Bool: %d",[[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier]);
        NSLog(@"Defaults Object: %@",[[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier]);
        BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
        if (productPurchased) {
            [_purchasedProductIdentifiers addObject:productIdentifier];
            NSLog(@"Previously purchased: %@", productIdentifier);
        } else {
            NSLog(@"Not purchased: %@", productIdentifier);
        }
    }

Results in this:

2013-03-10 17:45:13.609 COMPANY[1140:907] ProductIdentifier:  com.COMPANY.PRODUCTIDENTIFIER
2013-03-10 17:45:13.611 COMPANY[1140:907] Defaults Value: (null)
2013-03-10 17:45:13.612 COMPANY[1140:907] Defaults Bool: 0
2013-03-10 17:45:13.611 COMPANY[1140:907] Defaults Object: (null)   

NOTE - The above methods are both called in the same method in this order, so it isn't a problem with the app not loading the defaults yet or anything like that since the value is correctly shown through the 'dictionaryRepresentation' method. Also, Company and Product Identifiers have been replaced for privacy. There are no typos in the real app so that is not the issue.

UPDATE

Ok so this is interesting. If I use a static string as the key, it returns the correct value. But using the 'productIdentifier' (which is NSString) does not, even though logging the 'productIdentifier' it is the exact same string... Any Ideas???

Code:

    for (NSString *productIdentifier in _productIdentifiers) {
        NSLog(@"ProductIdentifier: %@",productIdentifier);
        NSLog(@"Defaults Value: %@",[[NSUserDefaults standardUserDefaults] valueForKey:productIdentifier]);
        NSLog(@"Defaults Bool: %d",[[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier]);
        NSLog(@"Defaults Object: %@",[[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier]);
        NSLog(@"Static Key: %d",[[NSUserDefaults standardUserDefaults] boolForKey:@"com.COMPANY.PRODUCTIDENTIFIER"]);
        BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
        if (productPurchased) {
            [_purchasedProductIdentifiers addObject:productIdentifier];
            NSLog(@"Previously purchased: %@", productIdentifier);
        } else {
            NSLog(@"Not purchased: %@", productIdentifier);
        }
    }

Result:

2013-03-10 17:45:13.609 COMPANY[1140:907] ProductIdentifier:  com.COMPANY.PRODUCTIDENTIFIER
2013-03-10 17:45:13.611 COMPANY[1140:907] Defaults Value: (null)
2013-03-10 17:45:13.612 COMPANY[1140:907] Defaults Bool: 0
2013-03-10 17:45:13.611 COMPANY[1140:907] Defaults Object: (null)   
2013-03-10 17:45:13.611 COMPANY[1140:907] Static Key: 1   
JimmyJammed
  • 9,598
  • 19
  • 79
  • 146
  • 2
    Have you used `[[NSUserDefaults standardUserDefaults] synchronize]` after you saved the values? If you are accessing the saved values immediately after saving you may find that the `syncronized` will be necessary. – Rog Mar 11 '13 at 00:59
  • 2
    -synchronize will never help with issues while the app is running. It only applies to persisting the values outside the app, and is usually unnecessary even then. – Catfish_Man Mar 11 '13 at 01:06
  • @Rog There is no need to call `synchronize` except in the `applicationDidEnterBackground:` app delegate method. – rmaddy Mar 11 '13 at 03:52
  • yes, synchronize is called after the value is set (after purchase is completed and verified). Like I said, the issue is NOT saving the value. The value is clearly saved when spitting out all NSUserDefaults as dictionaryRepresentation. – JimmyJammed Mar 11 '13 at 20:09

2 Answers2

5

The problem is your use of valueForKey: instead of objectForKey:. valueForKey has different semantics (key-value coding), especially when used with a key that has dots in it.

Change this:

[[NSUserDefaults standardUserDefaults] valueForKey:productIdentifier]

to this:

[[NSUserDefaults standardUserDefaults] objectForKey:productIdentifier]

and it should work.

rmaddy
  • 314,917
  • 42
  • 532
  • 579
  • Are you running the app in the debugger? Are you killing the app through the debugger, running again, and finding that the value is missing? This is normal because if you do these things, `NSUserDefaults` probably hasn't been synchronized to disk yet and killing the app prevents this. Add a call to `[[NSUserDefaults standardUserDefaults] synchronize]` in your app delegate's `applicationDidEnterBackground:` method. Now make sure you send your app to the background before you kill it with the debugger. – rmaddy Mar 11 '13 at 23:55
  • No using it on both simulator and device with same results (even after deleting and re-running the app). – JimmyJammed Mar 11 '13 at 23:59
  • I did just discover that using a static string for the call 'boolForKey' returns the correct result. But logging the 'productIdentifier' NSString is an exact match for the correct key (I just copied it pasted it from the log into the static key test). See above update in posting. – JimmyJammed Mar 12 '13 at 00:00
  • Are you killing the app and restarting using the "recently-used-apps" list? If so it's the same issue. Add the code I mentioned. – rmaddy Mar 12 '13 at 00:00
  • No. I am testing on an actual device, deleting the app, re-installing, etc. The problem is not the values being saved. They are saved. The problem is for some reason the 'productIdentifier' string variable is somehow not the same as using a static string. – JimmyJammed Mar 12 '13 at 00:02
  • 1
    From the log output the problem is clear (but not obvious). Your `productIdentifier` variable has some leading space. Change the log to: `NSLog(@"ProductIdentifier: '%@'",productIdentifier);`. Note the added quotes around the `%@`. You will then see the spaces. – rmaddy Mar 12 '13 at 00:04
  • Yeah I just found that when looking at the NSSet of product identifiers. So stupid!! Good catch though, not sure how I missed that. Thanks! – JimmyJammed Mar 12 '13 at 00:05
1

Problem was the product identifier in the NSSet being used had a leading whitespace so it was never a direct match.

JimmyJammed
  • 9,598
  • 19
  • 79
  • 146