ios – Why is one UserDefault value missing?

0
152


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"