I have an enum like this:
enum Environment {
case Production
case Staging
case Dev
}
And I\'d like to save an instance in NSUserDef
Swift 5.1 You can create a generic property wrapper, using Codable to transform values in and out the UserDefaults
extension UserDefaults {
// let value: Value already set somewhere
// UserDefaults.standard.set(newValue, forKey: "foo")
//
func set(_ value: T, forKey: String) where T: Encodable {
if let encoded = try? JSONEncoder().encode(value) {
setValue(encoded, forKey: forKey)
}
}
// let value: Value? = UserDefaults.standard.get(forKey: "foo")
//
func get(forKey: String) -> T? where T: Decodable {
guard let data = value(forKey: forKey) as? Data,
let decodedData = try? JSONDecoder().decode(T.self, from: data)
else { return nil }
return decodedData
}
}
@propertyWrapper
public struct UserDefaultsBacked: Equatable where Value: Equatable, Value: Codable {
let key: String
let defaultValue: Value
var storage: UserDefaults = .standard
public init(key: String, defaultValue: Value) {
self.key = key
self.defaultValue = defaultValue
}
// if the value is nil return defaultValue
// if the value empty return defaultValue
// otherwise return the value
//
public var wrappedValue: Value {
get {
let value: Value? = storage.get(forKey: key)
if let stringValue = value as? String, stringValue.isEmpty {
// for string values we want to equate nil with empty string as well
return defaultValue
}
return value ?? defaultValue
}
set {
storage.set(newValue, forKey: key)
storage.synchronize()
}
}
}
// use it
struct AppState: Equatable {
enum TabItem: String, Codable {
case home
case book
case trips
case account
}
var isAppReady = false
@UserDefaultsBacked(key: "selectedTab", defaultValue: TabItem.home)
var selectedTab
// default value will be TabItem.home
@UserDefaultsBacked(key: "selectedIndex", defaultValue: 33)
var selectedIndex
// default value will be 33
}