1

我想将数组 PatientList 保存在 UserDefaults 中。Patient 是一个自定义类,所以我需要将它转移到 Data 对象中,但这在 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)

这就是 Swift 的建议,但是当我这样尝试时,它总是崩溃并且我没有收到错误

4

3 回答 3

3

您根本不能使用NSKeyedArchiverwith 结构。对象必须是NSObject采用NSCoding并实现所需方法的子类。

正如评论Codable中所建议的那样,例如更好的选择

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]
do {
    let encodeData = try JSONEncoder().encode(patientList)
    UserDefaults.standard.set(encodeData, forKey: "patientList")
    // synchronize is not needed
} catch { print(error) }

如果要为这些值提供默认值,则Bool必须编写一个初始化程序。

于 2019-04-15T16:02:01.237 回答
0

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。此外,请确保您在提供应用程序的任何市场中都遵守健康数据的安全和隐私最佳做法。国家之间的法律可能会有非常不同的体验。UserDefaults 可能不够安全。

有很多很棒的钥匙串包装器可以让事情变得更容易。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) }
于 2019-04-15T18:13:49.800 回答
-1
struct Employee: Codable{
  var name: String
}

var emp1 = Employee(name: "John")
 let encoder = JSONEncoder()
    do {
        let data = try encoder.encode(emp1)
        UserDefaults.standard.set(data, forKey: "employee")
        UserDefaults.standard.synchronize()
    } catch {
        print("error")
    }
于 2021-06-21T11:25:28.400 回答