SwiftUI:如何使用UserDefaults持久化@Published变量?

SwiftUI:如何使用UserDefaults持久化@Published变量?,swiftui,swift5,xcode11,Swiftui,Swift5,Xcode11,我想要一个@Published变量被持久化,这样每次我重新启动我的应用程序时它都是一样的 我想在一个变量上同时使用@UserDefault和@Published属性包装器。例如,我需要一个“@PublishedUserDefault var isLogedIn” 我有以下财产说唱歌手 import Foundation @propertyWrapper struct UserDefault<T> { let key: String let defaultValue:

我想要一个@Published变量被持久化,这样每次我重新启动我的应用程序时它都是一样的

我想在一个变量上同时使用@UserDefault和@Published属性包装器。例如,我需要一个“@PublishedUserDefault var isLogedIn

我有以下财产说唱歌手

import Foundation

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

    init(_ key: String, defaultValue: T) {
        self.key = key
        self.defaultValue = defaultValue
    }

    var wrappedValue: T {
        get {
            return UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}
我的观点课

struct HomeView : View {
    @EnvironmentObject var settings: Settings
    var body: some View {
        VStack {
            if settings.isLogedIn {
            Text("Loged in")
            } else{
            Text("Not Loged in")
            }
        }
    }
}

有没有一种方法可以创建一个同时覆盖持久化和发布的属性包装器?

您当前无法将
@UserDefault
包装在
@Published
周围,因为当前不允许这样做

实现
@PublishedUserDefault
的方法是将
objectWillChange
传递到包装器中,并在设置变量之前调用它。

private var cancelables=[String:anycancelables]()
已发布的扩展{
init(wrappedValue-defaultValue:Value,key:String){
让value=UserDefaults.standard.object(forKey:key)作为?value??defaultValue
self.init(initialValue:value)
可取消项[key]=projectedValue.sink{val in
UserDefaults.standard.set(val,forKey:key)
}
}
}
类设置:ObserveObject{
@已发布(键:“isLogedIn”)变量isLogedIn=false
...
}
样本:


所有
Codable
类型的版本签出应该可以组成一个新的属性包装:

这项提案的第一次修订没有包括组成部分, 因为可以手动组合属性包装器类型。例如 组合@A@B可以实现为AB包装器:

@propertyWrapper
结构AB{
私有var存储
var wrappedValue:Value{
获取{storage.wrappedValue.wrappedValue}
设置{storage.wrappedValue.wrappedValue=newValue}
}
}
这种方法的主要好处是它的可预测性 AB决定如何最好地实现A和B的组合,命名它 并提供正确的API和其应用程序的文档 语义学。另一方面,必须手动写出每个 合成是很多样板,特别是对于一个 主要的卖点是消除了陈词滥调。也是 不幸的是,当我尝试时,不得不为每一篇作文发明名字 通过@A@B组合A和B,我如何知道去寻找 手动合成的属性包装类型AB?或者应该是这样 文学学士


Ref:

要持久保存数据,可以使用
@AppStorage
属性包装器。 但是,如果不使用
@Published
,您的
ObservableObject
将不再发布有关更改数据的新闻。要解决此问题,只需从属性的
willSet
observer调用
objectWillChange.send()

class Settings: ObservableObject {
    @AppStorage("Example") var example: Bool = false {
        willSet {
            // Call objectWillChange manually since @AppStorage is not published
            objectWillChange.send()
        }
    }
}

在示例
struct字段中
。它将
字符串
作为参数。您必须传入
objectWillChange
。发布的
@Published
的唯一特殊之处在于它调用
objectWillChange.send()
在适当的时候。@Fabian我在下面发布了我的解决方案,避免了传入
objectWillChange
的麻烦,而定制
PropertyRapper
的意思是使用一个定制init,它只使用现有的存储,并通过更改时的接收器在设置时存储。我没想到。:-)我的解决方案更麻烦,在使用PropertyRapper之前,您必须在
init()
内部设置
objectWillChange
。谢谢您,这使它更简单了^ ^谢谢@victor!应该可以双向设置,以便用户默认值更改更新值。我对您的答案有一个小问题,在已发布的扩展中添加单个Userdefaults是否会导致应用程序使所有项目在任何地方都可以访问?如果我也这么做,但使用Keychain,会对性能产生影响吗?
@propertyWrapper
struct AB<Value> {
  private var storage: A<B<Value>>

  var wrappedValue: Value {
    get { storage.wrappedValue.wrappedValue }
    set { storage.wrappedValue.wrappedValue = newValue }
  }
}
struct HomeView : View {
    @StateObject var auth = Auth()
    @AppStorage("username") var username: String = "Anonymous"

    var body: some View {
        VStack {
            if username != "Anonymous" {
                Text("Logged in")
            } else{
                Text("Not Logged in")
            }
        }
        .onAppear(){
             auth.login()
        }
    }
}


import SwiftUI
import Combine

class Auth: ObservableObject {

 func login(params:[String:String]) {

        Webservice().login(params: params) { response in

            if let myresponse = response {          
                    UserDefaults.standard.set(myresponse.login, forKey: "username")`
                    }
               }
         }

}
class Settings: ObservableObject {
    @AppStorage("Example") var example: Bool = false {
        willSet {
            // Call objectWillChange manually since @AppStorage is not published
            objectWillChange.send()
        }
    }
}