无法在 UserDefaults 中存储自定义类 - 尝试插入非属性列表 [英] Cannot store custom class in UserDefaults - Attempt to insert non-property list
问题描述
我正在尝试在 UserDefaults
中存储自定义类,但是我收到 Attempt to insert non-property list"
错误应用程序启动.我已经成功地存储了自定义类的数组,所以我遵循了相同的原则,但是出了点问题.
I'm trying to store a custom class in UserDefaults
, however I'm getting the "Attempt to insert non-property list"
error as soon as the app starts. I have successfully stored arrays of custom classes, so I followed the same principle, but something is wrong.
这是我的课:
import Foundation
public class PlanRouteFilters: NSObject, NSCoding, Codable {
var favoriteLines: Bool
var routeType: Int
var bus: Bool
var train: Bool
var subway: Bool
var electric: Bool
var sharedCar: Bool
var bike: Bool
var electricScooter: Bool
var providers: [Provider]?
override init() {
self.favoriteLines = false
self.routeType = 1
self.bus = false
self.train = false
self.subway = false
self.electric = false
self.sharedCar = false
self.bike = false
self.electricScooter = false
self.providers = []
}
init(favLines: Bool, rType: Int, bus: Bool, train: Bool, subway: Bool, electric: Bool, sCar: Bool, bike: Bool, eScooter: Bool, providers: [Provider]?) {
self.favoriteLines = favLines
self.routeType = rType
self.bus = bus
self.train = train
self.subway = subway
self.electric = electric
self.sharedCar = sCar
self.bike = bike
self.electricScooter = eScooter
self.providers = providers
}
//MARK: NSCoding protocol methods
public func encode(with aCoder: NSCoder){
aCoder.encode(self.favoriteLines, forKey: "favoriteLines")
aCoder.encode(self.routeType, forKey: "RouteType")
aCoder.encode(self.bus, forKey: "bus")
aCoder.encode(self.train, forKey: "train")
aCoder.encode(self.subway, forKey: "subway")
aCoder.encode(self.electric, forKey: "electric")
aCoder.encode(self.sharedCar, forKey: "sharedCar")
aCoder.encode(self.bike, forKey: "bike")
aCoder.encode(self.electricScooter, forKey: "electricScooter")
aCoder.encode(self.providers, forKey: "providers")
}
public required init(coder decoder: NSCoder) {
self.favoriteLines = decoder.decodeBool(forKey: "favoriteLines")
self.routeType = decoder.decodeInteger(forKey: "routeType")
self.bus = decoder.decodeBool(forKey: "bus")
self.train = decoder.decodeBool(forKey: "train")
self.subway = decoder.decodeBool(forKey: "subway")
self.electric = decoder.decodeBool(forKey: "electric")
self.sharedCar = decoder.decodeBool(forKey: "sharedCar")
self.bike = decoder.decodeBool(forKey: "bike")
self.electricScooter = decoder.decodeBool(forKey: "electricScooter")
self.providers = decoder.decodeObject(forKey: "providers") as? [Provider]
}
}
这是我设置和获取对象的方式:
This is how I set and get the object:
var planRouteFilters: PlanRouteFilters {
get {
var filters = PlanRouteFilters()
let data = self.userSettings.settings.object(forKey: "planRouteFilters") as? Data
if nil != data {
filters = try! NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data!) as! PlanRouteFilters
}
return filters
}
set {
let data = try! NSKeyedArchiver.archivedData(withRootObject: newValue, requiringSecureCoding: false)
self.userSettings.settings.setValue(data, forKey: "planRouteFilters")
self.objectWillChange.send()
}
}
这就是我创建 userDefaults 的方式:
And this is how I create the userDefaults:
import Foundation
struct UserSettings {
var settings: UserDefaults
init() {
self.settings = UserDefaults.standard
self.settings.register(
defaults: [
"userCity": "",
"showUserCityActionSheet": true,
"showTutorial": 1,
"initialPage": 1,
"favoriteStops": [Place](),
"history": [Place](), //Maximum of 10 places stored
"notifications": [MoveMeNotification](),
"planRouteFilters": PlanRouteFilters()
])
}
}
如果我删除 PlanRouteFilters 的默认值,应用会启动,但不会在整个应用中使用这些值.这是我错的地方吗?我应该以其他方式提供默认值吗?
If I remove the default value for the PlanRouteFilters the app starts, but the values are not used throughout the app. Is this where I am wrong? Should I provide the default value in some other way?
谢谢!
我的Provider
类:
public class Provider: NSObject, NSCoding, Codable {
let name: String
init(name: String) {
self.Name = name
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(self.Name, forKey: "Name")
}
public required init(coder decoder: NSCoder) {
self.Name = decoder.decodeObject(forKey: "Name") as! String
}
}
更新:
好的,我没能解决这个问题.但是,我认为我不需要在 userSettings 中设置默认值.这让我可以继续前进.但是,这是特定于我的情况.对于没有此选项的任何人,请查看@LeoDabus 回答.
Ok, I was not able to solve this issue. However, I figured I did not need to have a default value in the userSettings. That allows me to move on. However, that is specific to my case. For any of those that don't have this option, take a look at @LeoDabus answers.
推荐答案
即使您的自定义类符合 NSCoding,它也不会自动工作.您需要将您的 providers 数组编码为 Data 才能对 PlanRouteFilters 进行编码:
Even when conforming your custom class to NSCoding it doesn't work automatically. You need to encode your providers array into Data to be able to encode PlanRouteFilters:
extension NSCoder {
func decodeData(forKey key: String) -> Data {
decodeObject(forKey: key) as? Data ?? Data()
}
func decodeString(forKey key: String) -> String {
decodeObject(forKey: key) as? String ?? ""
}
}
public class Provider: NSObject, NSCoding {
let name: String
init(name: String) { self.name = name }
public func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
}
public required init(coder decoder: NSCoder) {
name = decoder.decodeString(forKey: "name")
}
}
public class PlanRouteFilters: NSObject, NSCoding {
var favoriteLines: Bool
var routeType: Int
var bus: Bool
var train: Bool
var subway: Bool
var electric: Bool
var sharedCar: Bool
var bike: Bool
var electricScooter: Bool
fileprivate var providersData = Data()
var providers: [Provider] = []
override init() {
self.favoriteLines = false
self.routeType = 1
self.bus = false
self.train = false
self.subway = false
self.electric = false
self.sharedCar = false
self.bike = false
self.electricScooter = false
self.providers = []
}
init(favLines: Bool, rType: Int, bus: Bool, train: Bool, subway: Bool, electric: Bool, sCar: Bool, bike: Bool, eScooter: Bool, providers: [Provider]) {
self.favoriteLines = favLines
self.routeType = rType
self.bus = bus
self.train = train
self.subway = subway
self.electric = electric
self.sharedCar = sCar
self.bike = bike
self.electricScooter = eScooter
self.providers = providers
}
public func encode(with aCoder: NSCoder){
aCoder.encode(favoriteLines, forKey: "favoriteLines")
aCoder.encode(routeType, forKey: "routeType")
aCoder.encode(bus, forKey: "bus")
aCoder.encode(train, forKey: "train")
aCoder.encode(subway, forKey: "subway")
aCoder.encode(electric, forKey: "electric")
aCoder.encode(sharedCar, forKey: "sharedCar")
aCoder.encode(bike, forKey: "bike")
aCoder.encode(electricScooter, forKey: "electricScooter")
aCoder.encode((try? NSKeyedArchiver.archivedData(withRootObject: providers, requiringSecureCoding: false)) ?? Data(), forKey: "providersData")
}
public required init(coder decoder: NSCoder) {
favoriteLines = decoder.decodeBool(forKey: "favoriteLines")
routeType = decoder.decodeInteger(forKey: "routeType")
bus = decoder.decodeBool(forKey: "bus")
train = decoder.decodeBool(forKey: "train")
subway = decoder.decodeBool(forKey: "subway")
electric = decoder.decodeBool(forKey: "electric")
sharedCar = decoder.decodeBool(forKey: "sharedCar")
bike = decoder.decodeBool(forKey: "bike")
electricScooter = decoder.decodeBool(forKey: "electricScooter")
providers = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(decoder.decodeData(forKey: "providersData"))) as? [Provider] ?? []
}
}
let planRouteFilters: PlanRouteFilters = .init(favLines: true, rType: 1, bus: false, train: true, subway: false, electric: true, sCar: true, bike: true, eScooter: false, providers: [.init(name: "test"), .init(name: "drive")])
do {
let data = try NSKeyedArchiver.archivedData(withRootObject: planRouteFilters, requiringSecureCoding: false)
if let loadedFilters = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? PlanRouteFilters {
print("success", loadedFilters)
print("favoriteLines", loadedFilters.favoriteLines)
print("routeType", loadedFilters.routeType ?? "nil")
print("bus", loadedFilters.bus)
print("train", loadedFilters.train)
print("subway", loadedFilters.subway)
print("electric", loadedFilters.electric)
print("sharedCar", loadedFilters.sharedCar)
print("bike", loadedFilters.bike)
print("electricScooter", loadedFilters.electricScooter)
print("providers first", loadedFilters.providers.first?.name ?? "nil")
print("providers last", loadedFilters.providers.last?.name ?? "nil")
}
} catch {
print(error)
}
这将打印
成功 <__lldb_expr_54.PlanRouteFilters: 0x600000a410c0>
最喜欢的线是真的
路线类型 1
总线错误
训练真实
地铁假
电动真
共享汽车真实
自行车是真的
ElectricScooter false
供应商第一次测试
供应商最后一次开车
success <__lldb_expr_54.PlanRouteFilters: 0x600000a410c0>
favoriteLines true
routeType 1
bus false
train true
subway false
electric true
sharedCar true
bike true
electricScooter false
providers first test
providers last drive
更好/更简单的方法是坚持使用 Codable 协议并扩展 UserDefaults 以能够编码/解码 Codable 类型,如 this答案:
extension UserDefaults {
func decode<T: Decodable>(_ type: T.Type, forKey defaultName: String) throws -> T {
try JSONDecoder().decode(T.self, from: data(forKey: defaultName) ?? .init())
}
func encode<T: Encodable>(_ value: T, forKey defaultName: String) throws {
try set(JSONEncoder().encode(value), forKey: defaultName)
}
}
这篇关于无法在 UserDefaults 中存储自定义类 - 尝试插入非属性列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!