Is there any other way to access observeValueForKe...
# compose-ios
d
Is there any other way to access observeValueForKeyPath other than following the below process: https://proandroiddev.com/leveraging-key-value-observing-kvo-in-kotlin-multiplatform-kmp-for-ios-231519e5c1ff
I have confirmed, this does not work.
r
I remember reading that article and getting a demo working with it, but I didn't have a real need for it so I haven't looked back at it since.
I made this proof-of-concept in Multiplatform Settings but since I already had an existing observability mechanism there wasn't any reason to actually use it https://github.com/russhwolf/multiplatform-settings/commit/3b09345ab5cdbbea22711ad79a8f233975db69af
d
Hi @russhwolf, thanks for the help. I tried following the commit you linked. 1. Added required things to gradle.properties and build.gradle.kts. 2. Added the nskeyvalueobserving.def inside my src/iosMain/cinterop/ 3. I created an observer according to my needs:
Copy code
@OptIn(ExperimentalForeignApi::class)
private fun setupStatusObserver(item: AVPlayerItem, onReady: () -> Unit): AVPlayerStatusObservation {
    val observer = object : NSObject(), NSKeyValueObservingProtocol {
        override fun observeValueForKeyPath(
            keyPath: String?,
            ofObject: Any?,
            change: Map<Any?, *>?,
            context: COpaquePointer?
        ) {
            if (keyPath == "status" && item.status == AVPlayerItemStatusReadyToPlay) {
                onReady()
            }
        }
    }

    item.addObserver(
        observer = observer,
        forKeyPath = "status",
        options = NSKeyValueObservingOptionNew,
        context = null
    )

    return object : AVPlayerStatusObservation {
        override fun invalidate() {
            item.removeObserver(observer, forKeyPath = "status")
        }
    }
}
The issue I am getting is for the import of NSKeyValueObservingProtocol: import platform.Foundation.NSKeyValueObservingProtocol. platform.Foundation has no import for NSKeyValueObservingProtocol
r
Hmm. I'm not sure then. Can you link your project (or show your build.gradle changes and def file)?
d
build.gradle.kts:
Copy code
val appKeysProperties = Properties()
val appKeysPropertiesFile = project.rootProject.file("actual_name.properties")
if (appKeysPropertiesFile.exists()) {
    appKeysProperties.load(appKeysPropertiesFile.inputStream())
}

plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidApplication)
    alias(libs.plugins.composeMultiplatform)
    alias(libs.plugins.composeCompiler)
    alias(libs.plugins.jetbrains.kotlin.serialization)
    alias(libs.plugins.ksp)
    alias(libs.plugins.room)
    alias(libs.plugins.wire)
    alias(libs.plugins.google.services)
    alias(libs.plugins.crashlytics.plugin)
    alias(libs.plugins.firebase.perf.plugin)
    alias(libs.plugins.build.konfig.plugin)
}

