Last active
February 6, 2024 10:38
-
-
Save LeeKahSeng/20e0c3602d1141af3bcff45f1f02df10 to your computer and use it in GitHub Desktop.
Create the Perfect UserDefaults Wrapper Using Property Wrapper (https://swiftsenpai.com/swift/create-the-perfect-userdefaults-wrapper-using-property-wrapper/)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct User: Codable { | |
var firstName: String | |
var lastName: String | |
var lastLogin: Date? | |
} | |
@propertyWrapper | |
struct Storage<T: Codable> { | |
private let key: String | |
private let defaultValue: T | |
init(key: String, defaultValue: T) { | |
self.key = key | |
self.defaultValue = defaultValue | |
} | |
var wrappedValue: T { | |
get { | |
// Read value from UserDefaults | |
guard let data = UserDefaults.standard.object(forKey: key) as? Data else { | |
// Return defaultValue when no data in UserDefaults | |
return defaultValue | |
} | |
// Convert data to the desire data type | |
let value = try? JSONDecoder().decode(T.self, from: data) | |
return value ?? defaultValue | |
} | |
set { | |
// Convert newValue to data | |
let data = try? JSONEncoder().encode(newValue) | |
// Set value to UserDefaults | |
UserDefaults.standard.set(data, forKey: key) | |
} | |
} | |
} | |
@propertyWrapper | |
struct EncryptedStringStorage { | |
private let key: String | |
init(key: String) { | |
self.key = key | |
} | |
var wrappedValue: String { | |
get { | |
// Get encrypted string from UserDefaults | |
return UserDefaults.standard.string(forKey: key) ?? "" | |
} | |
set { | |
// Encrypt newValue before set to UserDefaults | |
let encrypted = encrypt(value: newValue) | |
UserDefaults.standard.set(encrypted, forKey: key) | |
} | |
} | |
private func encrypt(value: String) -> String { | |
// Encryption logic here | |
return String(value.reversed()) | |
} | |
} | |
struct AppData { | |
@Storage(key: "username_key", defaultValue: "") | |
static var username: String | |
@Storage(key: "enable_auto_login_key", defaultValue: false) | |
static var enableAutoLogin: Bool | |
// Declare a User object | |
@Storage(key: "user_key", defaultValue: User(firstName: "", lastName: "", lastLogin: nil)) | |
static var user: User | |
@EncryptedStringStorage(key: "password_key") | |
static var password: String | |
} | |
AppData.username = "swift-senpai" | |
print(AppData.username) // swift-senpai | |
AppData.enableAutoLogin = true | |
print(AppData.enableAutoLogin) // true | |
let johnWick = User(firstName: "John", lastName: "Wick", lastLogin: Date()) | |
AppData.user = johnWick | |
print(AppData.user.firstName) // John | |
print(AppData.user.lastName) // Wick | |
print(AppData.user.lastLogin!) // 2019-10-06 09:40:26 +0000 | |
AppData.password = "password1234" | |
print(AppData.password) // 4321drowssap |
Data will be lost when you run the code in Xcode Playground. However, if you use it in an app, the data will be persisted.
Hi, very nice implementation.
My question is: How can you inject mock/spy UserDefaults for unit testing?
And addition:
if let newValue {
let data = try? JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: key)
} else {
UserDefaults.standard.removeObject(forKey: key)
}
If set nil - remove data.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi. after restarting i'm losing the data because storage takes default value. what's the best approach to get the data after app restart by using your @propertyWrapper Storage