Оболочка свойства UserDefault не сохраняет значения в версиях iOS ниже iOS 13

Я использую оболочку свойств, чтобы сохранить свои пользовательские значения по умолчанию. На устройствах iOS 13 это решение отлично работает. Однако в iOS 11 и iOS 12 значения не сохраняются в пользовательских значениях по умолчанию. Я читал, что оболочки свойств обратно совместимы, поэтому не знаю, почему это не работает в более старых версиях iOS.

Это оболочка свойства:

@propertyWrapper
struct UserDefaultWrapper<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 {
            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)

            UserDefaults.standard.set(data, forKey: key)
            UserDefaults.standard.synchronize()
        }
    }
}

struct UserDefault {
    @UserDefaultWrapper(key: "userIsSignedIn", defaultValue: false)
    static var isSignedIn: Bool
}

Затем я могу установить такое значение:

UserDefault.isSignedIn = true

Я неправильно использую оболочку свойств? У кого-нибудь еще возникают проблемы с обертками свойств в более старых версиях iOS?


person dayloosha    schedule 24.12.2019    source источник
comment
Не используйте try ? для кодирования / декодирования ваших данных. Используйте try / catch и, по крайней мере, распечатайте любую возникающую ошибку. Кроме того, вам не нужно звонить synchronize (но это не будет вашей проблемой)   -  person Paulw11    schedule 25.12.2019


Ответы (1)


Ничего общего с обертками свойств! Проблема в том, что в iOS 12 и ранее простое значение, такое как Bool (или String и т. Д.), Хотя Codable как свойство структуры Codable (например), не может само по себе быть в кодировке JSON. Ошибка (которую вы выбрасываете) об этом довольно ясно:

Bool верхнего уровня, закодированный как числовой фрагмент JSON.

Чтобы в этом убедиться, просто запустите этот код:

    do {
        _ = try JSONEncoder().encode(false)
        print("succeeded")
    } catch {
        print(error)
    }

На iOS 12 мы получаем ошибку. В iOS 13 получаем "succeeded".

Но если мы обернем наш Bool (или String и т. Д.) В структуру Codable, все будет хорошо:

    struct S : Codable { let prop : Bool }
    do {
        _ = try JSONEncoder().encode(S(prop:false))
        print("succeeded")
    } catch {
        print(error)
    }

Это отлично работает на обоих iOS 12 и iOS 13.

И этот факт предлагает решение! Переопределите оболочку свойства, чтобы она заключала свое значение в общую структуру оболочки:

struct UserDefaultWrapper<T: Codable> {

    struct Wrapper<T> : Codable where T : Codable {
        let wrapped : T
    }

    private let key: String
    private let defaultValue: T

    init(key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            guard let data = UserDefaults.standard.object(forKey: key) as? Data 
                else { return defaultValue }
            let value = try? JSONDecoder().decode(Wrapper<T>.self, from: data)
            return value?.wrapped ?? defaultValue
        }
        set {
            do {
                let data = try JSONEncoder().encode(Wrapper(wrapped:newValue))
                UserDefaults.standard.set(data, forKey: key)
            } catch {
                print(error)
            }
        }
    }
}

Теперь он работает на iOS 12 и iOS 13.


Кстати, я действительно думаю, что лучше сохранить в виде списка свойств, а не в формате JSON. Но в целом это не имеет значения. Вы также не можете кодировать простой Bool как список свойств. Вам все равно понадобится подход Wrapper.

person matt    schedule 25.12.2019
comment
Это очень сбивало с толку благодарность или разъяснение. Я мог сохранять сложные конструкции, но не простые. Теперь я знаю почему. - person mkeremkeskin; 25.08.2020