SwiftUI:如何使用UserDefaults保留@Published变量?

时间:2019-08-22 14:29:25

标签: swiftui swift5 xcode11

我希望持久保留@Published变量,以便每次重新启动应用程序时都相同。

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

我有以下propertyWrapper

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)
        }
    }
}

这是我的设置课程

import SwiftUI
import Combine

 class Settings: ObservableObject {

   @Published var isLogedIn : Bool = false

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

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

            if let myresponse = response {                    
                    self.login = myresponse.login
                    }
               }
         }

}

“我的视图”课程

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

是否有一种方法可以制作一个同时包含持久性和发布性的属性包装器?

5 个答案:

答案 0 :(得分:5)

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)
        }
    }
}

class Settings: ObservableObject {
    @Published(key: "isLogedIn") var isLogedIn = false
    ...
}

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

答案 1 :(得分:3)

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

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

答案 2 :(得分:1)

应该可以组成一个新的属性包装器:

  

该提案的第一修订版不包括组成,   因为可以手动编写属性包装器类型。例如,   组合@A @B可以实现为AB包装器:

@propertyWrapper
struct AB<Value> {
  private var storage: A<B<Value>>

  var wrappedValue: Value {
    get { storage.wrappedValue.wrappedValue }
    set { storage.wrappedValue.wrappedValue = newValue }
  }
}
  

这种方法的主要好处是可预测性:   AB决定如何最好地实现A和B的组成,并为其命名   适当地提供正确的API和相关文档   语义。另一方面,必须手动将每个   构图是很多样板,特别是对于其功能   主要卖点是消除样板。也是   不幸的是,必须为每种成分发明名称---当我尝试   通过@A @B组成A和B,我怎么知道去寻找   手动组成的属性包装类型AB?也许应该是   BA?

参考:Property WrappersProposal: SE-0258

答案 3 :(得分:0)

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

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

答案 4 :(得分:0)

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")`
                    }
               }
         }

}