Understanding iOS Data Persistence — NSUserDefaults and the file system

Feifan Wang
3 min readMay 11, 2015

--

Data persistence can be a bit tricky in iOS, especially for new comers. This series focuses on the most important aspects of data persistence. In this article, we will discuss saving data to NSUserDefaults and the file system. We will touch upon NSCoder and Core Data in later articles. Hopefully it can be a reference for the everyday iOS developer.

First, understand the application lifecycle

The AppDelegate.swift file contains callbacks for the entire lifecycle of our application. Each time our app transitions from one state to another, an app lifecycle event is triggered. We can use these lifecycle events as opportunities to persist our precious data. Read Apple’s doc on UIApplicationDelegate Protocol to learn about all the possible app states and their corresponding callbacks.

It is important to understand these lifecycle events in order to use them to persist and retrieve data.

User Defaults: the simplest data storage

User Defaults corresponds to the NSUserDefaults class. It is a simple key-value store meant for saving user defaults and preferences. The iPhone keeps track of user defaults for each app, which is perfect for storing any type of simple data.

Invoking NSUserDefaults

NSUserDefaults is a singleton object, which returns a reference to the same user defaults object regardless where we invoke it from. In the example below, we try to save an integer value:

NSUserDefaults.standardUserDefaults().setInteger(value: 12, forKey: "myIntVal")

To read that value:

NSUserDefaults.standardUserDefaults().integerForKey("myIntVal")

Note that NSUserDefaults can only store NSData, NSString, NSNumber, NSDate, NSArray, and NSDictionary. If you’d like to save a Swift struct or class, you will either have to convert it into one of the types mentioned here or archive it.

File System

Apps can save files (audio, images, etc.) directly to the iOS file system. It is geared towards apps running on their own and enforces a set of conventions. With a few exceptions, each app can only interact with the sandbox it has been given. Below is a quick reference of commonly used directories:

  • AppName.app is the app’s bundle, containing the app and all of its resources.
  • Documents/ contains user-generated content. Store any data the user will need to access again.
  • Documents/Inbox contains files our app was asked to open by outside entities, such as the Mail program.
  • Library/ is the top-level directory for any files that are not user data files. Use it for any files we don’t want exposed to the user.
  • tmp/ stores temporary files that don’t need to persist between launches.

The Documents/ directory: the bread and butter

The Documents/ directory is one of the most commonly used storage spaces, which we can use to store different types of files and data (from audio files to NSStrings).

We can perform file operations with the NSFileManager object. Before operating on an file, we will have to get an object that describes the complete path of a file in the Documents directory:

Boilerplate for getting the file path

let filename = "myAudio.wav"
let dirPath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String
let pathArray = [dirPath, filename]
let fileURL = NSURL.fileURLWithPathComponents(pathArray)!

Here we end up with an NSURL object, which can produce a string version of the path by calling fileURL.path.

Checking if a file exists

We can use the NSFileManager singleton object to check for a file’s existence. Assuming we already have the fileURL available from the above code block:

if NSFileManager.defaultManager().fileExistsAtPath(fileURL.path!) {
println("The file exists!")
}

--

--