Storing data in the defaults database is simple thanks to the easy-to-use API of the UserDefaults
class. You can take advantage of the API on iOS, tvOS, macOS, iPadOS, and watchOS. In this post, I show you how to store a dictionary in the defaults database. I promise you that it isn't rocket science.
Writing or Setting a Dictionary To User Defaults
You probably know that you can store strings, numbers, Date
objects, and Data
objects in the user's defaults database. Storing a dictionary is similar. There is one caveat, though. The types of the keys and values of the dictionary need to be supported by the defaults system, that is, strings, numbers, Date
objects, and Data
objects. Let's take a look at an example.
We access the shared defaults object through the standard
class property of the UserDefaults
class.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
We then create a dictionary of type [String:String]
and store the dictionary in the user's defaults database by invoking the set(_:forKey:)
method of the UserDefaults
database. We pass the dictionary as the first argument and a key as the second argument.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Create and Write Dictionary
let dictionary = [
"A": "One",
"B": "Two",
"C": "Three"
]
userDefaults.set(dictionary, forKey: "myKey")
This approach doesn't work if we try to store a dictionary that contains keys and/or values of an unsupported type, for example, a dictionary of type [String:URL]
. Take a look at this example. We create a URL
object and store it in a dictionary. The dictionary
constant is of type [String:URL]
. What happens if we pass the dictionary to the set(_:forKey:)
method? The result is a runtime exception because URL
isn't supported by the defaults system.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Create and Write Dictionary
let url = URL(string: "https://cocoacasts.com")!
let dictionary = [
"A": url
]
userDefaults.set(dictionary, forKey: "myKey")
Reading or Getting a Dictionary From User Defaults
Reading or getting a dictionary from the user's defaults database is straightforward. The UserDefaults
class doesn't define a convenience method for easily accessing a dictionary of values stored in the user's defaults database and that is why we use the object(forKey:)
method. The object(forKey:)
method returns an object of type Any?
, an optional. In the first example, we stored a dictionary of type [String:String]
in the user's defaults database. Let me show you how to retrieve that dictionary.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Create and Write Dictionary
let dictionary = [
"A": "One",
"B": "Two",
"C": "Three"
]
userDefaults.set(dictionary, forKey: "myKey")
// Read/Get Dictionary
let strings = userDefaults.object(forKey: "myKey")
We invoke the object(forKey:)
method on the shared defaults object, passing in the key of the value we are interested in, myKey
in this example. Remember that object(forKey:)
returns an object of type Any?
. This means we need to cast the result of object(forKey:)
to a dictionary of type [String:String]
using the as?
operator.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Create and Write Dictionary
let dictionary = [
"A": "One",
"B": "Two",
"C": "Three"
]
userDefaults.set(dictionary, forKey: "myKey")
// Read/Get Dictionary
let strings = userDefaults.object(forKey: "myKey") as? [String:String]
The strings
constant is of type [String:String]?
, an optional. I strongly discourage you from using the as!
operator to forced cast the result of object(forKey:)
to a dictionary of type [String:String]
. A runtime exception is thrown if the key that is passed to object(forKey:)
doesn't exist or if the result can't be cast to a dictionary of type [String:String]
.
Updating or Adding a Key-Value Pair to a Dictionary In User Defaults
Updating a dictionary or adding a key-value pair to a dictionary in the user's defaults database shouldn't be difficult with what you have learned so far. We start by retrieving the dictionary from the user's defaults database. This should look familiar.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Read/Get Dictionary
var strings: [String:String] = userDefaults.object(forKey: "myKey") as? [String:String] ?? [:]
Notice that we use the nil-coalescing operator to default to an empty dictionary of type [String:String]
. What does that mean? If the key-value pair doesn't exist, we start with an empty dictionary. It's as simple as that.
Because we want to add a key-value pair to the dictionary of strings, we define strings
as a variable instead of a constant. The next step is adding a key-value pair to the dictionary.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Read/Get Dictionary
var strings: [String:String] = userDefaults.object(forKey: "myKey") as? [String:String] ?? [:]
// Add Key-Value Pair to Dictionary
strings["D"] = "Four"
To update the dictionary in the user's defaults database, we need to invoke the set(_:forKey:)
method on the shared defaults object. We pass in the dictionary and the key we used to retrieve the dictionary.
import Foundation
// Access Shared Defaults Object
let userDefaults = UserDefaults.standard
// Read/Get Dictionary
var strings: [String:String] = userDefaults.object(forKey: "myKey") as? [String:String] ?? [:]
// Add Key-Value Pair to Dictionary
strings["D"] = "Four"
// Write/Set Dictionary
userDefaults.set(strings, forKey: "myKey")
That's it. If you need to perform this task in several places in your project, I recommend creating an extension for the UserDefaults
class to make that task easier. This avoids code duplication and it also results in a cleaner API. You can read more about this technique in this post.