微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

尝试在 UserDefaults 中观察字符串,但遇到编译错误 更新

如何解决尝试在 UserDefaults 中观察字符串,但遇到编译错误 更新

我在 GitHub 上有 a small project

TopViewModel.swift 中,我首先获取对象的 JSON 列表,然后将它们存储在 Core Data 中,最后将它们显示在 SwiftUI List 中。

这很好用,但现在我在顶部添加一个 Picker,允许用户选择其中一种语言:“en”、“de”、“ru”,然后将字符串存储在 { {1}}:

screenshot

这也很好用,这对我作为 Swift 新手来说还不错:-)

然而,当我试图从我的视图模型观察 UserDefaults 中的 @AppStorage 键时,我的问题开始了:

xcode

我尝试将以下代码添加TopViewModel.swift,但它甚至无法编译:

language

编译错误之一是我的视图模型不是 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) }

无法将“Topviewmodel”类型的值转换为预期的参数类型“NSObject”

为什么不呢?

更新:

我已将 NSObject 作为父项添加到 [Topviewmodel.swift],现在当用户选择语言 NSObject 中的值时调用回调方法 observeValue:>

Picker

剩下的唯一(我认为是次要的)问题是我不知道如何从 class Topviewmodel: NSObject,ObservableObject { override init() { super.init() UserDefaults.standard.addobserver(self,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?,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) } 参数中获取 language 字符串(调用 observeValue 作为解决方法有效,但我有兴趣从参数中提取值,因为调试器在那里显示 UserDefaults.standard.string(forKey: "language") 字符串):

xcode 2

解决方法

你可以试试这个。

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 作为父类并使用 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?,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
    }

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。