微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

如何使用 UserDefaults propertyWrapper 无缝包装 @Published 变量

如何解决如何使用 UserDefaults propertyWrapper 无缝包装 @Published 变量

我一直在寻找完美的 UserDefaults 包装器

  • 对存储中的数据对象进行无缝编码和解码
  • 与@Published 合作

我的起点是这个 StackOverflow answer,但它使用了 object:forKey,它不适用于自定义对象(编码 URL 对我来说总是很重要)。

我的想法是能够像这样使用它:

struct Server: Identifiable,Codable,Equatable,Hashable { /* vars */ }

class ServerPickerviewmodel: ObservableObject {

  @Published(wrappedValue: Server.defaultServers.first!,type: Server.self,key: "currentServer")
  var currentServer: Server?

}

为了实现这一点,我修改了@VictorKushnerov 的回答中的代码

import Combine

private var cancellables = [String: AnyCancellable]()

extension Published {
  init<T: encodable & Decodable>(wrappedValue defaultValue: T,type: T.Type,key: String) {
      let decoder = JSONDecoder()
      var value: T
      if
        let data = UserDefaults.standard.data(forKey: key),let decodedVal = try? decoder.decode(T.self,from: data) {
        value = decodedVal
      } else {
        value = defaultValue
      }

        self.init(initialValue: value) // <-- Error

        cancellables[key] = projectedValue.sink { val in
          let encoder = JSONEncoder()
          let encodedVal = encoder.encode(val) // <-- Error
          UserDefaults.standard.set(encodedVal,forKey: key)
        }
    }
}

目前有两个错误我无法解决,如下:

  • Cannot convert value of type 'T' to expected argument type 'Value' 它仍然依赖底层 @PublishedValue 泛型类型,我希望我可以用我的类型 T 覆盖它。
  • Instance method 'encode' requires that 'Published<Value>.Publisher.Output' (aka 'Value') conform to 'encodable'

解决方法

您可以通过使用 where Value : Codable 来限制您的扩展来修复编译错误。然后,您可以完全摆脱 T 泛型(并且您也不必使用 type 参数):

extension Published where Value : Codable {
  init(wrappedValue defaultValue: Value,key: String) {
      let decoder = JSONDecoder()
      var value: Value
      if
        let data = UserDefaults.standard.data(forKey: key),let decodedVal = try? decoder.decode(Value.self,from: data) {
        value = decodedVal
      } else {
        value = defaultValue
      }

        self.init(initialValue: value)

        cancellables[key] = projectedValue.sink { val in
          let encoder = JSONEncoder()
            do {
                let encodedVal = try encoder.encode(val)
                UserDefaults.standard.set(encodedVal,forKey: key)
            } catch {
                print(error)
                assertionFailure(error.localizedDescription)
            }
        }
    }
}

话虽如此,我可能会采用路径而不是创建自定义属性包装器来包装 @AppStorage 而不是扩展 @Published

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。