无法在 UserDefaults 中存储自定义类 - 尝试插入非属性列表 [英] Cannot store custom class in UserDefaults - Attempt to insert non-property list

查看:66
本文介绍了无法在 UserDefaults 中存储自定义类 - 尝试插入非属性列表的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