Sometimes, you will want to put some objects to NSUserDefaults. Unfortunately, you will get an error with something like the object is not encoded. Because NSUserDefaults archives to a plist under the hood). And, plists only support the core types: NSString, NSDate, NSData, NSNumber, NSArray, NSDictionary.
So, what should you do if you want to put your data object to NSUserDefaults. Thanks God, NSUserDefaults accepts NSData type. Any object can be converted to NSData within two steps: implementing NSCoding and then using archive and unarchive to convert it to NSData and vice versa.
Step 1: Implementing NSCoding
NSCoding is the protocol which you can implement on your data classes to support encoding and decoding your data into data buffer, which can then be persisted to disk.
Now, let's start the tutorial. Suppose you have a data object class like the following:
BookObj.h
@interface BookObj : NSObject
@property (nonatomic, retain) NSString *title;
@property (nonatomic, retain) NSString *author;
@property (nonatomic, retain) NSDate *publishedDate;
@property (nonatomic, readwrite) double price;
@end
BookObj.m
@implementation BookObj
@synthesize title = _title;
@synthesize author = _author;
@synthesize publishedDate = _publishedDate;
@synthesize price = _price;
@end
Now, we will add the NSCoding implementation.
At header file, we add:
@interface BookObj : NSObject <NSCoding>
...
@end
At implementation file, we add these 2 functions:
@implementation BookObj
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
self.title = [aDecoder decodeObjectForKey:@"title"];
self.author = [aDecoder decodeObjectForKey:@author"];
self.publishedDate = [aDecoder decodeObjectForKey:@"publishedDate"];
self.price = [aDecoder decodeDoubleForKey:@"price"];
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
[aCoder encodeObject:_title forKey:@"title"];
[aCoder encodeObject:_author forKey:@"author"];
[aCoder encodeObject:_publishedDate forKey:@"publishedDate"];
[aCoder encodeDouble:_double forKey:@"price"];
}
...
@end
As we see, we will encode all our object properties in -(void)encodeWithCoder: selector and decode them in -(id)initWithCoder:. We can encode and decode with the core types using encodeObject:forKey: and decodeObject:forKey:, and using the different methods (encodeDouble:forKey:,...) for any non-object type properties such as integer, double, float, BOOL, CGRect, CGPoint, CGSize and Objective-C type.
After doing these functions, we've done the first step.
Step 2: Archive and Unarchive
As we said before, we have to convert the data object to NSData before put it into NSUserDefaults. We convert it by using NSKeyedArchiver and convert it back to object type by NSKeyedUnarchiver.
Convert to NSData:
NSData *data = [NSKeyedArchiver archivedDataWithRootObj:obj]; // obj is the data object we want to put into NSUserDefaults.
After that, we just put data to NSUserDefaults.
Convert back to object type:
BookObj *obj = [NSKeyedUnarchiver unarchivedObjectWithData:data]; // data is NSData which stored into NSUserDefaults.
The tutorial is done now! Besides the benefit can save the custom object to NSUserDefaults. With NSCoding protocol, you can easily save your data object to file too. Let's try it!