iOS apps have an in-built data dictionary for storing small amounts of data, mostly user settings and preferences.
These data persist across reboots and relaunches of the app.
UserDefaults is Key-Value storage. It stores Property List objects (String, Boolean, Integer, Date, Array, Dictionary and more) identified by String keys.
In this tutorial, we learn how to save simple values, arrays, dictionaries, as well as custom objects and lists of custom objects in UserDefaults.
Saving simple values
First, we initialise UserDefaults
let defaults = UserDefaults.standard
Here is how we save simple values
defaults.set(25, forKey: "days") defaults.set(true, forKey: "isAdmin") defaults.set("English", forKey: "Language") defaults.set(Date(), forKey: "LastRun")
to retrieve these values
let days = defaults.integer(forKey: "days")
Similarly, for other data types, use the appropriate method.
For primitive data types, UserDefaults returns a default value if the key does not exist in the data dictionary.
- integer(forKey:) returns an integer if the key exists, or 0 if it doesn’t.
- bool(forKey:) returns a boolean if the key exists, or false if it doesn’t.
- float(forKey:) returns a float if the key exists, or 0.0 if it doesn’t.
- double(forKey:) returns a double if the key exists, or 0.0 if it doesn’t.
Saving an array
let array = ["Hello", "World"] defaults.set(array, forKey: "myArray")
Saving a dictionary
let dict = ["blog": "finecoding", "author": "teknofreek"] defaults.set(dict, forKey: "myDict")
Retrieving objects
When retrieving objects, the result is optional. If the key doesn’t exist in UserDefault, it returns nil.
So you either need to store it in an optional variable; or handle the missing value.
//retrieve array, init a new empty array if nil. let mySavedArray = defaults.object(forKey: "myArray") as? [String] ?? [String]()
Saving custom objects
For saving custom objects to UserDefaults, our custom object must conform to NSCoding protocol, it declares two methods that your object must implement in order to be encoded and decoded.
Here’s how you define the object
import UIKit class MyPlaceObj: NSObject, NSCoding { var name: String? var add: String? var lat: Double var lng: Double init(name: String, add: String, lat: Double, lng: Double) { self.name = name self.add = add self.lat = lat self.lng = lng } required init?(coder aDecoder: NSCoder) { self.name = aDecoder.decodeObject(forKey: "name") as? String ?? "" self.add = aDecoder.decodeObject(forKey: "add") as? String ?? "" self.lat = aDecoder.decodeDouble(forKey: "lat") self.lng = aDecoder.decodeDouble(forKey: "lng") } func encode(with aCoder: NSCoder) { aCoder.encode(lat, forKey: "lat") aCoder.encode(lng, forKey: "lng") aCoder.encode(name, forKey: "name") aCoder.encode(add, forKey: "add") } }
To save this object, we will use NSKeyedArchiver, that encodes the object into an architecture-independent format that can be stored in a file.
let placeObj = MyPlaceObj.init(name: "some place name", add: "some address", lat: 25.3548, lng: 51.1839) let placeData = NSKeyedArchiver.archivedData(withRootObject: placeObj) defaults.set(placeData, forKey: "userlocation")
Retrieving custom object
guard let placeData = defaults.object(forKey: "userlocation") as? Data else { return } // Use NSKeyedUnarchiver to convert Data / NSData back to Player object guard let userplace = NSKeyedUnarchiver.unarchiveObject(with: placeData) as? MyPlaceObj else { return }
Saving an array of custom objects
For saving an array of custom objects, just make sure the object conforms to NSCoding protocol as shown above, and then save it the same way as you store a custom object.
//CREATE AN ARRAY var placesArray: [MyPlaceObj] = [] placesArray.append(placeObj) placesArray.append(placeObj) placesArray.append(placeObj) //SAVE IT let placesDataNew = NSKeyedArchiver.archivedData(withRootObject: placesArray) defaults.set(placesDataNew, forKey: "places")
Retrieving it
if let placesData = defaults.object(forKey: "places") as? NSData { placesArray = NSKeyedUnarchiver.unarchiveObject(with: placesData as Data) as! [MyPlaceObj] }
Saving custom objects using Codable
The Codable protocol introduced by Apple in Swift 4. It is a combination of Encodable
and Decodable
protocol, so we don’t need to write lengthy methods.
So instead of our MyPlaceObj Object, we can simply define a struct since we don’t need conformation to NSObject anymore.
struct MyPlace : Codable { var name: String var add: String var lat: Double var lng: Double }
Saving it
// Use PropertyListEncoder to convert MyPlace into Data / NSData
defaults.set(try? PropertyListEncoder().encode(place), forKey: "place")
Retrieving it
guard let placeData = defaults.object(forKey: "place") as? Data else { return }
// Use PropertyListDecoder to convert Data into MyPlace
guard let place = try? PropertyListDecoder().decode(MyPlace.self, from: placeData) else { return }
—
Leave A Comment