[英]How to observe changes in UserDefaults?
我的视图中有一个@ObservedObject
:
struct HomeView: View {
@ObservedObject var station = Station()
var body: some View {
Text(self.station.status)
}
它根据来自Station.status
的String
更新文本:
class Station: ObservableObject {
@Published var status: String = UserDefaults.standard.string(forKey: "status") ?? "OFFLINE" {
didSet {
UserDefaults.standard.set(status, forKey: "status")
}
}
但是,我需要在AppDelegate
中更改status
的值,因为这是我收到Firebase Cloud Messages的地方:
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
// If you are receiving a notification message while your app is in the background,
// this callback will not be fired till the user taps on the notification launching the application.
// Print full message.
let rawType = userInfo["type"]
// CHANGE VALUE OF status HERE
}
但是,如果我更改AppDelegate
中的status
UserDefaults
值 - 在我看来它不会更新。
当status
更改时,如何通知我视图中的@ObservedObject
?
编辑:忘记提及在上述示例中使用了 SwiftUI 的 2.0 beta 版本。
这是可能的解决方案
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"
...
如果需要,我建议您使用环境 object或两者的组合。 环境对象基本上是一个全局 state对象。 因此,如果您更改环境 object 的已发布属性,它将反映您的视图。 要设置它,您需要通过SceneDelegate将 object 传递给您的初始视图,并且您可以在整个视图层次结构中使用 state。 这也是跨非常远的兄弟视图(或者如果您有更复杂的场景)传递数据的方式。
在您的 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()
}
}
全局 state 应符合ObservableObject 。 您应该将全局变量作为@Published放在那里。
class GlobalState: ObservableObject {
@Published var isLoggedIn: Bool
init(isLoggedIn : Bool) {
self.isLoggedIn = isLoggedIn
}
}
如何发布变量的示例,与 SceneDelegate 中已显示的示例无关
这就是您如何在视图中使用全局 state 的方式。 您需要使用@EnvironmentObject包装器注入它,如下所示:
struct ContentView: View {
@EnvironmentObject var globalState: GlobalState
var body: some View {
Text("Hello World")
}
}
现在,在您的情况下,您还想在 AppDelegate 中使用 state。 为了做到这一点,我建议您在 AppDelegate 中保护全局 state 变量,并在您的 SceneDelegate 中从那里访问它,然后再传递到初始视图。 为此,您应该在 AppDelegate 中添加以下内容:
var globalState : GlobalState!
static func shared() -> AppDelegate {
return UIApplication.shared.delegate as! AppDelegate
}
现在您可以 go 回到您的 SceneDelegate 并执行以下操作,而不是直接初始化 GlobalState:
let contentView = ContentView().environmentObject(AppDelegate.shared().globalState)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.