是否可以使用 Kotlin/Native 观察 iOS NSObject 值的变化

Kam*_*hev 7 ios kotlin kotlin-native kotlin-multiplatform

我正在尝试实现一个观察者,以更改 UserDefaults 中给定键的值,该值来自用 Kotlin/Native 编写的多平台项目的 ios 本机部分。这是我写的代码:

fun subscribeForDataChange(storeName: String, callback: () -> Unit) {
        NSUserDefaults(storeName).addObserver(
            object : NSObject() {
                fun observeValue(
                    observer: NSObject,
                    forKeyPath: String,
                    options: NSKeyValueObservingOptions,
                    context: COpaquePointer?
                ) {
                    callback()
                    print("Data Changed!!!")
                }
            },
            options = NSKeyValueObservingOptionNew,
            forKeyPath = DATA_KEY,
            context = null
        )

    }
Run Code Online (Sandbox Code Playgroud)

问题是我从来没有收到通知,很可能是因为observeValueNSObject 中没有定义,但是我还应该做什么才能实现这一点?

Vít*_*ola 0

这是同一组中的 2 个应用程序共享 UserDefaults 的解决方案。我在两个进程之间共享 SQLite 数据库,并且我需要知道一个进程何时将 somethink 写入数据库。经典流不会被触发,因此我编写了一个流助手,当 NSUserDefaults 更改时,它会在 Kotlin 中发出值。

NSObject作为 Swift 代码库的一部分实现( Swift 代码灵感)。当 NSUserDefaults 更改时,Swift 会调用 Kotlin 方法。首先定义接口。

interface NSUserDefaultsKotlinHelper {
    fun userDefaultsChanged()
}

interface SwiftInjector {
    fun injectIntoSwift(nsUserDefaultsKotlinHelper: NSUserDefaultsKotlinHelper?)
}
Run Code Online (Sandbox Code Playgroud)

让该接口将侦听器注入到 Swift 代码中:

class InterprocessObserver: NSObject, SwiftInjector {
    let key: String = "interprocess_communication"
    private var nsUserDefaultsKotlinHelper : NSUserDefaultsKotlinHelper?
    private let userDefaults = UserDefaults.init(suiteName: "group.your.group.id")

    override init() {
        super.init()
        userDefaults?.addObserver(self, forKeyPath: key, options: [.old, .new], context: nil)
    }
    
    func injectIntoSwift(nsUserDefaultsKotlinHelper: NSUserDefaultsKotlinHelper?) {
        self.nsUserDefaultsKotlinHelper = nsUserDefaultsKotlinHelper
    }
    
    func dataChangedFromAnotherProcess(data : [AnyHashable : Any]) {
        userDefaults?.set(data, forKey: key)
    }
    
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        guard let _ = change, object != nil, keyPath == key else { return }
        nsUserDefaultsKotlinHelper?.userDefaultsChanged()
    }
    
    deinit {
        userDefaults?.removeObserver(self, forKeyPath: key, context: nil)
    }
}
Run Code Online (Sandbox Code Playgroud)

在 Kotlin 中注入侦听器 - 我将在流开始收集时注入:

class InterProcessCommunication(val interPlatformInjector: InterplatformInjector) : InterplatformInjector by interplatformInjector {

    val testFlow: Flow<Emitter> = flow {
        val channel = Channel<Emitter>(CONFLATED)
        channel.trySend(Emitter.STAY_CALM)

        val listener = object : IInterprocessCommunication {
            override fun interProcessChanged() {
                channel.trySend(Emitter.EMIT)
            }

        }
        interPlatformInjector.injectListener(listener)
        try {
            for (item in channel) {
                emit(item)
            }
        } finally {
            interPlatformInjector.injectListener(null)
        }

    }
}

Run Code Online (Sandbox Code Playgroud)

使用 Koin 创建对象将是:

//Swift
func initObservers() {
    let interplatformInjector = InterprocessObserver()
    initKoin(interplatformInjector : interplatformInjector)
}
//Kotlin
fun initKoin(interplatformInjector : InterplatformInjector){
    startKoin {
      module {
         single {InterProcessCommunication(interplatformInjector)}
      }
    }
}

//Swift Second process (for example NotificationService)
func dataChanged(interprocessObserver : InterprocessObserver) {
    interprocessObserver.dataChangedFromAnotherProcess(data) //data could be anythink - for example a string
}
Run Code Online (Sandbox Code Playgroud)

该方法dataChenged()将触发 Kotlin 流程。这是你想要的?