How do you store multiple UIControlState instances in NSUserDefaults? - iphone

I'm looking to storing multiple instances of UIControlState within NSUserDefaults on the IOS platform.
Let's say there is a preference panel with various UIControl elements, be they UISlider, UISwitch etc.
Currently I have a loadPrefs function within viewWillAppear and a corresponding writePrefs within viewWillDisappear.
Rather than adding the code to store the state within NSUserDefaults for any future control that I add, is there an easy way to iterate through all the keys in NSUserDefaults and set the relevant states accordingly?
Is there a "proper" way of doing this via an NSArray or some other collection or is it not worth the time and bother?

You can get the entire NSUserDefaults stuff as an NSDictionary by using the dictionaryRepresentation method:
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation];
for (NSString *key in [dict allKeys])
{
// do whatever you want
}
Hope this helps.

Related

NSUserDefaults: Question about difference between two approaches

Problem
I want to store a NSString in NSUserDefaults and retrieve it later. I have a question about two different retrieving methods. Now at the top of the file I have:
// String used to identify the update object in the user defaults storage.
static NSString * const kLastStoreUpdateKey = #"LastStoreUpdate";
Method 1
NSString *lastUpdate = [[NSUserDefaults standardUserDefaults] objectForKey:kLastStoreUpdateKey];
Method 2
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *myString = [prefs stringForKey:kLastStoreUpdateKey];
Are there are significant differences I should know about? Also, can someone please explain what exactly is objectForKey? Apple's API states: that it "Returns the object associated with the first occurrence of the specified default." What exactly do they mean by the "specified default?
Thank you!
Generally you should use method 1.
that is "objectForKey".
Because, you know that, whatever you have stored in NSUserDefault. So, at the time of retriving it, you can catch the object with proper class like NSString, Array or any other user defined.
genrally "stringForKey" is not used.
If you are storing ingteger, BOOL into NSUserDefault then you should use intForKey, BOOLforKey, etc..
Cheers.

Copying the contents of an NSMutableDictionary into another NSMutableDictionary?

I have an NSMutableDictionary each element of which is another dictionary. What is the best way I can copy its contents into another NSMutableDictionary? I've tried using:
firstDictionary = [NSMutableDictionary dictionaryWithDictionary:secondDictionary];
However not sure if this is the best way to do it.
You can also jump between mutable and non-mutable dictionaries using copy and mutableCopy.
- (void) someFunc:(NSMutableDictionary *)myDict {
NSDictionary *anotherDict = [myDict copy];
NSMutableDictionary *yetAnotherDict = [anotherDict mutableCopy];
}
Check the NSDictionary initWithDictionary:copyItems: method.
It it enables deep copying of elements thru calling copyWithZone: method of item's class. You will have to take care of copying the fields yourself within the method.
What do you mean by "best"?
Anyway, I listed some ways here:
firstDictionary =
[NSMutableDictionary
dictionaryWithDictionary:secondDictionary];
[[NSDictionary alloc]
initWithDictionary:secondDictionary];
//don't forget to release later
using deep copy
using shallow copy
Conform to NSCopying Protocol and do copyWithZone on every object.
If NsMutableDictionary contains another dictionary, which contains another dictionary,, then you need to do copyWithZone on each dictionary at all levels.

trouble with saving iPhone App variables of different types

