Save id type object in NSUserDefaults - ios

I want to save id type object value in NSUserDefaults.
-(IBAction)sendOrderReady:(id)sender
{
NSUserDefaults *d = [NSUserDefaults standardUserDefaults];
[d setValue:sender forKey:#"sender"];
// [d setObject:sender forKey:#"sender"];
[d synchronize];
}

from the NSUserDefaults class description
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
so if your id object isn't one of the above instances, then you will have to convert it to one of them, and this question is what exactly you're looking for.

With NSUserDefaults you can save objects from the following class types:
NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary
If you want to store any other type of object then you need to archive it or wrap it in an instance of NSData

NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
// saving an NSString
[prefs setObject:#"iPhone" forKey:#"keyToLookupString"];
// saving an NSInteger
[prefs setInteger:442 forKey:#"integerKey"];
// saving a Double
[prefs setDouble:5.15 forKey:#"doubleKey"];
// saving a Float
[prefs setFloat:123.45678 forKey:#"floatKey"];
// This is suggested to synch prefs, but it is not needed
[prefs synchronize];

Related

Clear data of a particular NSUserDefaults object Data

In my project I am using NSUseDefaults for store data with the different objects.
NSUserDefaults *defaults1=[NSUserDefaults standardUserDefaults];
//---- I have set object for this
[defaults1 synchronize];
NSUserDefaults *defaults2=[NSUserDefaults standardUserDefaults];
//---- I have set object for this
[defaults2 synchronize];
Now I want clear all keys data only for defaults2, not for defaults1. So whenever I am applying below code:
NSDictionary *defaultsDictionary = [defaults2 persistentDomainForName: appDomain];
for (NSString *key in [defaultsDictionary allKeys]) {
NSLog(#"removing user pref for %#", key);
[[NSUserDefaults standardUserDefaults] removeObjectForKey:key];
}
Above code have remove value for defaults2 but also for defaults1. But I don't want to remove objects for defaults1. So please help me out.
NSUserDefaults is like a singelton class so it will always return the same shared system object.
You can store multiple objects using multiple keys and can delete/remove objects against those keys.
If you have read a doc about NSUserDefaults standardUserDefaults you should know that standardUserDefaults Returns the shared defaults object. and actually defaults1 and defaults2 the same.
You can store keys and then delete only those keys like:
NSUserDefaults *defaults1=[NSUserDefaults standardUserDefaults];
//---- I have set object for this
[defaults1 synchronize];
[[defaults1 dictionaryRepresentation] allKeys]; // use this keys for deleting

Saving NSdictionary in NSUserDefaults

I tried to save an NSDictionary in NSUSerDefaults, but I get the following error:
Attempt to insert non-property value
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if(defaults) {
[defaults setBool: YES forKey: #"disableGetStarted"];
[defaults setObject: [json mutableCopy] forKey: #"user"];
[defaults synchronize];
NSLog(#"defaults %#", [defaults objectForKey: #"user"]);
}
Where json is an NSDictionary.
What can I do?
json may be a dictionary but all of the contents of the dictionary must be legal values to be stored in user defaults. All must be instances of: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.
If you only store standard objects inside the dictionary like NSData, NSString, NSNumber, NSDate, NSArray, NSDictionary or a combination of them you don't have to do anything special.
However, if you have instances of custom objects in it (i.e. classes that you've created) you first need to convert it into a compatible type (e.g. NSData). You can do this using the code below:
NSData* data=[NSKeyedArchiver archivedDataWithRootObject:json];
[NSUserDefaults standardUserDefaults] setObject:data forKey:#"user"]
For this method to work, you ned to implement these 2 methods in the custom classes you are trying to save:
- (id)initWithCoder:(NSCoder*)coder
- (void)encodeWithCoder:(NSCoder*)coder;
To get the dictionary back from NSUserDefaults (decode) you can use:
NSData* data = [[NSUserDefaults standardUserDefaults] objectForKey:#"user"];
NSDictionary* json = [NSKeyedUnarchiver unarchiveObjectWithData:data];
EDIT
To check if your json object contains any [NSNull null] values, add this piece of code before you are making your insert into NSUserDefaults
for (id val in [json allValues])
{
if ([val isKindOfClass:[NSNull class]])
{
NSLog(#"This bad! NSNull should not be in the dictionary");
break;
}
}
If you get any This is bad... messages in the console, then you have 2 choices.
1. Use the archiving/unarchiving method I described above
2. Replace the NSNull objects from the dictionary with other values (e.g. empty strings) if this does not break your code.
One or more of the objects in your dictionary are not NSString, NSArray, NSDictionary, NSDate or NSData, the only objects that may be present. This code may help, it gives you more details why your dictionary is not valid:
- (BOOL)isValidPropertyList:(id)object {
//
// Check if `object` is a valid property list.
//
// # Return Value
// `YES` if the receiver is a valid property list, `NO` otherwise.
//
// # Discussion
// Intended to be called on NSArray and NSDictionary object to verify if the
// receiver is a valid property list. Will emit a description of any
// detected error.
//
NSData *xmlData;
NSString *error;
xmlData=[NSPropertyListSerialization
dataFromPropertyList:object
format:NSPropertyListXMLFormat_v1_0
errorDescription:&error
];
if(xmlData)
{
return YES;
}
else
{
NSLog(#"Tested object is not a valid property list: %#",error);
}
return NO;
}
NSDictionary *dict = [[NSDictionary alloc] init];
dict = json;
[NSUserDefaults standardDefaults] setObject:dict forKey:#"user"];
NSUserDefaults doesn't distinguish between mutable and immutable objects, so when you get it back it'll be immutable. So if you make a mutable dictionary by chance ->
[[NSUserDefaults standardDefaults] setObject:dict forKey:#"user"] mutableCopy];
All objects of the dictionary json must be instances of: NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary.

Possible to observe (KVO) every property of an NSObject?

I'm thingking of create a base class where every change made is immediately saved to NSUserDefaults (only KVO compilant parts of course), and automatically loads whenever that type of object is instantiated (a really basic, reusable user data store).
I have no intention to provide a "list of keys"-like constant to every subclass of this object, so I'm hoping that there is an automatic way to observe every property of an object.
Any ideas how to do this? With merely public API of course.
If you have a set of properties for an object, you can save them to a dictionary in NSUserDefaults. To save each property every time it is set you can create custom setter methods for each property using this:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"yourKey"] == nil) {
NSDictionary *dict = [NSDictionary dictionaryWithObject:#"yourObject" forKey:#"keyForYourObject"];
// Add stuff to the dictionary
[defaults setObject:dict forKey:#"yourKey"];
}else{
NSDictionary *dict = [defaults objectForKey:#"yourKey"];
// Add stuff to the dictionary
[defaults setObject:dict forKey:#"yourKey"];
}
This will give you a single dictionary stored in UserDefaults with all the properties. Then to get the list of keys use the standart method:
NSDictionary *dict = [[NSUserDefaults standardUserDefaults] objectForKey:#"yourKey"];
NSArray *keys = [dict allKeys];

Save NSArray to NSUserDefaults issues (using custom object in array)

I try to save my object to NSUserDefaults. But when I call this method again it is not have any info about previous operation.
There is my method below:
- (void)addToCart {
if([[NSUserDefaults standardUserDefaults] objectForKey:kCart]) {
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSMutableArray *products = [[NSMutableArray alloc] initWithArray:[prefs objectForKey:kCart]];
[products addObject:self.product];
[prefs setObject:products forKey:kCart];
[prefs synchronize];
[products release];
}
else {
//Saving...
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:[NSArray arrayWithObjects:self.product, nil] forKey:kCart];
[prefs synchronize];
}
}
I need to save a collection with a products to NSUserDefault. I wrap my object to NSArray and save it but it doesn't work.
Everything put into NSUserDefaults must be a valid property list object (NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary). All collection elements must themselves also be property list objects.
In order to save non-PL objects into NSUserDefaults, you must first convert the object into a PL object. The most generic way to do this is by serializing it to NSData.
Serializing to NSData is handled with NSKeyedArchiver. See Storing NSColor in User Defaults for the canonical example of this. (That document is very old and still references NSArchiver which will work fine for this problem, but NSKeyedArchiver is now the preferred serializer.)
In order to archive using NSKeyedArchiver, your object must conform to NSCoding as noted by #harakiri.
You need to conform to the <NSCoding> protocol and implement -initWithCoder: and -encodeWithCoder: in your custom object.
See: https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSCoding_Protocol/Reference/Reference.html

How do I store and retrieve an NSMutableDictionary to and from NSUserDefaults?

I tried to do this to store an empty dictionary in NSUserDefaults.
NSMutableDictionary* fruits = [NSMutableDictionary alloc];
[defaults setObject:fruits forKey:#"fruits"];
and then later this to retrieve it.
NSMutableDictionary* fruits = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:#"fruits"]];
However, retrieving the dictionary crashes my application. Why? How do I store a dictionary in NSUserDefaults?
You get a immutable dictionary back. You do not need to "capsulate" it in another dictionary. If you want to make it mutable write:
NSMutableDictionary* animals = [[defaults objectForKey:#"animals"] mutableCopy];
The NSUserDefaults class provides convenience methods for accessing common types such as floats, doubles, integers, Booleans, and URLs. A default object must be a property list, that is, an instance of (or for collections a combination of instances of): NSData, NSString, NSNumber, NSDate, NSArray, or NSDictionary. If you want to store any other type of object, you should typically archive it to create an instance of NSData.
Values returned from NSUserDefaults are immutable, even if you set a
mutable object as the value. For example, if you set a mutable string
as the value for "MyStringDefault", the string you later retrieve
using stringForKey: will be immutable.
Note: The user defaults system, which you programmatically access through the NSUserDefaults class, uses property lists to store objects representing user preferences. This limitation would seem to exclude many kinds of objects, such as NSColor and NSFont objects, from the user default system. But if objects conform to the NSCoding protocol they can be archived to NSData objects, which are property list–compatible objects. For information on how to do this, see ““Storing NSColor in User Defaults”“; although this article focuses on NSColor objects, the procedure can be applied to any object that can be archived.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/nsuserdefaults_Class/Reference/Reference.html
You can use:
Save:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:mutableArray];
[[NSUserDefaults standardUserDefaults] setObject:stack forKey:#"Your Key"];
Retrieve:
NSMutableArray *mutableArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
You need to init your dictionary and set is as object later. This way works, it's the same as your example but just with properly initialization.
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSMutableDictionary *dict = [[NSMutableDictionary alloc] initWithObjectsAndKeys:#"someValue", #"someKey", nil];
[defaults setObject:dict forKey:#"slovnik"];
[dict release];
NSLog(#"READ: %#", [defaults objectForKey:#"slovnik"]);
NSMutableDictionary *newDict = [[NSMutableDictionary alloc] initWithDictionary:[defaults objectForKey:#"slovnik"]];
NSLog(#"READ2: %#", newDict);
Now I get to log console and app do not crash:
2012-04-12 08:47:55.030 Test[12179:f803] READ: {
someKey = someValue;
}
2012-04-12 08:47:55.031 Test[12179:f803] READ2: {
someKey = someValue;
}
NSMutableDictionary* fruits = [NSMutableDictionary alloc];
should be
NSMutableDictionary* fruits = [[NSMutableDictionary alloc] init];
You need to always initialize objects after allocating them.

Resources