Doing math on NSUserDefaults and re saving - Not working - iphone

I am running the following code to save the total bytes sent:
NSUInteger bytes = 10;
int newBytes = bytes+[[NSUserDefaults standardUserDefaults] integerForKey:#"TotalBytes"];
NSLog(#"newBytes %i", newBytes);
[[NSUserDefaults standardUserDefaults] setInteger:newBytes forKey:#"TotalBytes"];
[[NSUserDefaults standardUserDefaults] synchronize];
NSLog(#"%i", [[NSUserDefaults standardUserDefaults] integerForKey:#"TotalBytes"]);
The Math seems to work fine, newBytes logs out the correct number, then when I run the synchronise command, the NSLog again returns the correct value. However when this code runs a second time, the key value is still set at its previous value, and hasn't saved.
Am I doing something really stupid here? Help!?!

#mootymoots: there's really nothing wrong in your code, i tried it both in simulator and iTouch, it write and read the new +10 value... i tried also to start my app directly on iTouch (not via xCode) and when i run again via xCode the value was +10 * nTimes i used the app...
so... are you sure that you are writing on standardUserDefaults JUST with this code?
try "menu:edit:find:find in project" in xcode and search for standardUserDefaults...
and you can also try "menu:build:clean all targets"...

Related

How to prevent log message "skipping setting already-present value for key"

When storing values in NSUserDefaults:
[[NSUserDefaults standardUserDefaults] setValue:myValue forKey:#"myKey"];
... if value was already existing, Xcode 8 will log:
2016-07-14 09:59:04.081806 MyAppName[52232:2561291] [User Defaults] CFPrefsPlistSource<0x7941d950> (Domain: com.myAppBundle, User: kCFPreferencesCurrentUser, ByHost: No, Container: (null)) skipping setting already-present value for key myKey
1) Can I ask Xcode to not log this? [main question]
2) Or should I compare equality of previously stored values before using setValue:forKey:? [subsidiary question]
Example:
if (![[NSUserDefaults standardUserDefaults] valueForKey:#"myKey"] isEqual:myValue])
[NSUserDefaults standardUserDefaults] setValue:myValue forKey:#"myKey"];
3) Or should I perform this compare with a thread-safe lock to avoid storing twice the same value between the instruction where I compare and the instruction where I use setValue:forKey:? [subsidiary question]
Example:
#synchronized ([NSUserDefaults standardUserDefaults]) {
if (![[NSUserDefaults standardUserDefaults] valueForKey:#"myKey"] isEqual:myValue])
[NSUserDefaults standardUserDefaults] setValue:myValue forKey:#"myKey"];
}
Issue was with Beta 1 and Beta 2 only. Fixed with Xcode 8 beta 3:
When debugging applications in the Simulator, the OS will not produce an excessive amount of
unhelpful logging. (26652255)
Also, more logging can be removed by editing each scheme and adding a specific environment variable, OS_ACTIVITY_MODE to disable:

ExternalAccessory and NSUserDefaults strange issue

I am working with ExternalAccessory FW and able to pass data to the peripheral device.
I am facing a strange situation, when trying to save some value to NSUserDefaults I'm getting this (looks like) unrelated error:
UISupportedExternalAccessoryProtocols = com.xxxxx.yyy.zz
2013-07-24 17:01:39.181 myApp[791:907] ERROR - opening session failed
2013-07-24 17:01:39.182 myApp[791:907] ERROR - /SourceCache/ExternalAccessory/ExternalAccessory-213.3/EASession.m:-[EASession dealloc] - 137 unable to close session for _accessory=0x2088cdb0 and sessionID=65536
And connection with peripheral is dead.
The code that writes to NSUserDefaults:
NSInteger transactionId = [[NSUserDefaults standardUserDefaults] integerForKey:kTransactionId];
transactionId = transactionId % NSIntegerMax + 1;
[[NSUserDefaults standardUserDefaults] setInteger:transactionId forKey:kTransactionId];
[[NSUserDefaults standardUserDefaults] synchronize];
If I comment out [[NSUserDefaults standardUserDefaults] setInteger:transactionId forKey:kTransactionId]; everything works just fine...
Any idea?
NSUserDefaults works in the main thread. Perhaps you want to use something different, maybe an NSMutableDictionary that you then serialize to disk.

Storing values from NSUserDefaults in Settings bundle

I am trying to find out how to save/store my values from NSDefaults so that when I exit the application they are stored in the Settings.bundle. This is what I am doing...
NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:#"M1", #"IDMissiles",
#"G2", #"IDGuns",
#"B3", #"IDBombs",
#"KM", #"IDDistance", nil];
[[NSUserDefaults standardUserDefaults] registerDefaults:settings];
If I do the following, the values print out correctly from NSUserDefaults ...
NSLog(#"IDMissiles: %#", [userDefaults stringForKey:#"IDMissiles"]);
NSLog(#"IDGuns : %#", [userDefaults stringForKey:#"IDGuns"]);
NSLog(#"IDBombs : %#", [userDefaults stringForKey:#"IDBombs"]);
NSLog(#"IDDistance: %#", [userDefaults stringForKey:#"IDDistance"]);
However ... Each time I run the application the values in NSUserDefaults start off as (null), I was thinking that doing [[NSUserDefaults standardUserDefaults] synchronize]; would store the values for the next time I run the application, but no such luck.
Instead of using
[[NSUserDefaults standardUserDefaults] registerDefaults:settings];
try this:
[[NSUserDefaults standardUserDefaults] setObject:settings forKey:#"settings"];
Then, get from defaults like this:
NSLog(#"IDMissiles:%#[[[NSUserDefaultsstandardUserDefaults]objectForKey:#"settings"]objectForKey:#"IDMissiles"]);
One thing I discovered when working with the settings.bundle is that none of the values get initialized until you actually open the settings pane. You can have default values saved there, but they will return nil until you open the settings.
I'm not sure if this happens when you try and save values there but never open the settings pane.
If you are not using a settings pane, then you wouldn't want to use the registerDefaults option.
Try this instead.
[[NSUserDefaults standardDefaults] setObject:#"M1" forKey:#"IDMissiles"];
// set remaining values
[[NSUserDefaults standardDefaults] synchronize]; // this really only needs to be called if you plan on accessing values right away, otherwise they are saved automatically after the next run loop
From the documentation:
The contents of the registration domain are not written to disk; you need to call this method each time your application starts. You can place a plist file in the application's Resources directory and call registerDefaults: with the contents that you read in from that file.
In other words, you aren't storing anything by registering defaults like this. To have default values both in your app and in the settings bundle, you have to maintain the settings bundle separately as discussed here.
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[NSUserDefaults removeObjectForKey:#"userDefaults"];
[userDefaults setObject:[settings objectForKey:#"mainData"] forKey:#"userDefaultsValue"];
[userDefaults synchronize];

Unexpected results from NSUserDefaults boolForKey

After uninstalling an application completely from the device and then loading it in the debugger, I am attempting in a setup method to load a flag using boolForKey. The first time the app runs I have the expectation that the bool will not exist, since I have just reinstalled the app. I expect from the documentation that boolForKey will therefore return NO.
I am seeing the opposite though. boolForKey is returning YES, which fubars my initial user settings. Any idea why this might be happening or a good way around it?
BOOL stopAutoLogin = [[NSUserDefaults standardUserDefaults] boolForKey:#"StopAutoLogin"];
_userWantsAutoLogin = !stopAutoLogin;
So stopAutoLogin comes out as "YES", which is completely unexpected.
Stranger and stranger: When I call objectForKey:#"StopAutoLogin" I get a nil object, as expected. It's just the boolForKey that returns a bad value. So I changed the code to this:
// this is nil
NSObject *wrapper = [[NSUserDefaults standardUserDefaults] objectForKey:#"StopAutoLogin"];
// this is YES
BOOL stopAutoLogin = [[NSUserDefaults standardUserDefaults] boolForKey:#"StopAutoLogin"];
please try [UserDefaults synchronize];
Because this method is automatically invoked at periodic intervals, 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.
please see: http://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSUserDefaults_Class/Reference/Reference.html
Do you register the default values for your keys?
NSMutableDictionary *appDefaults = [NSMutableDictionary dictionaryWithCapacity:1];
[appDefaults setObject:#"NO" forKey:kReloadOnStartKey];
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults registerDefaults:appDefaults];
If there is no registration domain,
one is created using the specified
dictionary, and NSRegistrationDomain
is added to the end of the search
list.
The contents of the registration
domain are not written to disk; you
need to call this method each time
your application starts. You can place
a plist file in the application's
Resources directory and call
registerDefaults: with the contents
that you read in from that file.
See this link for more information.

Objective C - Problem with objectForKey

Okay, I'm trying to write a high score function for my app.
My problem is that when no high score has been saved yet, my program crashes.
If I save it with:
[[NSUserDefaults standardUserDefaults] setObject:#"[given string]" forKey:#"firstName"];
first, it works fine. However, if I start up the program for the first time and try to view the high scores with the following code:
first = [[NSString alloc] initWithString:[[NSUserDefaults standardUserDefaults] objectForKey:#"firstName"]];
bad things happen.
Basically, is there away to see if nothing yet exist under firstName? Is there a way to initialize without erasing any name that might already be present?
Thanks.
The NSString documentation for initWithString: says
Parameters
aString
The string from which to copy characters. This value must not be nil.
The documentation for objectForKey: says
Return Value
The object associated with the
specified key, or nil if the key was
not found.
The problem seems to be that there is a nil returned when you try to retrieve firstName that doesn't exist yet and try to create a NSString with it as input.
The NSUserDefaults instance method registerDefaults: is meant for exactly this purpose: You can set default values for your preferences that will be overridden by any other value set for the same preference key. Just make sure to call it early enough that it will run before any code that needs to access your preferences.
You could load "first" like this:
first = [[[NSUserDefaults standardUserDefaults] objectForKey:#"firstName"] retain];
if (!first) {
// set default or do something else if there wasn't a value saved
first = #"N/A";
}