As all who usually ask such questions I'm newbie in iPhone SDK programming and I've spend realy a lot of time to find the solution by my self.
So, I need to save a lot of app data of diff datatypes (bool,string,int,float,double,int arrays,double arrays and arrays of pointers) to file. There're many forms with fields, app settings etc. All that I need to save either user quit the app, or it was terminated unexpectedly. User could work with as much forms with fields, as it's possible. File is needed to open project with all filled forms and app sets on other device.
My trouble is that I couldn't find in what way I should wrilte all that data to file. I tried two diff ways. 1st: fill array with form's data, fill NSMutableArray with such arrays for every form. But for NSMutableArray there's a method (void)addObject:(id)anObject, and I didn't find how to get that (id)anObject from every array(or anything else) to add items.
The 2nd I've tried to use: NSMutableData. The same troubles: to fill with my data I need to convert them to Data ((void)appendData:(NSData *)otherData). In both cases objecs of that classes couldn't be filled correctly with my data. And I can't save it to file.
Maybe there's some better solution? Will be very apreciative for any help.
NSNumber is usually the way to go for basic stuff (integers, bools, and doubles/floats/etc). For example:
NSNumber *aBool = [NSNumber numberWithBool:YES];
NSNumber *aFloat = [NSNumber numberWithFloat:1.0f];
NSUserDefaults *settings = [NSUserDefaults standardUserDefaults];
[settings setValue:aBool forKey:#"some_key"];
The strings you can just add to NSUserDefaults because they are already objects (that conform to NSCoder, I believe), and for the NSArrays, just use the writeToFile: method:
NSArray *someArray = [NSArray arrayWithObjects:#"foo",#"bar",#"etc",nil];
[someArray writeToFile:#"filename" atomically:YES];

Is there a better way to put a bunch of stuff in NSUserDefaults?

I'm confused about NSUserDefaults on the iPhone.
I come from a C / asm background and the Objective-C stuff has been a learning experience.
I'm currently using NSUserDefaults to store some strings (for the names in a highscore table). I want to start implementing a "Save Game" type feature so if the user gets a call or exits out of the game for a moment, they can come back to it.
To recover the game, I've got a couple of BOOL flags, a couple of ints to store some necessary variables, but I'm stuck at an array I need to store.
I have a 50 element array of unsigned chars. I could move it to ints if it would make things easier, but I'm just not seeing it.
To work with NSUserDefaults I can setBool (already doing that), setFloat (again, already doing that), setInteger, and setObject.
Obviously I could declare keys for each element of the array and store them one by one with setInteger but that's really kludgy. What's the best way to tackle this? Instead of an array of unsigned chars, I somehow try to use an NSObject? Are there any good tutorials on NSObjects that I can read through to understand it better?
Would Property Lists fit better with what you are trying to achieve?
You can create dictionary, store value for each setting or an array, and then dump it to Property List. You can easily read them back or update the values based on keys.
http://developer.apple.com/documentation/Cocoa/Conceptual/PropertyLists/...
You can pass an NSArray or an NSDictionary to NSUserDefaults with setObject:forKey:. It works just fine. Obviously, your array of unsigned chars would have to become an an NSArray of NSNumbers, which implies some overhead, but it's probably your easiest option.
Try using an NSData object, which can be directly stored to NSUserDefaults
[[NSUserDefaults standardUserDefaults] setObject:[NSData dataWithBytes:array length:len]
forKey:#"someArray"];
Whoa there, you should really look into using Core Data (or just sqllite) to store game state. NSUserDefaults is for what is says - defaults, configuration settings. It is not really meant to store largish chunks of binary data.
To convert a 50 element C array of unsigned chars in to an NSArray which you can store in a NSUserDefaults using:
NSMutableArray* a = [NSMutableArray new];
for ( int i = 0; i < 50; ++i ) {
[a addObject:[NSNumber numberWithInteger:yourArray[i]]];
}
[[NSUserDefaults standardUserDefaults] setObject:a forKey:#"theArray"];
[a release];
to get it back:
NSArray* a = [[NSUserDefaults standardUserDefaults] objectForKey:#"theArray"];
for ( int i = 0; i < 50; ++i ) {
yourArray[i] = [[a objectAtIndex:i] integerValue];
}
There is certainly no problem with using NSUserDefaults to store your game state, not when it is so small. If your game state was humoungous, you might want to investiage storing it as a plist file, but NSUserDefaults will not break a sweat handling this trivial amount of data.

inconsistant defaults (NSUserDefaults) behavior in iPhone simulator defaults

I've been experiencing very inconsistent results while developing an iPhone app and trying to save preferences via the standard NSUserDefaults mechanism. I am using code almost straight out of the iPhone Developer's Cookbook by Erica Sadun (fantastic book btw), it looks like this:
(void) updateDefaults
{
NSMutableArray *spells = [[NSMutableArray alloc] init];
NSMutableArray *locs = [[NSMutableArray alloc] init];
for (DragView *dv in [boardView subviews])
{
[spells addObject:[dv whichSpell]];
[locs addObject:NSStringFromCGRect([dv frame])];
}
[[NSUserDefaults standardUserDefaults] setObject:spells forKey:#"spells"];
[[NSUserDefaults standardUserDefaults] setObject:locs forKey:#"locs"];
[[NSUserDefaults standardUserDefaults] synchronize];
[spells release];
[locs release];
}
The values are saved, sometimes...and restored, sometimes. I can't get an exact bead on what does or does not make it work.
Does anyone else have any similar experiences? Any suggestions on what might make it work? Is the synchronize method the best way to force a disk write and make the values save, or is there something better (both for production, as well as simulator).
Thanks
Ryan
You should be using an NSKeyedArchiver for saving your arrays, such as:
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:spells] forKey:#"spells"];
[[NSUserDefaults standardUserDefaults] setObject:[NSKeyedArchiver archivedDataWithRootObject:locs] forKey:#"locs"];
[[NSUserDefaults standardUserDefaults] synchronize];
You should also make sure your spells class implements the NSCoding protocol (encodeWithCoder: and initWithCoder:), if it's a custom class. It looks like your locs are NSStrings, which will archive just fine.
You'll also need to do something like
NSData *dataRepresentingSavedSpells = [currentDefaults objectForKey:#"spells"];
if (dataRepresentingSavedSpells != nil)
{
NSArray *oldSpells = [NSKeyedUnarchiver unarchiveObjectWithData:dataRepresentingSavedSpells];
}
To load the old values from the defaults.
I use synchronize to write to disk on exit, and it's been very reliable in my experience.
On Mac OS X, and probably also on iPhone OS, the defaults database can only contain property list objects: NSString, NSNumber, NSArray, NSDictionary, and NSData.
It's not clear from your code snippet what type a spell is, but if it isn't one of the above, it won't get stored in the defaults database. Your options are:
Write a method that will convert your object to and from a plist representation. In other words, a method to create an NSDictionary from your object and another method to initialize the object using an NSDictionary. You could also choose to store it as an NSArray or NSString.
Make your object implement the NSCoding protocol and then used NSKeyedArchiver to convert the object to an NSData object, and NSKeyedUnarchiver to convert it back.