Simple data persistence in iOS using Swift 3 (think user preferences)

[Download Xcode 8.2.1 project with full Swift 3 source from GitHub.]

Today, we’ll cover the topic of storing a user’s “default settings” inside your app — not forcing the user to go to a Settings bundle. The key to doing this is to save the user’s preferences to a persistent store whenever the user changes those preferences (or at least when the app goes into the background, but definitely before the app is terminated). But saving/writing those preferences is not enough. You must also read those preferences from the persistent store every time the app opens so that the user will always see their default settings. User settings/preferences must be synchronized with the persistent store.

I’m talking about saving simple pieces of data, like a user’s preferences for the background color of views, preferred language (i.e., English, Hindi, Portuguese, Spanish), preferred measuring units (metric or English), etc. Apple’s Human Interface Guidelines don’t explicitly forbid in-app settings. In fact, they explicitly permit them:

Note: Apps are not required to use a Settings bundle to manage all preferences. For preferences that the user is likely to change frequently, the app can display its own custom interface for managing those preferences.


While Apple does generally encourage the use of a Settings bundle, there is a very cogent “argument for placing settings in inside your app.” It’s just very convenient to set an app’s preferences without leaving the app. My latest iOS app, accepted into the App Store by the Apple review team just a couple weeks ago, uses in-app settings. In fact, the app reviewers didn’t take issue with any features in my app.

Today, we’ll create the simple app shown in the video immediately below (Figure 1). Please watch:


Figure 1. We use two UISwitch components to change property values in realtime. We also store the isOn property, or save the result of setting the isOn property, of each UISwitch in NSUserDefaults.

Before discussing the app’s sample code, let me summarize iOS data storage options just so you know about all the possibilities. There are several tools of varying levels of functionality and complexity offered by iOS for storing information across app sessions, i.e., between multiple app startups and termination. We can roughly classify iOS state management technologies into the following categories:

So let’s talk about in-app user preferences using the Apple Foundation Framework’s API class UserDefaults:

The NSUserDefaults class provides a programmatic interface for interacting with the defaults system. The defaults system allows an application to customize its behavior to match a user’s preferences. For example, you can allow users to determine what units of measurement your application displays or how often documents are automatically saved. Applications record such preferences by assigning values to a set of parameters in a user’s defaults database. The parameters are referred to as defaults since they’re commonly used to determine an application’s default state at startup or the way it acts by default. …

At runtime, you use an NSUserDefaults object to read the defaults that your application uses from a user’s defaults database. NSUserDefaults caches the information to avoid having to open the user’s defaults database each time you need a default value. The synchronize() method, which is automatically invoked at periodic intervals, keeps the in-memory cache in sync with a user’s defaults database.

When you run your app for the first time, nothing is stored in the “user’s defaults database.” I don’t have to worry about accessing an NSUserDefaults object the first time the app is run. Apple’s documentation states “If the shared defaults object does not exist yet, it is created.”

#ad

NSUserDefaults is a list of key-value pairs. All keys are of String type. All values are “types such as floats, doubles, integers, Booleans, and URLs.” You can even store more complex types like NSArray or NSDictionary, but we won’t get too fancy today. So NSUserDefaults is really easy to use. Here are some examples from Apple’s documentation:

… Setting Default Values

func set(Any?, forKey: String)

Sets the value of the specified default key in the standard application domain.

func set(Bool, forKey: String)

Sets the value of the specified default key to the specified Boolean value.

Getting Default Values

func object(forKey: String)

Returns the object associated with the first occurrence of the specified default.

func bool(forKey: String)

Returns the Boolean value associated with the specified key. …

Here’s a couple of simple examples in Swift 3. We store some key/value pairs to NSUserDefaults. Then we retrieve the values using the keys and print the values to console. Read my inline commentary:

When I started developing the app shown in the video above, I implemented two UISwitchs. You know how a UISwitch works. It’s an on/off toggle. When you slide one to the right and its background turns green, it is “on” and its isOn property is set to true. If you slide a UISwitch switch to the left, it is “off” and its isOn property is set to false. You’ve probably noticed that most Settings bundles have a lot of UISwitchs. There’s a good reason for that. The UISwitch is a great way to specify whether an app property (preference) is on or off.

