Understanding iOS Data Persistence — NSCoder

Last time we talked about NSUserDefaults and the file system as a way to store data. In this article, we will touch upon NSCoder.

NSCoder: Storing data on disk

NSCoder declares an interface for its subclasses to transfer data between memory and other formats (doc). It consists of:

  • Archiving: storing objects and data on disk
  • Distribution: objects and data are copied between different processes or threads.

NSKeyedArchiver & NSKeyedUnarchiver

NSKeyedArchiver and NSKeyedUnarchiver are concrete subclasses of NSCoder, which just means they are classes you can use to encode/decode data on disk (aka the archive).

Saving (archiving) simple data

// First, get the file path
let manager = NSFileManager.defaultManager()
let url = manager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as! NSURL
let filePath = url.URLByAppendingPathComponent("objectsArray").path!
// Saving an array of objects
NSKeyedArchiver.archiveRootObject(objectArray, toFile: filePath)

Retrieving (unarchiving) simple data

if let arrayData = NSKeyedUnarchiver.unarchiveObjectWithFile(filePath) as? [AnyObject] { 
// do something with the arrayData

Next up, we want to store arrays of more complex objects. In order for this to work, all objects in the array will have to conform to the NSCoder protocol, which declares the two methods that a class must implement so that instances of that class can be encoded and decoded. The beautiful thing is that the object can contain arrays of other complex objects. NSKeyedArchiver saves the entire object graph as long as they conform to the NSCoder protocol.

Saving (archiving) complex objects

// Inside the model file, implement these two methods
required init(coder unarchiver: NSCoder) {
name = unarchiver.decodeObjectForKey("name") as! String
func encodeWithCoder(archiver: NSCoder) {
archiver.encodeObject(name, forKey: "name")

NSCoder with NSUserDefaults

// Encode object into NSData
let data = NSKeyedArchiver.archivedDataWithRootObject(bikes)
// Save encoded NSData into NSUserDefaults
NSUserDefaults.standardUserDefaults().setObject(data, forKey: "bikes")
if let data = NSUserDefaults.standardUserDefaults().objectForKey("bikes") as? NSData {
// Decode the NSData back into an object
let bikes = NSKeyedUnarchiver.unarchiveObjectWithData(data)

So NSKeyedArchiver or Core Data?

So you must be thinking, that wasn’t so bad! But how about Core Data? As this useful article on NSHipster points out, Core Data is almost better in every way, except it’s a major pain in the ass. If you have a simple object graph, I would definitely use NSCoder.

If you definitely want to learn Core Data, stay tuned. We will be exploring Core Data in the next article.