SwiftUI 如何在本地存储用户数据
How to store user data locally in SwiftUI
我尝试完成具有已发布值的可观察对象 training
。在每次更改时,它都应将自定义结构保存为用户默认值。在每次加载(AppState init)时,它应该加载数据:
class AppState: ObservableObject {
var trainings: [Training] {
willSet {
if let encoded = try? JSONEncoder().encode(trainings) {
let defaults = UserDefaults.standard
defaults.set(encoded, forKey: "trainings")
}
objectWillChange.send()
}
}
init() {
self.trainings = []
if let savedTrainings = UserDefaults.standard.object(forKey: "trainings") as? Data {
if let loadedTraining = try? JSONDecoder().decode([Training].self, from: savedTrainings) {
self.trainings = loadedTraining
}
}
}
}
我知道这是否是最佳做法,但我想将数据保存在本地。
我写的代码不工作,我不明白为什么。
我是初学者,我从未将数据存储到设备。
每次调用 init
方法时,第一行重置存储在 UserDefaults
中的值,然后 returns 空数组而不是先前存储的值。尝试对您的 init
方法进行此修改以修复它:
init() {
if let savedTrainings = UserDefaults.standard.object(forKey: "trainings") as? Data,
let loadedTraining = try? JSONDecoder().decode([Training].self, from: savedTrainings) {
self.trainings = loadedTraining
} else {
self.trainings = []
}
}
更好的方法:更好的方法是修改您的 trainings
属性 以具有 get
和 set
而不是当前设置。这是一个例子:
var trainings: [Training] {
set {
if let encoded = try? JSONEncoder().encode(newValue) {
let defaults = UserDefaults.standard
defaults.set(encoded, forKey: "trainings")
}
objectWillChange.send()
}
get {
if let savedTrainings = UserDefaults.standard.object(forKey: "trainings") as? Data,
let loadedTraining = try? JSONDecoder().decode([Training].self, from: savedTrainings) {
return loadedTraining
}
return []
}
}
注意:这可以使用 Swift 5.1 的 @PropertyWrapper
再次改进。如果有人希望我也将其包含在答案中,请在评论中告诉我。
更新:这是使 UserDefaults
使用 Swift 的 @PropertyWrapper
更简单的解决方案,正如您所要求的:-
@propertyWrapper struct UserDefault<T: Codable> {
var key: String
var wrappedValue: T? {
get {
if let data = UserDefaults.standard.object(forKey: key) as? Data {
return try? JSONDecoder().decode(T.self, from: data)
}
return nil
}
set {
if let encoded = try? JSONEncoder().encode(newValue) {
UserDefaults.standard.set(encoded, forKey: key)
}
}
}
}
class AppState: ObservableObject {
@UserDefault(key: "trainings") var trainings: [Training]?
@UserDefault(key: "anotherProperty") var anotherPropertyInUserDefault: AnotherType?
}
我尝试完成具有已发布值的可观察对象 training
。在每次更改时,它都应将自定义结构保存为用户默认值。在每次加载(AppState init)时,它应该加载数据:
class AppState: ObservableObject {
var trainings: [Training] {
willSet {
if let encoded = try? JSONEncoder().encode(trainings) {
let defaults = UserDefaults.standard
defaults.set(encoded, forKey: "trainings")
}
objectWillChange.send()
}
}
init() {
self.trainings = []
if let savedTrainings = UserDefaults.standard.object(forKey: "trainings") as? Data {
if let loadedTraining = try? JSONDecoder().decode([Training].self, from: savedTrainings) {
self.trainings = loadedTraining
}
}
}
}
我知道这是否是最佳做法,但我想将数据保存在本地。 我写的代码不工作,我不明白为什么。
我是初学者,我从未将数据存储到设备。
每次调用 init
方法时,第一行重置存储在 UserDefaults
中的值,然后 returns 空数组而不是先前存储的值。尝试对您的 init
方法进行此修改以修复它:
init() {
if let savedTrainings = UserDefaults.standard.object(forKey: "trainings") as? Data,
let loadedTraining = try? JSONDecoder().decode([Training].self, from: savedTrainings) {
self.trainings = loadedTraining
} else {
self.trainings = []
}
}
更好的方法:更好的方法是修改您的 trainings
属性 以具有 get
和 set
而不是当前设置。这是一个例子:
var trainings: [Training] {
set {
if let encoded = try? JSONEncoder().encode(newValue) {
let defaults = UserDefaults.standard
defaults.set(encoded, forKey: "trainings")
}
objectWillChange.send()
}
get {
if let savedTrainings = UserDefaults.standard.object(forKey: "trainings") as? Data,
let loadedTraining = try? JSONDecoder().decode([Training].self, from: savedTrainings) {
return loadedTraining
}
return []
}
}
注意:这可以使用 Swift 5.1 的 @PropertyWrapper
再次改进。如果有人希望我也将其包含在答案中,请在评论中告诉我。
更新:这是使 UserDefaults
使用 Swift 的 @PropertyWrapper
更简单的解决方案,正如您所要求的:-
@propertyWrapper struct UserDefault<T: Codable> {
var key: String
var wrappedValue: T? {
get {
if let data = UserDefaults.standard.object(forKey: key) as? Data {
return try? JSONDecoder().decode(T.self, from: data)
}
return nil
}
set {
if let encoded = try? JSONEncoder().encode(newValue) {
UserDefaults.standard.set(encoded, forKey: key)
}
}
}
}
class AppState: ObservableObject {
@UserDefault(key: "trainings") var trainings: [Training]?
@UserDefault(key: "anotherProperty") var anotherPropertyInUserDefault: AnotherType?
}