kotlin {

    targets.withType<KotlinNativeTarget>()
        .matching { it.konanTarget.family.isAppleFamily }
        .configureEach {
            compilations.getByName("main") {
                cinterops.create("nskeyvalueobserving") {
                    defFile = file("src/iosMain/cinterop/nskeyvalueobserving.def")
                }
            }
        }

    androidTarget {
        @OptIn(ExperimentalKotlinGradlePluginApi::class)
        compilerOptions {
            jvmTarget.set(JvmTarget.JVM_11)
        }
    }

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "ComposeApp"
            isStatic = true
            export(libs.kmpnotifier)
        }
    }

    room {
        schemaDirectory("$projectDir/schemas")
    }

    sourceSets {
        androidMain.dependencies {
            implementation(compose.preview)
            implementation(libs.androidx.activity.compose)
            implementation(libs.core.splashscreen)
            implementation(libs.koin.android)
            implementation(libs.koin.androidx.compose)
            implementation(libs.ktor.client.okhttp)

            // exoplayer
            implementation(libs.androidx.media3.ui)
            implementation(libs.androidx.media3.exoplayer)
            implementation(libs.androidx.media3.common)
            implementation(libs.androidx.media3.hls)

            implementation(libs.otpless.android)
            implementation(libs.bundles.playService)
            implementation(libs.libphonenumber)
            implementation(libs.androidx.lifecycle.livedata)
            implementation(libs.truecaller.sdk)
            implementation(project.dependencies.platform(libs.firebase.bom))
            implementation(libs.firebase.config)
            implementation(libs.firebase.crashlytics)
            implementation(libs.firebase.analytics)
            implementation(libs.firebase.perf)
            implementation(libs.mixpanel.android)
            implementation(libs.appsFlyer.android.sdk)
            implementation(project(":paygate"))
            implementation(libs.app.update.ktx)
        }

        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material3)
            implementation(compose.ui)
            implementation(compose.components.resources)
            implementation(compose.components.uiToolingPreview)
            implementation(libs.androidx.lifecycle.viewmodel)
            implementation(libs.androidx.lifecycle.runtime.compose)
            api(compose.materialIconsExtended)
            implementation(libs.cmptoast)
            implementation(libs.jetbrains.compose.navigation)
            implementation(libs.kotlinx.serialization.json)
            implementation(libs.androidx.room.runtime)
            implementation(libs.sqlite.bundled)
            implementation(libs.koin.compose)
            implementation(libs.koin.compose.viewmodel)
            api(libs.koin.core)

            implementation(libs.bundles.ktor)
            implementation(libs.bundles.coil)
            implementation(libs.paging.compose.common)

            api(libs.datastore.preferences)
            api(libs.datastore)
            api(libs.wire.runtime)
            implementation(libs.napier)
            implementation(libs.konnectivity)
            implementation(libs.kotlinx.datetime)

            //permissions handler
            api(libs.moko.permissions)
            api(libs.moko.permissions.compose)
            implementation(libs.permissions.notifications)

            /*IAP billing sdk*/
            implementation(project(":kratos"))
            implementation(libs.sdp.ssp.compose.multiplatform)

            api(libs.kmpnotifier)
            implementation(libs.kottie)
            /*Api calls logger for kmp*/
            implementation(libs.lens.logger)
        }

        nativeMain.dependencies {
            implementation(libs.ktor.client.darwin)
        }

//        Android specific
        dependencies {
            debugImplementation(compose.uiTooling)
            ksp(libs.androidx.room.compiler)
            //pluto
            debugImplementation(libs.bundles.plutoDebug)
            releaseImplementation(libs.bundles.plutoRelease)

            coreLibraryDesugaring(libs.desugar.jdk.libs)

            implementation(libs.installreferrer)
        }
    }
}

wire {
    kotlin {}
    sourcePath {
        srcDir("src/commonMain/proto")
    }
}

val crashlyticsEnabled = false
val proguardEnabled = false
val debugEnabled = false

android {
    namespace = libs.versions.application.id.get().toString()
    compileSdk = libs.versions.android.compileSdk.get().toInt()

    defaultConfig {
        applicationId = libs.versions.application.id.get().toString()
        minSdk = libs.versions.android.minSdk.get().toInt()
        targetSdk = libs.versions.android.targetSdk.get().toInt()
        versionCode = libs.versions.versioncode.get().toInt()
        versionName = libs.versions.appVersion.get().toString()
    }
    signingConfigs {
        create("release") {
            // release configs
        }
    }

    packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }

    buildTypes {
        getByName("release") {
            isMinifyEnabled = proguardEnabled
            isShrinkResources = proguardEnabled
            isDebuggable = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "<http://proguard-rules.pro|proguard-rules.pro>"
            )
            signingConfig = signingConfigs.getByName("release")
        }

        getByName("debug") {
            isMinifyEnabled = false
            isDebuggable = true
            versionNameSuffix = "-dev"
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
        isCoreLibraryDesugaringEnabled = true
    }
    buildFeatures {
        buildConfig = true
    }
}
nskeyvalueobserving.def:
Copy code
# CF <https://proandroiddev.com/leveraging-key-value-observing-kvo-in-kotlin-multiplatform-kmp-for-ios-231519e5c1ff>
package = platform.Foundation
language = Objective-C
---
#import <Foundation/Foundation.h>

@protocol NSKeyValueObserving
@required
- (void) observeValueForKeyPath:(NSString *)keyPath
    ofObject:(id)object
    change:(NSDictionary<NSKeyValueChangeKey, id> *)change
    context:(void *)context;
@end;
r
Well, nothing jumps out at me. Sorry. Not sure why it isn't working for you, but I confirmed that my example is still working for me.