DEV Community

Cover image for Working with UserDefaults in iOS
Shivam Maggu
Shivam Maggu

Posted on • Originally published at blog.shivammaggu.com on

1

Working with UserDefaults in iOS

UserDefaults provides a database to store data and it is available as long as the app is installed.

Table of Contents

Features

  • The UserDefaults class provides access to the user's defaults database.

  • It stores data persistently across the app launches as long as the app is installed.

  • Access to the data is private to the app unless shared through App Groups.

  • Access to this database is thread-safe.

  • It is a key-value store that works through a property list (plist) file.

  • Supported data types include String, Bool, Date, Array, Dictionary, Data and Numbers.

  • Custom objects can be stored by encoding them as data.

  • We can use this to store non-sensitive data such as user preferences.

Usage

Utilising the default shared instance of UserDefaults.

import Foundation
// Set a key value in UserDefaults
UserDefaults.standard.set("Shivam", forKey: "firstname")
// Get a key value from User
DefaultsUserDefaults.standard.value(forKey: "firstname")
// Empty User
DefaultsUserDefaults.standard.removeObject(forKey: "firstname")
Enter fullscreen mode Exit fullscreen mode

Creating a UserDefaults that can be shared with multiple apps and extensions.

import Foundation
// User Defaults with Suite.
// Useful for sharing data with other apps
// or extensions with app groups.
let defaults = UserDefaults(suiteName: "group.com.organisation.appname")!
// Set a key value in User Defaults
defaults.set("Shivam", forKey: "firstname")
// Get a key value from User Defaults
defaults.value(forKey: "firstname")
// Empty User Defaults
defaults.removeObject(forKey: "firstname")
Enter fullscreen mode Exit fullscreen mode

Storing custom objects in UserDefaults.

import Foundation

// Custom model to be stored in UserDefaults
struct Student: Codable {
    let firstname: String
    let lastname: String
    let grade: Int
    let subjects: Array<String>
    let teachers: [String: String]
    let profileImage: Data
    let dateModified: Date
}

// Student Object
let student = Student(firstname: "John",
                      lastname: "Doe",
                      grade: 2,
                      subjects: ["Hindi", "English", "Maths"],
                      teachers: ["Hindi": "Teacher A", "English": "Teacher b", "Maths": "Teacher X"],
                      profileImage: Data(),
                      dateModified: Date())

// Encode custom object to data and store it in UserDefaults
let encoder = JSONEncoder()
do {
    let data = try encoder.encode(student)
    UserDefaults.standard.setValue(data, forKey: "StudentData")
} catch {
    throw error
}

// Retrieve data from UserDefaults and decode it to custom object
let decoder = JSONDecoder()
if let data = UserDefaults.standard.value(forKey: "StudentData") as? Data {
    do {
        let object = try decoder.decode(Student.self, from: data)
        print(object)
    } catch {
        throw error
    }
} else {
    print("No data in Defaults.")
}
Enter fullscreen mode Exit fullscreen mode

Using UserDefaults as a Property Wrapper

Check out this gist to use UserDefaults with Property Wrapper. This is a more generalized way to use in Projects.

import UIKit
// Protocol to check for Nil
protocol AnyOptional {
var isNil: Bool { get }
}
// Conforming nil check protocol to optional
extension Optional: AnyOptional {
var isNil: Bool {
return self == nil
}
}
// Helper Class for UserDefaults with Property Wrapper
@propertyWrapper
class UserDefaultsHelper<T> {
private let defaultValue: T // Stores an initial default value
private let key: String // The key representing the value
private let store: UserDefaults // Used for defaults initialisation (Eg. .standard or .init(suiteName:))
// Initialiser
init(defaultValue: T, key: String, store: UserDefaults) {
self.defaultValue = defaultValue
self.key = key
self.store = store
print("\(self.key) initialised with default value \(self.defaultValue)\n")
}
// Getter Setter fot the wrapped property value
var wrappedValue: T {
set {
self.setValue(newValue: newValue)
}
get {
return self.getValue()
}
}
// Sets a value to Defaults or Removes it if nil
private func setValue(newValue: T) {
guard let newValue = newValue as? AnyOptional, newValue.isNil == true else {
self.store.set(newValue, forKey: self.key)
print("\(self.key) initialised with new value \(newValue)\n")
return
}
self.store.removeObject(forKey: self.key)
print("\(self.key) is removed\n")
}
// Fetches a value against a key from Defaults
private func getValue() -> T {
let value = self.store.value(forKey: self.key) as? T ?? defaultValue
print("Value for \(self.key) is \(value)\n")
return value
}
}
// Initialiser with nil default value
extension UserDefaultsHelper where T: ExpressibleByNilLiteral {
convenience init(key: String, store: UserDefaults) {
self.init(defaultValue: nil, key: key, store: store)
}
}
extension UserDefaults {
// Defaults object
private static var store: UserDefaults {
return UserDefaults.standard
}
// Defaults object shared with app groups
private static var groupStore: UserDefaults {
return UserDefaults.init(suiteName: "group.com.orgainsation.appname")!
}
// User Default Keys
enum Keys {
static let firstname: String = "firstname"
static let lastname: String = "lastname"
static let grade: String = "grade"
static let dateModified: String = "dateModified"
static let subjects: String = "subjects"
static let subjectTeacher: String = "subjectTeacher"
static let isActive: String = "isActive"
static let profileImage: String = "profileImage"
static let coverImage: String = "coverImage"
static var allCases: [String] = [firstname,
lastname,
grade,
dateModified,
subjects,
subjectTeacher,
isActive,
profileImage,
coverImage]
}
// Using property wrapper to set key values stored in user defaults
@UserDefaultsHelper(defaultValue: "Shivam", key: Keys.firstname, store: .groupStore)
static var firstname: String
@UserDefaultsHelper(defaultValue: "Maggu", key: Keys.lastname, store: .groupStore)
static var lastname: String
@UserDefaultsHelper(defaultValue: 5, key: Keys.grade, store: .store)
static var grade: Int?
@UserDefaultsHelper(defaultValue: Date(), key: Keys.dateModified, store: .store)
static var dateModified: Date
@UserDefaultsHelper(defaultValue: ["English", "Maths", "Science"], key: Keys.subjects, store: .store)
static var subjects: Array<String>
@UserDefaultsHelper(defaultValue: ["English": "Mrs. Jaggi", "Maths": "Mr. Karim", "Science": "Mrs. Rita"],
key: Keys.subjectTeacher,
store: .store)
static var subjectTeacher: [String: String]
@UserDefaultsHelper(defaultValue: true, key: Keys.isActive, store: .store)
static var isActive: Bool
@UserDefaultsHelper(defaultValue: Data(), key: Keys.profileImage, store: .store)
static var profileImage: Data
@UserDefaultsHelper(key: Keys.coverImage, store: .store)
static var coverImage: Data?
// Sets all the key-value pairs to their default values
static func clearAll() {
Keys.allCases.forEach { key in
self.store.removeObject(forKey: key)
self.groupStore.removeObject(forKey: key)
}
}
}
// Usage of UserDefaults
print(UserDefaults.firstname)
print(UserDefaults.lastname)
print(UserDefaults.grade)
print(UserDefaults.dateModified)
print(UserDefaults.subjects)
print(UserDefaults.subjectTeacher)
print(UserDefaults.isActive)
print(UserDefaults.profileImage)
print(UserDefaults.coverImage)
UserDefaults.isActive = false
print(UserDefaults.isActive)
UserDefaults.grade = 5
print(UserDefaults.grade)
UserDefaults.clearAll()

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay