Оболочка свойства 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?
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.