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 属性 以具有 getset 而不是当前设置。这是一个例子:

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?
}