NSUserDefaults - storing custom objects with NSDictionary
Using [NSUserDefaults standardUserDefaults]
for storing some local data has became a very often practice.
However, you are able to successfully store only native data types such as:
NSData
NSString
NSNumber
NSDate
NSArray
NSDictionary
Here’s a hint how to handle serialization / deserialization in a bit different way, that you can reuse with Web services.
The original solution
proposed by Apple is that you implement
- (void)encodeWithCoder:(NSCoder *)encoder {
[encoder encodeObject:self.name forKey:@"name"];
[encoder encodeObject:self.surname forKey:@"surname"];
}
- (instancetype)initWithCoder:(NSCoder *)decoder {
self = [super initWithCoder:decoder];
if (self) {
_name = [decoder decodeObjectForKey:@"name"];
_surname = [decoder decodeObjectForKey:@"surname"];
}
return self;
}
And then when you want to store / retrieve it:
//storing
Person *person = //get the person
NSData *encodedPerson = [NSKeyedArchiver archivedDataWithRootObject:person];
[[NSUserDefaults standardUserDefaults] setObject:myEncodedObject forKey:@"encodedPersonKey"];
//retrieving
NSData *encodedPerson = [[NSUserDefaults standardUserDefaults] objectForKey:@"encodedPersonKey"];
Person *person = (Person *)[NSKeyedUnarchiver unarchiveObjectWithData:encodedPerson];
Basically, you’re converting your object here into NSData
and storing / reading it like that.
What this post is about - we’ll use the NSDictionary
instead of NSData
for doing the same job.
Let’s assume we have a Web service, and we’re receiving a Person (or people) remotely.
When they’re converted using the JSON (or XML) frameworks, they’re becoming an array of NSDictionaries
.
Our app is object oriented, so we don’t want to access the Person all around the app with
personDict[@"name"];
The proper way would be to have an Person object with it’s properties, so we would use it everywhere like
Person *person = //get the person
person.name
person.surname
To achieve that point, we need to deserialize the Person
from NSDictionary
received from JSON.
So, in addition to the code from the top, we would need also:
- (id)initWithDictionary:(NSDictionary *)personDict {
self = [super init];
if (self) {
self.name = personDict[@"name"];
self.surname = personDict[@"surname"];
}
return self;
}
By that, we already have 2 deserialization methods. Also, if we want to send the modified object back to the server,
we’ll need to add one more serialization method, that’ll convert it back to NSDictionary
, and then to JSON.
NSDictionary only solution
The only trick here is that we provide an -toDictionary
method. As we already have -initWithDictionary:
, this means we can serialize/deserialize to and from JSON and NSUserdefaults
.
- (NSDictionary *)toDictionary {
return @{
@"first_name": self.name,
@"last_name": self.surname
};
}
To sum up, you can
- save / load the custom object with:
//save
[[NSUserDefaults standardUserDefaults] setObject:[person toDictionary]] forKey:@"personKey"];
//load
[[NSUserDefaults standardUserDefaults] objectForKey:@"personKey"];
- receive / send the object from a web service
NSDictionary *personFromJSON = //get the person
Person *person = [[Person alloc] initWithDictionary:personFromJSON];
person.surname = @"modifiedSurname";
[person toDictionary]; // dict - you can convert it back to JSON