Vadian ответ правильный, вы не можете использовать NSKeyedArchiver со структурами. Приведение всех ваших объектов в соответствие с Codable
- лучший способ воспроизвести искомое поведение. Я делаю то же, что и Вадиан, но вы также можете использовать расширения протокола, чтобы сделать это безопаснее.
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)
}
}
Экономия упрощена, ознакомьтесь с двумя примерами ниже!
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) }