【问题标题】:Save A Non-Codable Swift Dictionary Using NSKeyedArchiver使用 NSKeyedArchiver 保存不可编码的 Swift 字典
【发布时间】:2019-09-29 13:51:34
【问题描述】:

我希望使用 NSKeyedArchiver 将包含字典的 swift 结构保存到数据中。我使用 NSKeyedArchiver 的原因是因为字典有一个与之关联的不可编码变量。我正在关注来自Paul Hudson 的本指南。

我遇到的问题是我不断收到错误消息“无法写入数据,因为它的格式不正确。”在“让编码=尝试编码器。编码(测试) " 这似乎适用于不可编码的类型,但当它们在 dictovnay 中时则不行。有没有人知道如何让它工作?这里是代码:

import SwiftUI
import Combine
import HealthKit

struct Testing: View {

    func saveData(){
        let test = TestHealthSample(
            myHKUnit : [Unit.imperial : HKUnit.kilocalorie(), Unit.metric : HKUnit.kilocalorie()],
            isFavorite: true
        )

        let encoder = JSONEncoder()

        do {
            let encoded = try encoder.encode(test)
            let str = String(decoding: encoded, as: UTF8.self)
            print(str)
        } catch {
            print(error.localizedDescription)
        }
    }

    var body: some View {
        Text("Test")
            .onAppear{
                self.saveData()
        }
    }
}


struct TestHealthSample{
    var myHKUnit     : [Unit: HKUnit]
    var isFavorite   : Bool
}


extension TestHealthSample: Codable {

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: TestCodingKeys.self)
        isFavorite = try container.decode(Bool.self, forKey: .isFavorite)


        let hkUnitData = try container.decode(Data.self, forKey: .myHKUnit)
        myHKUnit = try (NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(hkUnitData) as? [Unit:HKUnit]) ?? [Unit.metric : HKUnit.count()]
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: TestCodingKeys.self)
        try container.encode(isFavorite, forKey: .isFavorite)


        let hkUnitData = try NSKeyedArchiver.archivedData(withRootObject: myHKUnit, requiringSecureCoding: false)
        try container.encode(hkUnitData, forKey: .myHKUnit)
    }
}


enum TestCodingKeys: String, CodingKey {
    case myHKUnit
    case isFavorite
}

enum Unit : String, Codable{
    case metric
    case imperial
}

【问题讨论】:

  • print(error.localizedDescription) 更改为 print(error) 以获取更多详细信息。
  • 不幸的是,这并没有起到多大作用。它在归档期间吐出 UserInfo={NSDebugDescription=Caught 异常:-[__SwiftValue encodeWithCoder:]: unrecognized selector sent to instance 0x283e45ef0 (..... 然后是一堆文本。
  • 您混淆了CodableNSCoding。后者根本不适用于结构。删除 NSKeyedArchiver 并仅使用 Codable
  • @vadian 我会但是 "var myHKUnit : [Unit: HKUnit]" 包含 HKUnit 这是一个不可编码的类型
  • 尽可能使其符合Codable 或编写包装器。

标签: swift dictionary nskeyedarchiver


【解决方案1】:

问题是 Unit 枚举不能暴露给 Objective-C。

你必须添加一个逻辑来将枚举映射到它的原始值,反之亦然,像这样

extension TestHealthSample: Codable {

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: TestCodingKeys.self)
        isFavorite = try container.decode(Bool.self, forKey: .isFavorite)
        let hkUnitData = try container.decode(Data.self, forKey: .myHKUnit)
        let myHKUnitObjC = try (NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(hkUnitData) as? [String: HKUnit]) ?? [Unit.metric.rawValue : HKUnit.count()]
        var myHKUnitData = [Unit:HKUnit]()
        for (key, value) in myHKUnitObjC {
            myHKUnitData[Unit(rawValue: key)!] = value
        }
        myHKUnit = myHKUnitData
    }

    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: TestCodingKeys.self)
        try container.encode(isFavorite, forKey: .isFavorite)
        var myHKUnitObjc = [String:HKUnit]()
        for (key, value) in myHKUnit {
            myHKUnitObjc[key.rawValue] = value
        }
        let hkUnitData = try NSKeyedArchiver.archivedData(withRootObject: myHKUnitObjc, requiringSecureCoding: false)
        try container.encode(hkUnitData, forKey: .myHKUnit)
    }
}

【讨论】:

  • 完美答案!非常感谢。
猜你喜欢
  • 1970-01-01
  • 2019-01-12
  • 2017-12-25
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 2017-10-28
  • 1970-01-01
  • 2016-11-12
相关资源
最近更新 更多