更改结构时防止 AppStorage 上的数据丢失
Prevent lost of data on AppStorage when changing a struct
我有一个数据模型来处理应用程序使用的结构和数据。我正在使用 AppStorage
.
保存该数据
我最近需要向该结构添加一个额外的值,当我这样做时,所有保存的数据都消失了。
有什么办法可以避免这种情况吗?我在 Apple 的文档或其他 Swift 或 SwiftUI 网站上找不到任何关于此的内容。
这是我的数据结构以及我如何保存它。
let dateFormatter = DateFormatter()
struct NoteItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
var tags: [String] = []
//var starred: Int = 0 // if I add this, it wipes all the data the app has saved
}
final class DataModel: ObservableObject {
@AppStorage("myappdata") public var notes: [NoteItem] = []
init() {
self.notes = self.notes.sorted(by: {
[=10=].date.compare(.date) == .orderedDescending
})
}
func sortList() {
self.notes = self.notes.sorted(by: {
[=10=].date.compare(.date) == .orderedDescending
})
}
}
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}
我当然同意 UserDefaults (AppStorage) 不是最佳选择,但无论您选择哪种存储解决方案,您都需要一个迁移策略。因此,您可以采用以下两条路线来迁移已更改的 json 结构。
第一个是向您的结构添加自定义 init(from:)
并单独处理新的 属性
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
text = try container.decode(String.self, forKey: .text)
date = try container.decode(Date.self, forKey: .date)
tags = try container.decode([String].self, forKey: .tags)
if let value = try? container.decode(Int.self, forKey: .starred) {
starred = value
} else {
starred = 0
}
}
另一种选择是用另一个名字保留旧版本的结构,如果普通结构解码失败则使用它,然后将结果转换为新结构
extension NoteItem {
static func decode(string: String) -> [NoteItem]? {
guard let data = string.data(using: .utf8) else { return nil }
if let result = try? JSONDecoder().decode([NoteItem].self, from: data) {
return result
} else if let result = try? JSONDecoder().decode([NoteItemOld].self, from: data) {
return result.map { NoteItem(id: [=11=].id, text: [=11=].text, date: [=11=].date, tags: [=11=].tags, starred: 0)}
}
return nil
}
}
我有一个数据模型来处理应用程序使用的结构和数据。我正在使用 AppStorage
.
我最近需要向该结构添加一个额外的值,当我这样做时,所有保存的数据都消失了。
有什么办法可以避免这种情况吗?我在 Apple 的文档或其他 Swift 或 SwiftUI 网站上找不到任何关于此的内容。
这是我的数据结构以及我如何保存它。
let dateFormatter = DateFormatter()
struct NoteItem: Codable, Hashable, Identifiable {
let id: UUID
var text: String
var date = Date()
var dateText: String {
dateFormatter.dateFormat = "EEEE, MMM d yyyy, h:mm a"
return dateFormatter.string(from: date)
}
var tags: [String] = []
//var starred: Int = 0 // if I add this, it wipes all the data the app has saved
}
final class DataModel: ObservableObject {
@AppStorage("myappdata") public var notes: [NoteItem] = []
init() {
self.notes = self.notes.sorted(by: {
[=10=].date.compare(.date) == .orderedDescending
})
}
func sortList() {
self.notes = self.notes.sorted(by: {
[=10=].date.compare(.date) == .orderedDescending
})
}
}
extension Array: RawRepresentable where Element: Codable {
public init?(rawValue: String) {
guard let data = rawValue.data(using: .utf8),
let result = try? JSONDecoder().decode([Element].self, from: data)
else {
return nil
}
self = result
}
public var rawValue: String {
guard let data = try? JSONEncoder().encode(self),
let result = String(data: data, encoding: .utf8)
else {
return "[]"
}
return result
}
}
我当然同意 UserDefaults (AppStorage) 不是最佳选择,但无论您选择哪种存储解决方案,您都需要一个迁移策略。因此,您可以采用以下两条路线来迁移已更改的 json 结构。
第一个是向您的结构添加自定义 init(from:)
并单独处理新的 属性
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
text = try container.decode(String.self, forKey: .text)
date = try container.decode(Date.self, forKey: .date)
tags = try container.decode([String].self, forKey: .tags)
if let value = try? container.decode(Int.self, forKey: .starred) {
starred = value
} else {
starred = 0
}
}
另一种选择是用另一个名字保留旧版本的结构,如果普通结构解码失败则使用它,然后将结果转换为新结构
extension NoteItem {
static func decode(string: String) -> [NoteItem]? {
guard let data = string.data(using: .utf8) else { return nil }
if let result = try? JSONDecoder().decode([NoteItem].self, from: data) {
return result
} else if let result = try? JSONDecoder().decode([NoteItemOld].self, from: data) {
return result.map { NoteItem(id: [=11=].id, text: [=11=].text, date: [=11=].date, tags: [=11=].tags, starred: 0)}
}
return nil
}
}