Place this file under composeApp/src/nativeInterop/cinterop/nskeyvalueobserving.def
# https://slack-chats.kotlinlang.org/t/528792/hello-is-it-possible-to-use-in-kotlin-addobserver-on-nsobjec#968aa67c-9166-41c6-b44c-d549a377c08e
package = platform.foundation
language = Objective-C
---
#import <Foundation/Foundation.h>
@protocol NSKeyValueObserving
@required
// https://developer.apple.com/documentation/objectivec/nsobject/1416553-observevalueforkeypath
- (void) observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary<NSKeyValueChangeKey, id> *)change
context:(void *)context;
@end;
Then in composeApp/build.gradle.kts, add this:
kotlin {
// iOS 平台配置:支持真机和模拟器
listOf(
iosX64(), // x64 模拟器
iosArm64(), // 真机
iosSimulatorArm64() // Apple Silicon 模拟器
).forEach { target ->
target.compilations.getByName("main") {
// 定义文件路径:src/nativeInterop/cinterop/nskeyvalueobserving.def
// 参考文档:https://kotlinlang.org/docs/multiplatform-dsl-reference.html#cinterops
val nskeyvalueobserving by cinterops.creating
}
}
}Usage:
import platform.darwin.NSObject
import platform.darwin.NSObjectProtocol
import platform.Foundation.addObserver
import platform.Foundation.removeObserver
import platform.Foundation.NSKeyValueObservingOptionNew
import platform.foundation.NSKeyValueObservingProtocol
import platform.AVFAudio.AVAudioSession
import platform.AVFAudio.outputVolume
class Foo {
private val volumeObserver = object : NSObject(), NSKeyValueObservingProtocol {
override fun observeValueForKeyPath(
keyPath: String?,
ofObject: Any?,
change: Map<Any?, *>?,
context: COpaquePointer?
) {
if (keyPath == "outputVolume")
handleVolumeChange()
}
}
private fun setupObserver() {
AVAudioSession.sharedInstance().addObserver(
observer = volumeObserver,
forKeyPath = "outputVolume",
options = NSKeyValueObservingOptionNew,
context = null
)
}
}