I have a custom UserDefaults class that uses a generic type to get/set the value. As far as i’m aware in my code, there is no way to pass a value with a type different than the defaultValue
that is passed in initialization of CustomUserDefaults
. I have no optionals, I never set values to nil and I never call removeObject
on a UserDefault key.
Somehow whenever my app launches there is only ONE key that is missing from my UserDefaults. It comes from an enum with 3 cases as shown below with the default being none
. Is there any possible way that data is being lost in UserDefaults on launch or shutdown? My unit tests pass that chek that the UserDefault
can be set and retrieved for the given enum and I can’t find any issues in my code. Please help!
enum MyEnum {
case one, two, none
}
extension UserDefaults {
enum Keys: String {
case myEnumKey
}
}
struct CustomUserDefaults<T: Codable> {
private let defaultKey: UserDefaults.Keys
private var key: String {
defaultKey.rawValue
}
private let defaultValue: T
private let defaults = UserDefaults.standard
init(defaultValue: T, key: UserDefaults.Keys) {
self.defaultKey = key
self.defaultValue = defaultValue
// Create the UserDefault if it doesn't exist
createDefault()
}
/**
* Sets defaultValue for UserDefault key
*/
func reset() {
set(defaultValue)
}
/**
* Creates the UserDefault if there is no value available for the key
*/
func createDefault() {
if defaults.object(forKey: self.key) == nil {
set(defaultValue)
}
/**
* Sets `newValue` to UserDefault. To store the value appropriately, the value is specially handled
* for objects that aren't part of the base data types (ex: String, Bool, Int, etc...)
* - Parameter newValue: Generic value to be set to UserDefault
*/
func set(_ newValue: T) {
if newValue is MyEnum {
let value = newValue as? NavigationTab
if value != nil {
let rawValue = value!.rawValue
defaults.set(rawValue, forKey: key)
return
}
} else if newValue is FlightInfo {
// Create JSON Encoder
let encoder = JSONEncoder()
// Encode FlightInfo
do {
let data = try encoder.encode(newValue)
defaults.set(data, forKey: key)
} catch {
// Log error
}
return
}
defaults.set(newValue, forKey: key)
}
/**
Retrieves the value from user default for the given (and supported) type. If no type is found or the type can not be inferred,
we will return the defaultValue for the `CAUserDefaults`
- Returns user stored value for the `UserDefault.Key`
*/
func get() -> T {
var value: T?
switch T.self {
case is String.Type:
value = defaults.string(forKey: key) as? T
if value != nil { return value! }
case is Bool.Type:
value = defaults.bool(forKey: key) as? T
if value != nil { return value! }
case is Int.Type:
value = defaults.integer(forKey: key) as? T
if value != nil { return value! }
case is Double.Type:
value = defaults.double(forKey: key) as? T
if value != nil { return value! }
case is NavigationTab.Type:
let string = defaults.string(forKey: key)
if string != nil {
let tab = NavigationTab(rawValue: string!) as? T
if tab != nil { return tab! }
}
default:
value = defaults.value(forKey: key) as? T
if value != nil { return value! }
}
// If we are not able to return a value from a case, we return defaultValue
// ------ I'm getting here with no value from the switch case or default case -----
return defaultValue
}
}
Getting/Setting the enum with CustomUserDefaults
var customValue: CustomUserDefaults<MyEnum> = .none
customValue.set(.one) // This works
customValue.set("string") // This will give a build error
var whatsTheValue = customValue.get()
print(whatsTheValue.rawValue) // prints "one"