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];
}

Date: 2014-04-08T04:07+0000

Author: Korey

Org version 7.9.3f with Emacs version 24

Validate XHTML 1.0