【问题标题】:SwiftUI: How to persist an @EnvironmentObject variable?SwiftUI:如何持久化 @EnvironmentObject 变量?
【发布时间】:2019-12-17 02:53:06
【问题描述】:

我希望一个变量成为一个 EnvironmentObject 并且我还希望它被持久化,这样每次我重新启动我的应用程序时它都是一样的。

为此,我已经创建了以下 propertyWrapper:

import Foundation

@propertyWrapper
struct UserDefault<T: Codable> {
    let key: String
    let defaultValue: T

    var wrappedValue: T {
        get {
            if let encodedValue = UserDefaults.standard.object(forKey: key) as? Data {
                let decoder = JSONDecoder()
                let decodedValue = try! decoder.decode(T.self, from: encodedValue)
                return decodedValue
            } else {
                return defaultValue
            }
        } set {
            let encoder = JSONEncoder()
            let encodedValue = try! encoder.encode(newValue)
            UserDefaults.standard.set(encodedValue, forKey: key)
        }
    }
}

但是已经有一个属性包装器意味着我不能使用来自 Combine 的 @Published 属性包装器。 (在一个变量上使用两个属性包装器听起来不是一个好主意,而且我还没有找到一种方法来实现它。)

我通过自定义objectWillChange let 常量并在 willSet 中为每个变量调用其.send(input:) 方法解决了这个问题。

这是我的 DataStore 类:

import SwiftUI
import Combine

final class DataStore: ObservableObject {
    let objectWillChange = PassthroughSubject<DataStore, Never>()

    @UserDefault(key: "the text", defaultValue: "Hello world!")
    var text: String {
        willSet {
            objectWillChange.send(self)
        }
    }
}

这是我的观点:

struct StartView : View {
    @EnvironmentObject var dataStore: DataStore
    var body: some View {
        VStack {
            TextField("Enter text", text: $dataStore.text)
            Button("Reset text", action: {
                self.dataStore.text = "Hello World!"
            })
        }
    }
}

但不知何故,我真的相信应该有比制作自定义 objectWillChange 更漂亮的方法。有没有办法制作一个涵盖持久性和“发布”的单一属性包装器?还是我应该做一些完全不同的事情来达到我的目标?

谢谢!

【问题讨论】:

    标签: swift swiftui


    【解决方案1】:

    根据 Genetec Tech (https://medium.com/genetec-tech/property-wrappers-in-swift-5-1-the-missing-published-implementation-1a466ebcf660) 的猜测实现,您可以将两个属性包装器合并为一个 @PublishedUserDefault。

    例子:

    这是 UserDefaults 的普通 propertyWrapper

    @propertyWrapper
    struct UserDefault<T> {
        let key: String
        let defaultValue: T
    
        var wrappedValue: T {
            get {
                UserDefaults.standard.value(forKey: key) as? T ?? defaultValue
            } set {
                UserDefaults.standard.set(newValue, forKey: key)
            }
        }
    }
    

    当调用 UserDefaults.set(_ ,forKey:) 时,此 viewModel 也会更新。

    final class UserSettings: ObservableObject {
        let objectWillChange = ObservableObjectPublisher()
    
        @UserDefault(key: "text", defaultValue: "")
        var text: String
    
        private var notificationSubscription: AnyCancellable?
    
        init() {
            notificationSubscription = NotificationCenter.default.publisher(for: UserDefaults.didChangeNotification).sink { _ in
                self.objectWillChange.send()
            }
        }
    }
    

    【讨论】:

      【解决方案2】:
      private var cancellables = [String:AnyCancellable]()
      
      extension Published {
          init(wrappedValue defaultValue: Value, key: String) {
              let value = UserDefaults.standard.object(forKey: key) as? Value ?? defaultValue
              self.init(initialValue: value)
              cancellables[key] = projectedValue.sink { val in
                  UserDefaults.standard.set(val, forKey: key)
              }
          }
      }
      
      final class DataStore: ObservableObject {
          @Published(key: "theText")
          var text = "Hello world!"
      }
      

      示例:https://youtu.be/TXdAg_YvBNE

      【讨论】:

        猜你喜欢
        • 2019-12-27
        • 1970-01-01
        • 2011-11-02
        • 1970-01-01
        • 1970-01-01
        • 2014-07-12
        • 2018-12-20
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多