Ios 如何观察UserDefaults中的更改?

Ios 如何观察UserDefaults中的更改?,ios,swift,swiftui,Ios,Swift,Swiftui,我的视图中有一个@ObservedObject: struct HomeView: View { @ObservedObject var station = Station() var body: some View { Text(self.station.status) } 它根据站点中的字符串更新文本。状态: class Station: ObservableObject { @Published var status: String

我的视图中有一个
@ObservedObject

struct HomeView: View {

    @ObservedObject var station = Station()

    var body: some View {
        Text(self.station.status)
    }
 
它根据
站点中的
字符串更新文本。状态

class Station: ObservableObject {
    @Published var status: String = UserDefaults.standard.string(forKey: "status") ?? "OFFLINE" {
        didSet {
            UserDefaults.standard.set(status, forKey: "status")
        }
    }      
但是,我需要更改我的
AppDelegate
中的
status
的值,因为这是我接收以下信息的地方:

但是,如果我在
AppDelegate
中更改
状态
UserDefaults
值,它将不会在我的视图中更新

状态更改时,如何通知我视图中的
@ObservedObject


编辑:忘记提到在上述示例中使用了SwiftUI的2.0测试版。

我建议您使用环境对象,或者在需要时使用两者的组合。环境对象基本上是一个全局状态对象。因此,如果更改环境对象的“已发布”属性,它将反映您的视图。要设置它,您需要通过SceneDelegate将对象传递到初始视图,并且您可以使用整个视图层次结构中的状态。这也是跨非常远的同级视图传递数据的方法(或者如果您有更复杂的场景)

简单例子 在您的SceneDelegate.swift中:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
    let contentView = ContentView().environmentObject(GlobalState())

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}
全局状态应符合可观察对象的要求。您应该将全局变量作为@Published放入其中

class GlobalState: ObservableObject {
    @Published var isLoggedIn: Bool
    
    init(isLoggedIn : Bool) {
        self.isLoggedIn = isLoggedIn
    }
}
发布变量的示例,与ScenedLegate中已显示的示例无关

这就是如何在视图中处理全局状态。您需要向它注入@EnvironmentObject包装器,如下所示:

struct ContentView: View {
    
    @EnvironmentObject var globalState: GlobalState

    var body: some View {
        Text("Hello World")
    }
}
现在,在您的例子中,您还需要处理AppDelegate中的状态。为了做到这一点,我建议您保护AppDelegate中的全局状态变量,并在传递到初始视图之前从ScenedLegate中的全局状态变量进行访问。要实现这一点,应在AppDelegate中添加以下内容:

var globalState : GlobalState!
    
static func shared() -> AppDelegate {
    return UIApplication.shared.delegate as! AppDelegate
}
现在,您可以返回到SceneDelegate并执行以下操作,而不是直接初始化GlobalState:

let contentView = ContentView().environmentObject(AppDelegate.shared().globalState)

我建议您使用环境对象,或者在需要时使用两者的组合。环境对象基本上是一个全局状态对象。因此,如果更改环境对象的“已发布”属性,它将反映您的视图。要设置它,您需要通过SceneDelegate将对象传递到初始视图,并且您可以使用整个视图层次结构中的状态。这也是跨非常远的同级视图传递数据的方法(或者如果您有更复杂的场景)

简单例子 在您的SceneDelegate.swift中:

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        
    let contentView = ContentView().environmentObject(GlobalState())

    if let windowScene = scene as? UIWindowScene {
        let window = UIWindow(windowScene: windowScene)
        window.rootViewController = UIHostingController(rootView: contentView)
        self.window = window
        window.makeKeyAndVisible()
    }
}
全局状态应符合可观察对象的要求。您应该将全局变量作为@Published放入其中

class GlobalState: ObservableObject {
    @Published var isLoggedIn: Bool
    
    init(isLoggedIn : Bool) {
        self.isLoggedIn = isLoggedIn
    }
}
发布变量的示例,与ScenedLegate中已显示的示例无关

这就是如何在视图中处理全局状态。您需要向它注入@EnvironmentObject包装器,如下所示:

struct ContentView: View {
    
    @EnvironmentObject var globalState: GlobalState

    var body: some View {
        Text("Hello World")
    }
}
现在,在您的例子中,您还需要处理AppDelegate中的状态。为了做到这一点,我建议您保护AppDelegate中的全局状态变量,并在传递到初始视图之前从ScenedLegate中的全局状态变量进行访问。要实现这一点,应在AppDelegate中添加以下内容:

var globalState : GlobalState!
    
static func shared() -> AppDelegate {
    return UIApplication.shared.delegate as! AppDelegate
}
现在,您可以返回到SceneDelegate并执行以下操作,而不是直接初始化GlobalState:

let contentView = ContentView().environmentObject(AppDelegate.shared().globalState)

这里有可能的解决办法

import Combine

// define key for observing
extension UserDefaults {
    @objc dynamic var status: String {
        get { string(forKey: "status") ?? "OFFLINE" }
        set { setValue(newValue, forKey: "status") }
    }
}

class Station: ObservableObject {
    @Published var status: String = UserDefaults.standard.status {
        didSet {
            UserDefaults.standard.status = status
        }
    }

    private var cancelable: AnyCancellable?
    init() {
        cancelable = UserDefaults.standard.publisher(for: \.status)
            .sink(receiveValue: { [weak self] newValue in
                guard let self = self else { return }
                if newValue != self.status { // avoid cycling !!
                    self.status = newValue
                }
            })
    }
}
注意:SwiftUI 2.0允许您通过
AppStorage
直接在视图中使用/观察
UserDefaults
,因此如果您仅在视图中需要该状态,您可以使用

struct SomeView: View {
    @AppStorage("status") var status: String = "OFFLINE"
    ...

这里有可能的解决办法

import Combine

// define key for observing
extension UserDefaults {
    @objc dynamic var status: String {
        get { string(forKey: "status") ?? "OFFLINE" }
        set { setValue(newValue, forKey: "status") }
    }
}

class Station: ObservableObject {
    @Published var status: String = UserDefaults.standard.status {
        didSet {
            UserDefaults.standard.status = status
        }
    }

    private var cancelable: AnyCancellable?
    init() {
        cancelable = UserDefaults.standard.publisher(for: \.status)
            .sink(receiveValue: { [weak self] newValue in
                guard let self = self else { return }
                if newValue != self.status { // avoid cycling !!
                    self.status = newValue
                }
            })
    }
}
注意:SwiftUI 2.0允许您通过
AppStorage
直接在视图中使用/观察
UserDefaults
,因此如果您仅在视图中需要该状态,您可以使用

struct SomeView: View {
    @AppStorage("status") var status: String = "OFFLINE"
    ...

@AppStorage
看起来正是我需要的-我会试试看。希望在我更改我的
AppDelegate
中的
状态
UserDefault
值时视图会更新。谢谢
@AppStorage
看起来正是我需要的-我会试试看。希望在我更改我的
AppDelegate
中的
状态
UserDefault
值时视图会更新。谢谢你!谢谢你。然而,我认为Swiftui2.0中的
@AppStorage
可能更有效,所以我将尝试一下:谢谢。但是,我认为SwiftUI 2.0中的
@AppStorage
可能更有效,因此我将尝试以下方法: