Archive and Unarchive a DTO Object to and from NSUserDefaults
Korey Hinton –> My Blog –> iOS –> Archive and Unarchive a DTO Object to and from NSUserDefaults
March 8, 2014
NSUserdefaults can be an easy way persist app data beyond an app's single lifecycle without having to set-up a data model with all the CoreData boilerplate code to fetch and persist it. Persisting and retrieving a single value in NSUserDefaults is trivial compared to all the required effort in setting it up in CoreData. I have ran into situations where I've wanted to persist an entire DTO object and I didn't have sufficient reasoning to make an entire class object-graphed so instead I came across the NSCoding protocol.
In the example below I am going to show you how a DTO object can use this protocol to decode and encode itself properly and also conveniently have this same object's class methods archive it using NSKeyedArchiver
and persist to NSUserDefaults
and also retrieve it in similar manner.
Implementation
MyDataObject.h
// // MyDataObject.h // NSCodingExample // // Created by Korey Hinton on 2/1/14. // Copyright (c) 2014 Korey Hinton. All rights reserved. // #import <Foundation/Foundation.h> @interface MyDataObject : NSObject <NSCoding> @property (strong, nonatomic) NSString *name; @property (assign) NSUInteger age; - (id)initWithName:(NSString *)name andAge:(NSUInteger)age; + (void)archiveMyDataObject:(MyDataObject *)myDataObject; + (MyDataObject *)unArchiveMyDataObject; + (BOOL)isArchived; @end
MyDataObject.m
// // MyDataObject.m // NSCodingExample // // Created by Korey Hinton on 2/1/14. // Copyright (c) 2014 Korey Hinton. All rights reserved. // #import "MyDataObject.h" #define kMyObjectNameKey @"myobjectname" #define kMyObjectAgeKey @"myobjectage" #define kMyObjectUserDefaultKey @"myobject" @implementation MyDataObject - (id)initWithName:(NSString *)name andAge:(NSUInteger)age { if (self = [super init]) { self.name = name; self.age = age; } return self; } - (id)initWithCoder:(NSCoder *)aDecoder { if (self = [super init]) { self.name = [aDecoder decodeObjectForKey:kMyObjectNameKey]; self.age = [aDecoder decodeIntegerForKey:kMyObjectAgeKey]; } return self; } - (void)encodeWithCoder:(NSCoder *)aCoder { [aCoder encodeObject:self.name forKey:kMyObjectNameKey]; [aCoder encodeInteger:self.age forKey:kMyObjectAgeKey]; } + (void)archiveMyDataObject:(MyDataObject *)myDataObject { NSData *data = [NSKeyedArchiver archivedDataWithRootObject:myDataObject]; [[NSUserDefaults standardUserDefaults] setObject:data forKey:kMyObjectUserDefaultKey]; [[NSUserDefaults standardUserDefaults] synchronize]; } + (MyDataObject *)unArchiveMyDataObject { NSData *data = [[NSUserDefaults standardUserDefaults] objectForKey:kMyObjectUserDefaultKey]; return [NSKeyedUnarchiver unarchiveObjectWithData:data]; } + (BOOL)isArchived { return [[NSUserDefaults standardUserDefaults] objectForKey:kMyObjectUserDefaultKey] != nil; } @end
Usage
The usage logic is simple here, the MyDataObject
has a static class method for checking if the object has been archived before. This makes it easy to set-up the object in one place based on whether it has been saved at least once before.
MyDataObject *myObject = nil; if (![MyDataObject isArchived]) { myObject = [[MyDataObject alloc] initWithName:@"Korey" andAge:27]; [MyDataObject archiveMyDataObject:myObject]; } else { myObject = [MyDataObject unArchiveMyDataObject]; }