我想将数组patientList保存为UserDefaults。Patient是一个自定义类,因此我需要将其转换为数据对象,但这在Swift 5上不像以前那样工作。

func addFirstPatient(){
    let newPatient = Patient(name: nameField.text!, number: numberField.text!, resultArray: resultArray, diagnoseArray: diagnoseArray)
    let patientList: [Patient] = [newPatient]
    let encodeData: Data = NSKeyedArchiver.archivedData(withRootObject: patientList)
    UserDefaults.standard.set(encodeData, forKey: "patientList")
    UserDefaults.standard.synchronize()
}
struct Patient {
    var diagnoseArray: [Diagnose]
    var resultArray: [Diagnose]
    var name: String
    var number: String
    init(name: String, number: String, resultArray: [Diagnose], diagnoseArray: [Diagnose]) {
        self.diagnoseArray = diagnoseArray
        self.name = name
        self.number = number
        self.resultArray = resultArray
    }
}
struct Diagnose{
    var name: String
    var treatments: [Treatment]
    var isPositiv = false
    var isExtended = false
    init(name: String, treatments: [Treatment]) {
        self.name = name
        self.treatments = treatments
    }
}
struct Treatment {
    var name: String
    var wasMade = false
    init(name: String) {
        self.name = name
    }
}


这就是函数的样子。
问题出在我初始化encodeData的那一行。
let encodeData: Data = try! NSKeyedArchiver.archivedData(withRootObject: patientList, requiringSecureCoding: false)

这是斯威夫特的建议,但当我像这样尝试时,它总是崩溃,我没有得到错误

最佳答案

Vadian的答案是正确的,不能将NSKeyedArchiver与结构一起使用。让所有对象都符合Codable是再现所需行为的最佳方法。我做了Vadian所做的,但我也可以使用协议扩展来提高安全性。

import UIKit

struct Patient: Codable {
    var name: String
    var number: String
    var resultArray: [Diagnose]
    var diagnoseArray: [Diagnose]
}

struct Diagnose: Codable {
    var name: String
    var treatments: [Treatment]
    var isPositiv : Bool
    var isExtended : Bool
}

struct Treatment: Codable {
    var name: String
    var wasMade : Bool
}

let newPatient = Patient(name: "John Doe",
                         number: "123",
                         resultArray: [Diagnose(name: "Result", treatments: [Treatment(name: "Treat1", wasMade: false)], isPositiv: false, isExtended: false)],
                         diagnoseArray: [Diagnose(name: "Diagnose", treatments: [Treatment(name: "Treat2", wasMade: false)], isPositiv: false, isExtended: false)])
let patientList: [Patient] = [newPatient]

引入一个协议来管理对象的编码和保存。
这不必从Codable继承,但为了简单起见,在本例中是这样做的。
/// Objects conforming to `CanSaveToDisk` have a save method and provide keys for saving individual objects or a list of objects.
protocol CanSaveToDisk: Codable {

    /// Provide default logic for encoding this value.
    static var defaultEncoder: JSONEncoder { get }

    /// This key is used to save the individual object to disk. This works best by using a unique identifier.
    var storageKeyForObject: String { get }

    /// This key is used to save a list of these objects to disk. Any array of items conforming to `CanSaveToDisk` has the option to save as well.
    static var storageKeyForListofObjects: String { get }

    /// Persists the object to disk.
    ///
    /// - Throws: useful to throw an error from an encoder or a custom error if you use stage different from user defaults like the keychain
    func save() throws

}

使用协议扩展,我们添加了一个选项来保存这些对象的数组。
extension Array where Element: CanSaveToDisk {

    func dataValue() throws -> Data {
        return try Element.defaultEncoder.encode(self)
    }

    func save() throws {
        let storage = UserDefaults.standard
        storage.set(try dataValue(), forKey: Element.storageKeyForListofObjects)
    }

}


我们扩展了患者对象,以便它知道在保存时要做什么。
我使用“存储”以便可以与NSKeychain交换。如果要保存敏感数据(如患者信息),则应使用密钥链而不是用户默认值。此外,确保您在提供应用程序的任何市场上遵守健康数据的安全和隐私最佳实践。各国之间的法律可能是一种截然不同的经验。用户默认值可能不够安全。
有很多伟大的钥匙链包装,使事情更容易。UserDefaults只是使用键设置数据。钥匙链也一样。像https://github.com/evgenyneu/keychain-swift这样的包装器的行为类似于下面的UserDefaults。为了完整起见,我已经注释了等效用法的外观。
extension Patient: CanSaveToDisk {

    static var defaultEncoder: JSONEncoder {
        let encoder = JSONEncoder()
        // add additional customization here
        // like dates or data handling
        return encoder
    }

    var storageKeyForObject: String {
        // "com.myapp.patient.123"
        return "com.myapp.patient.\(number)"
    }

    static var storageKeyForListofObjects: String {
        return "com.myapp.patientList"
    }

    func save() throws {

        // you could also save to the keychain easily
        //let keychain = KeychainSwift()
        //keychain.set(dataObject, forKey: storageKeyForObject)

        let data = try Patient.defaultEncoder.encode(self)
        let storage = UserDefaults.standard
        storage.setValue(data, forKey: storageKeyForObject)
    }
}

节省是简单的,看看下面的2个例子!
do {

    // saving just one patient record
    // this saves this patient to the storageKeyForObject
    try patientList.first?.save()

    // saving the entire list
    try patientList.save()


} catch { print(error) }

关于swift - 如何在Swift 5/Xcode 10.2中的Userdefaults中保存自定义类的对象,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/55691207/

10-14 23:23
查看更多