SwiftUI:如何保留@EnvironmentObject 变量?
SwiftUI: How to persist an @EnvironmentObject variable?
我希望一个变量成为一个 EnvironmentObject 并且我也希望它被持久化,这样每次我重新启动我的应用程序时它都是一样的。
为此,我已经创建了以下 属性Wrapper:
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 class:
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 更漂亮的方法。有没有一种方法可以制作一个涵盖持久性和 "publishing" 的单个 属性 包装器?或者我应该做一些完全不同的事情来实现我的目标?
谢谢!
根据 Genetec Tech (https://medium.com/genetec-tech/property-wrappers-in-swift-5-1-the-missing-published-implementation-1a466ebcf660) 的猜测实现,您可以将两个 属性 包装器组合成一个 @PublishedUserDefault。
示例:
这是普通的 属性UserDefaults 的包装器
@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()
}
}
}
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!"
}
我希望一个变量成为一个 EnvironmentObject 并且我也希望它被持久化,这样每次我重新启动我的应用程序时它都是一样的。
为此,我已经创建了以下 属性Wrapper:
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 class:
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 更漂亮的方法。有没有一种方法可以制作一个涵盖持久性和 "publishing" 的单个 属性 包装器?或者我应该做一些完全不同的事情来实现我的目标?
谢谢!
根据 Genetec Tech (https://medium.com/genetec-tech/property-wrappers-in-swift-5-1-the-missing-published-implementation-1a466ebcf660) 的猜测实现,您可以将两个 属性 包装器组合成一个 @PublishedUserDefault。
示例:
这是普通的 属性UserDefaults 的包装器
@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()
}
}
}
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!"
}