UserDefaults
gives us access to an easy way to store data, but it has its limitations.
To quote Axel Lee’s website fluffy.es:
Using apps such as iExplorer, users can access the Library/Preferences folder of their iPhone and read / modify the UserDefaults plist data easily (eg: Change the boolean value of "boughtProVersion" from false to true, or change the amount of coins). Don't ever store a boolean for checking if user has bought in-app purchase in UserDefaults! User can change it very easily (without jailbreaking) and get your goodies for free! 😬
Other than in-app purchase status, you shouldn't store user password / API Keys in UserDefaults for the same reason as well.
Structured data belongs in CoreData
, while sensitive data belongs in the keychain.
When storing simple data however, such as the name and age of a user in my case, UserDefaults
works quite well.
As I established in Daily Coding Tip 002, raw strings are a dangerous thing. The compiler cannot help you if you make a spelling error, as a string can be given any spelling. This is especially true of UserDefaults
, which relies on strings to save and load values to its property list. It’s easy to see how misspelling a key when saving can lead to a value being stored multiple times under multiple spellings of the key, and how attempting to load from a misspelled key can give the false impression that no value was set.
import SwiftUI | |
extension String { | |
static let nameKey = "nameKey" | |
static let ageKey = "ageKey" | |
} | |
struct ContentView: View { | |
@AppStorage(.nameKey) var name: String = "" | |
let age = UserDefaults.standard.integer(forKey: .ageKey) | |
var body: some View { | |
VStack { | |
Text("name: \(name)") | |
Text("age: \(age)") | |
} | |
} | |
} |
Using an extension of String
allows us to easily assign static constants. Although I’m using UserDefaults
as an example, these could be used in any function or initializer that takes a String
as a parameter. In SwiftUI I am loading from UserDefaults
using the new @AppStorage property wrapper, which is new in iOS 14. This property wrapper requires a String
for the key, and so I’m giving it my nameKey
constant.
In any context outside of SwiftUI, it is necessary to do things the old-fashioned way.
I am loading my age property using UserDefaults.standard
and my ageKey
constant as a key.
It goes without saying that you must not make spelling mistakes in your constants, but that’s far less points of failure than every time you need to use them.