【问题标题】:How to store user data locally in SwiftUI如何在 SwiftUI 中本地存储用户数据
【发布时间】:2020-09-19 02:40:01
【问题描述】:

我试图完成一个具有发布值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
            }
        }
    }

}

我知道这是否是最佳做法,但我想将数据保存在本地。 我写的代码不起作用,我不知道为什么。

我是初学者,我从未将数据存储到设备中。

【问题讨论】:

    标签: swift xcode swiftui observableobject


    【解决方案1】:

    每次调用init 方法时,第一行都会重置UserDefaults 中存储的值,然后返回空数组而不是之前存储的值。尝试对您的 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 进行改进。如果有人希望我在答案中也包含这一点,请在 cmets 中告诉我。

    更新:这里是使用 Swift 的 @PropertyWrapper 来简化使用 UserDefaults 的解决方案:-

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

    【讨论】:

    • 这以Return from initializer without initializing all stored properties 结尾。如何使用更好的方法修改 init()?
    • 设置所有属性不成功时,默认[]。我现在更新了支票。
    • 在我尝试使用最新的编码状态时,您可以包含@PropertryWrapper 解决方案,谢谢!
    • 只是想提醒您问题 - PropertyWrapper 解决方案会非常有帮助
    • 因为工作比较忙,我会尽快更新 PropertyWrapper 解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-02-26
    • 2015-05-17
    • 2013-08-17
    • 2018-06-19
    • 2017-09-01
    • 2014-12-26
    • 2019-10-03
    相关资源
    最近更新 更多