将自定义对象转换为要保存在 NSUserDefauts 中的数据

Posted

技术标签:

【中文标题】将自定义对象转换为要保存在 NSUserDefauts 中的数据【英文标题】:Convert a custom object to Data to be saved in NSUserDefauts 【发布时间】:2020-11-03 21:26:23 【问题描述】:

我们有一个像这样的自定义对象设置:

struct BallPark: Codable,Equatable  
    static func == (lhs: LeanVenue, rhs: LeanVenue) -> Bool 
        return lhs.id == rhs.id
    
    let id: String
    let name: String?
    let location: VenueCoordinates?
    let mapEnabled: Bool?
    let facilityStatus: String?
    

struct VenueCoordinates: Codable 
    let latitude: String?
    let longitude: String?
    
    var lat: Double? 
        guard let latitud = latitude else  return nil
        return Double(latitud)
    
    var lon: Double? 
        guard let longitude = longitude else  return nil
        return Double(longitude)
    

我们正在尝试将其转换为 Data 类型,以便可以将其保存为用户默认值,如下所示:

guard let bestBallParkToSave = theBestBallPark // this is of type BallPark, after fetching data

否则返回

 let encodedBestBallPark = NSKeyedArchiver.archivedData(withRootObject: bestBallParkToSave)
            
 UserDefaults.standard.set(encodedBestBallPark, forKey: "favoriteBallPark")

问题是它在尝试转换时会导致错误:

'NSInvalidArgumentException',原因:'-[__SwiftValue encodeWithCoder:]: 无法识别的选择器发送到实例 0x6000027dca80'

我尝试过使用这个堆栈溢出答案:How to convert custom object to Data Swift,但除了确保它符合我相信我的结构和其中的所有内容的 Codable 之外,并没有真正明确的答案。如果您有任何建议,请告诉我。

【问题讨论】:

【参考方案1】:

那里的问题。是您将 Codable 与 NSCoding 混为一谈。要使用 NSKeyedArchiver 的 archivedData 方法,您需要有一个符合 NSCoding 的类。在您的情况下,您有一个符合 Codable 的结构,因此您需要使用 JSONEncoder 编码方法。注意:您的等价方法声明是错误的:

struct BallPark: Codable, Equatable  
    static func == (lhs: Self, rhs: Self) -> Bool 
        return lhs.id == rhs.id
    
    let id: String
    let name: String?
    let location: VenueCoordinates?
    let mapEnabled: Bool?
    let facilityStatus: String?


struct VenueCoordinates: Codable 
    let latitude: String?
    let longitude: String?
    
    var lat: Double? 
        guard let latitud = latitude else  return nil
        return Double(latitud)
    
    var lon: Double? 
        guard let longitude = longitude else  return nil
        return Double(longitude)
    


let bestBallParkToSave = BallPark.init(id: "1", name: "Steve", location: .init(latitude: "20.0", longitude: "40.0"), mapEnabled: true, facilityStatus: "a status")

do 
    let encodedBestBallPark = try JSONEncoder().encode(bestBallParkToSave)
    UserDefaults.standard.set(encodedBestBallPark, forKey: "favoriteBallPark")
    if let ballParkData = UserDefaults.standard.data(forKey: "favoriteBallPark") 
        let loadedBallPark = try JSONDecoder().decode(BallPark.self, from: ballParkData)
        print(loadedBallPark)  // BallPark(id: "1", name: Optional("Steve"), location: Optional(VenueCoordinates(latitude: Optional("20.0"), longitude: Optional("40.0"))), mapEnabled: Optional(true), facilityStatus: Optional("a status"))
    
 catch 
     print(error)


您还可以让您的生活更轻松扩展 UserDefaults 并创建自定义编码和解码方法:

extension UserDefaults 
    func decodeObject<T: Decodable>(forKey defaultName: String, using decoder: JSONDecoder = .init()) throws -> T 
        try decoder.decode(T.self, from: data(forKey: defaultName) ?? .init())
    
    func encode<T: Encodable>(_ value: T, forKey defaultName: String, using encoder: JSONEncoder = .init()) throws 
        try set(encoder.encode(value), forKey: defaultName)
    


用法:

let bestBallParkToSave = BallPark(id: "1", name: "Steve", location: .init(latitude: "20.0", longitude: "40.0"), mapEnabled: true, facilityStatus: "a status")
do 

    try UserDefaults.standard.encode(bestBallParkToSave, forKey: "favoriteBallPark")
    let loadedBallPark: BallPark = try UserDefaults.standard.decodeObject(forKey: "favoriteBallPark")
    print(loadedBallPark)  // BallPark(id: "1", name: Optional("Steve"), location: Optional(VenueCoordinates(latitude: Optional("20.0"), longitude: Optional("40.0"))), mapEnabled: Optional(true), facilityStatus: Optional("a status"))
    
 catch 
     print(error)

【讨论】:

如果使用了JSONEncoder方法,还能保存到NSUserDefaults吗? 好的,这更有意义。我去试试,谢谢!

以上是关于将自定义对象转换为要保存在 NSUserDefauts 中的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何将自定义对象保存在包含更多自定义对象的数组中

Typescript/Angular 12:将自定义对象转换为参数对象

无法将自定义对象从 API 保存到核心数据

将自定义javascript对象转换为json [重复]

将自定义对象转换为 NSData 以与 NSFileManager 一起使用

Swift 4 如何将自定义对象数组转换为 JSON