试图观察 UserDefaults 中的字符串,但遇到编译错误
Trying to observe a string in UserDefaults, but struggling with compile errors
我 a small project 在 GitHub。
在TopViewModel.swift中,我首先获取一个JSON对象列表,然后将它们存储在核心数据中,最后将它们显示在SwiftUI[=12中=].
这很好用,但现在我在顶部添加了一个 Picker
,允许用户 select 其中一种语言:“en”、“de”、“ru”和然后将字符串存储在 @AppStorage
:
这也很好用,这对我这个 Swift 新手来说还不错 :-)
然而,当我试图从我的视图模型观察 UserDefaults 中的 language
键时,我的问题就开始了:
我尝试将以下代码添加到 TopViewModel.swift,但它甚至无法编译:
init() {
UserDefaults.standard.addObserver(self, forKeyPath: "language", options: NSKeyValueObservingOptions.new, context: nil)
}
deinit() {
UserDefaults.standard.removeObserver(self, forKeyPath: "language")
}
func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// How to get language here from the params?
updateTopEntities(language: language)
fetchTopModels(language: language)
}
其中一个编译错误是我的视图模型不是 NSObject
Cannot convert value of type 'TopViewModel' to expected argument type 'NSObject'
为什么不呢?
更新:
我已将 NSObject
添加为 [TopViewModel.swift] 的父对象,现在当用户 select 是语言中的值时调用回调方法 observeValue
Picker
:
class TopViewModel: NSObject, ObservableObject {
override init() {
super.init()
UserDefaults.standard.addObserver(self,
forKeyPath: "language",
options: NSKeyValueObservingOptions.new, context: nil)
let language = UserDefaults.standard.string(forKey: "language") ?? "en"
updateTopEntities(language: language)
fetchTopModels(language: language)
}
deinit {
UserDefaults.standard.removeObserver(self, forKeyPath: "language")
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard keyPath == "language" else { return }
guard change?.count == 2 else { return }
print("observeValue language=\(change["new"].value)")
// How to get language here from the params?
//updateTopEntities(language: language)
//fetchTopModels(language: language)
}
剩下的唯一(我认为是次要的)问题是我不知道如何从 observeValue
参数中获取 language
字符串(调用 UserDefaults.standard.string(forKey: "language")
作为解决方法有效,但我有兴趣从参数中提取值,因为调试器在那里显示 language
字符串):
你可以试试这个。
extension UserDefaults {
@objc dynamic var language: String {
get { self.string(forKey: "language") ?? "en" }
set { self.setValue(newValue, forKey: "language") }
}
}
class MyObject {
var observer: NSKeyValueObservation?
init() {
observer = UserDefaults.standard.observe(\.language, options: [.new], changeHandler: { (defaults, change) in
// your change logic here
})
}
deinit {
observer?.invalidate()
}
}
更新
import Foundation
extension UserDefaults {
@objc dynamic var language: String {
get { self.string(forKey: #function) ?? "en" }
set { self.setValue(newValue, forKey: #function) }
}
}
class TopViewModel: NSObject {
let defaults = UserDefaults.standard
let languageKeyPath = #keyPath(UserDefaults.language)
override init() {
super.init()
defaults.addObserver(self, forKeyPath: languageKeyPath, options: .new, context: nil)
let language = defaults.language
print("initialLanguage: \(language)")
defaults.language = "en"
defaults.language = "fr"
}
deinit {
defaults.removeObserver(self, forKeyPath: languageKeyPath)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?) {
guard (object as? UserDefaults) === defaults,
keyPath == languageKeyPath,
let change = change
else { return }
if let updatedLanguage = change[.newKey] as? String {
print("updatedLanguage : \(updatedLanguage)")
}
}
}
// Test code, run init to observe changes
let viewModel = TopViewModel()
这是我自己的解决方案,我必须添加 NSObject
作为父 class 并使用 change[.newKey]
在 observeValue
回调方法中访问新值:
class TopViewModel: NSObject, ObservableObject {
override init() {
super.init()
UserDefaults.standard.addObserver(self,
forKeyPath: "language",
options: NSKeyValueObservingOptions.new,
context: nil)
let language = UserDefaults.standard.string(forKey: "language") ?? "en"
// use the language string value here
}
deinit {
UserDefaults.standard.removeObserver(self, forKeyPath: "language")
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard keyPath == "language",
let change = change,
let language = change[.newKey] as? String else {
return
}
// use the language string value here
}
我 a small project 在 GitHub。
在TopViewModel.swift中,我首先获取一个JSON对象列表,然后将它们存储在核心数据中,最后将它们显示在SwiftUI[=12中=].
这很好用,但现在我在顶部添加了一个 Picker
,允许用户 select 其中一种语言:“en”、“de”、“ru”和然后将字符串存储在 @AppStorage
:
这也很好用,这对我这个 Swift 新手来说还不错 :-)
然而,当我试图从我的视图模型观察 UserDefaults 中的 language
键时,我的问题就开始了:
我尝试将以下代码添加到 TopViewModel.swift,但它甚至无法编译:
init() {
UserDefaults.standard.addObserver(self, forKeyPath: "language", options: NSKeyValueObservingOptions.new, context: nil)
}
deinit() {
UserDefaults.standard.removeObserver(self, forKeyPath: "language")
}
func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
// How to get language here from the params?
updateTopEntities(language: language)
fetchTopModels(language: language)
}
其中一个编译错误是我的视图模型不是 NSObject
Cannot convert value of type 'TopViewModel' to expected argument type 'NSObject'
为什么不呢?
更新:
我已将 NSObject
添加为 [TopViewModel.swift] 的父对象,现在当用户 select 是语言中的值时调用回调方法 observeValue
Picker
:
class TopViewModel: NSObject, ObservableObject {
override init() {
super.init()
UserDefaults.standard.addObserver(self,
forKeyPath: "language",
options: NSKeyValueObservingOptions.new, context: nil)
let language = UserDefaults.standard.string(forKey: "language") ?? "en"
updateTopEntities(language: language)
fetchTopModels(language: language)
}
deinit {
UserDefaults.standard.removeObserver(self, forKeyPath: "language")
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard keyPath == "language" else { return }
guard change?.count == 2 else { return }
print("observeValue language=\(change["new"].value)")
// How to get language here from the params?
//updateTopEntities(language: language)
//fetchTopModels(language: language)
}
剩下的唯一(我认为是次要的)问题是我不知道如何从 observeValue
参数中获取 language
字符串(调用 UserDefaults.standard.string(forKey: "language")
作为解决方法有效,但我有兴趣从参数中提取值,因为调试器在那里显示 language
字符串):
你可以试试这个。
extension UserDefaults {
@objc dynamic var language: String {
get { self.string(forKey: "language") ?? "en" }
set { self.setValue(newValue, forKey: "language") }
}
}
class MyObject {
var observer: NSKeyValueObservation?
init() {
observer = UserDefaults.standard.observe(\.language, options: [.new], changeHandler: { (defaults, change) in
// your change logic here
})
}
deinit {
observer?.invalidate()
}
}
更新
import Foundation
extension UserDefaults {
@objc dynamic var language: String {
get { self.string(forKey: #function) ?? "en" }
set { self.setValue(newValue, forKey: #function) }
}
}
class TopViewModel: NSObject {
let defaults = UserDefaults.standard
let languageKeyPath = #keyPath(UserDefaults.language)
override init() {
super.init()
defaults.addObserver(self, forKeyPath: languageKeyPath, options: .new, context: nil)
let language = defaults.language
print("initialLanguage: \(language)")
defaults.language = "en"
defaults.language = "fr"
}
deinit {
defaults.removeObserver(self, forKeyPath: languageKeyPath)
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey: Any]?,
context: UnsafeMutableRawPointer?) {
guard (object as? UserDefaults) === defaults,
keyPath == languageKeyPath,
let change = change
else { return }
if let updatedLanguage = change[.newKey] as? String {
print("updatedLanguage : \(updatedLanguage)")
}
}
}
// Test code, run init to observe changes
let viewModel = TopViewModel()
这是我自己的解决方案,我必须添加 NSObject
作为父 class 并使用 change[.newKey]
在 observeValue
回调方法中访问新值:
class TopViewModel: NSObject, ObservableObject {
override init() {
super.init()
UserDefaults.standard.addObserver(self,
forKeyPath: "language",
options: NSKeyValueObservingOptions.new,
context: nil)
let language = UserDefaults.standard.string(forKey: "language") ?? "en"
// use the language string value here
}
deinit {
UserDefaults.standard.removeObserver(self, forKeyPath: "language")
}
override func observeValue(forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?) {
guard keyPath == "language",
let change = change,
let language = change[.newKey] as? String else {
return
}
// use the language string value here
}