See Figure 2 below to review the storyboard for the app I wrote for this tutorial:

Figure 2. Data persistence demo app’s storyboard.

I started the project without state preservation. (You always want to encode your core functionality first.) I could change the background color of the UIView behind the UIStackView containing the “Measure: feet/meters” text using the top UISwitch. I could toggle the text between “Measure: feet” and “Measure: meters” using the bottom UISwitch. But the app couldn’t remember these changes across app termination and restart. All I could do was change the UIView’s background color and UIStackView’s text while the app was running, but that’s it. Here’s the initial Swift 3 code — without persistence:

Now I’m going to add saving of user preferences — the values of the two UISwitch components — to my Swift 3 code. Note that I haven’t said anything about reading those preferences on app startup. We’ll do that later:

We’re saving user preferences to disk, but they won’t do us any good if we don’t read those preferences every time the app starts up. In a simple example like this, we’ll read preferences every time our one and only UIViewController is initialized (in viewDidLoad). Read my inline comments in the following code:

#ad

There you have it: Saving user preferences inside an app. The user can change their preferences at any time. And the preferences are preserved even if the app has been closed and restarted multiple times.

[Download Xcode 8.2.1 project with full Swift 3 source from GitHub.]

#ad

Author: Andrew Jaffee

Avid and well-published author, software engineer, designer, and developer, now specializing in iOS mobile app development in Objective-C and Swift, but with a strong background in C#, C++, .NET, JavaScript, HTML, CSS, jQuery, SQL Server, MySQL, Oracle, Agile, Test Driven Development, Git, Continuous Integration, Responsive Web Design, blah, blah, blah ... Did I miss any fad-based catch phrases? My brain avatar was kindly provided by https://icons8.com under a Creative Commons Attribution-NoDerivs 3.0 Unported license.

2 thoughts on “Simple data persistence in iOS using Swift 3 (think user preferences)”

  1. Hey Andrew,
    Thanks for the great article! I have a question, how can I achieve the same thing using Core Data? I know that I should use UserDefaults for such a simple task, but I really need to use Core Data.
    I need to persist the state of a single UISwitch. I would very much appreciate if you could point me in the right direction!
    Here’s my code
    I have a DataModel created that hold 1 entity: SwitchState and 1 attribute: isOn – of type Bool
    My ViewController,
    1. The variable that will store the fetched request.

    var switchArray = [SwitchState]()

    2. Store the viewContext

    let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

    3. IBAction when the switch is turned on/off

    @IBAction func changeTemp(_ sender: UISwitch) {
    let switchState = SwitchState(context: context)
    switchState.isOn = true
    if sender.isOn {
    // Idealy I’d have to use something like: switchArray.something to get to .isOn atribute = true
    switchState.isOn = true
    saveItems()
    updateUIWithWeatherData(temperatureIn: “\(weatherDataModel.temperature * Int(1.8) + 32)°F”)
    } else {
    // Idealy I’d have to use something like: switchArray.something to get to .isOn atribute = false
    switchState.isOn = false
    saveItems()
    updateUIWithWeatherData(temperatureIn: “\(weatherDataModel.temperature)°C”)
    }
    }

    4. The save method

    func saveItems() {
    do {
    try context.save()
    } catch {
    print(“Error saving context: \(error)”)
    }
    }

    So far it is saving the data in the sqlite properly. 0 for off state and 1 for on state.

    5. Here comes the trouble 🙂

    func loadItems() {
    let request: NSFetchRequest = SwitchState.fetchRequest()
    do {
    switchArray = try context.fetch(request)
    } catch {
    print(“Error fetching data from context: \(error)”)
    }
    }

    How can I access the isOn attribute in SwitchState if I save the fetched request into an array?(because context.fetch(request) always returns an array)

Leave a Reply

Your email address will not be published. Required fields are marked *