Чтобы очистить значение из UserDefaults, я раньше думал, что можно сделать что-то вроде этого:
UserDefaults.standard.set(nil, forKey: "myKey")
Оказывается, это работает по-разному в разных версиях iOS. Начиная с iOS 11, установка nil для ключа работает, как я и ожидал, и следующее печатает nil:
UserDefaults.standard.set(nil, forKey: "myKey") print(UserDefaults.standard.data(forKey: "myKey")) // nil
Однако в iOS 10 UserDefaults фактически пытается сериализовать nil в Data
:
UserDefaults.standard.set(nil, forKey: "myKey") print(UserDefaults.standard.data(forKey: "myKey")) // 135 bytes
Если мы посмотрим на эти данные, может показаться, что это plist:
print(String( data: UserDefaults.standard.data(forKey: "myKey")!, encoding: .ascii )) //Optional("bplist00Ô\u{01}\u{02}\u{03}\u{04}\u{05}\u{08}\n\u{0B}T$topX$objectsX$versionY$archiverÑ\u{06}\u{07}Troot\0¡\tU$null\u{12}\0\u{01} _\u{10}\u{0F}NSKeyedArchiver\u{08}\u{11}\u{16}\u{1F}(25:<>DI\0\0\0\0\0\0\u{01}\u{01}\0\0\0\0\0\0\0\u{0C}\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0[")
Оказывается, мы можем использовать PropertyListSerialization, чтобы распечатать сериализованное значение nil
:
let data = UserDefaults.standard.data(forKey: "myKey")! let propertyList = try! PropertyListSerialization.propertyList( from: data, options: [], format: nil ) print(propertyList) // prints: { "$archiver" = NSKeyedArchiver; "$objects" = ( "$null" ); "$top" = { root = "<CFKeyedArchiverUID 0x79e7c420 [0xf126e8]>{value = 0}"; }; "$version" = 100000; }
По какой-то причине это было совершенно неожиданно для меня. Это особенно сложно, когда вы пытаетесь интерпретировать значение ранее очищенного ключа UserDefaults как JSON:
if let data = UserDefaults.standard.data(forKey: "myKey") { let json = try JSONDecoder().decode(MyClass.self, from: data) print(json) } else { print("No data found") }
Это будет отлично работать на iOS 11+, но выдаст ошибку на iOS 10.
Вот пример проекта, который демонстрирует такое поведение. В итоге я нашел об этом один пост stackoverflow, но, насколько я могу судить, это нигде не задокументировано. Если у вас есть дополнительная информация об этом, дайте мне знать 👋
Усвоенный урок — установка nil — не лучший способ очистить значение UserDefaults. Вместо этого просто используйте рекомендуемый подход:
UserDefaults.standard.removeObject(forKey: "myKey")