Skip to content

Instantly share code, notes, and snippets.

@LeeKahSeng
Last active July 2, 2025 16:45
Show Gist options
  • Select an option

  • Save LeeKahSeng/20e0c3602d1141af3bcff45f1f02df10 to your computer and use it in GitHub Desktop.

Select an option

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/)
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
@NasrullahKhan

Copy link
Copy Markdown

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

@LeeKahSeng

Copy link
Copy Markdown
Author

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.

@KalinRangelovRangelov

Copy link
Copy Markdown

Hi, very nice implementation.
My question is: How can you inject mock/spy UserDefaults for unit testing?

@NikKovIos

Copy link
Copy Markdown

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