Сохранить некодируемый словарь Swift с помощью NSKeyedArchiver - PullRequest
0 голосов
/ 29 сентября 2019

Я хочу сохранить быструю структуру, которая включает в себя словарь данных, используя NSKeyedArchiver.Причина, по которой я использую NSKeyedArchiver, заключается в том, что со словарем связана некодируемая переменная.Я следую этому руководству от Пола Хадсона .

Проблема, с которой я сталкиваюсь, заключается в том, что я получаю сообщение об ошибке "Данные не могут быть записаны, потому что они не находятся вправильный формат. "at "let encoded = try encoder.encode (test)" Это, кажется, работает с некодируемыми типами, но не тогда, когда они находятся в 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
}

1 Ответ

2 голосов
/ 29 сентября 2019

Проблема в том, что перечисление 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)
    }
}
...