Blog Infos
Author
Published
Topics
, , , ,
Published
Photo by Miguel A Amutio on Unsplash
What is Key-Value Observing (KVO)?

Key-Value Observing is a pattern used in Cocoa frameworks to allow objects to be notified of changes to specified properties of another object. This mechanism is particularly handy when dealing with dynamic data changes in iOS applications.

KVO establishes a dependency between an observer and an observed object. The observer registers its interest in specific properties of the observed object. Whenever the observed property’s value changes, the observer is notified. This allows the observer to react accordingly, often by updating its state.

Observing an attribute of AVPlayer object in Swift:

internal class AudioPlayerImpl: NSObject, AudioPlayer {
    
    private let player = AVPlayer()
    private var observation: NSKeyValueObservation?
    
    override init() {
        super.init()
        
        addObserver(self, forKeyPath: "timeControlStatus", options: [.new], context: nil)
    }
    
    override func observeValue(
        forKeyPath keyPath: String?,
        of: Any?,
        change: [NSKeyValueChangeKey: Any]?,
        context: UnsafeMutableRawPointer?
    ) {
        print("\(keyPath!) has been updated to: \(change![.newKey]!)")
    }
}
KVO in KMP iOS

Using KVO in the KMP iOS source set might seem like a straightforward approach, but it comes with limitations:

  • Inheriting from NSObject is not possible if you are already inheriting from/implementing any Kotlin class/interface:
Mixing Kotlin and Objective-C supertypes is not supported
  • Overriding a function to observe changes is also not possible because observeValueForKeyPath is imported as Kotlin extension, which can’t be overridden:
  • ‘observeValueForKeyPath’ overrides nothing

     

What should we do?

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Migrating to Jetpack Compose – an interop love story

Most of you are familiar with Jetpack Compose and its benefits. If you’re able to start anew and create a Compose-only app, you’re on the right track. But this talk might not be for you…
Watch Video

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer for Jetpack Compose
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engin ...
Google

Migrating to Jetpack Compose - an interop love story

Simona Milanovic
Android DevRel Engineer f ...
Google

Jobs

🛟 Cinterops for the rescue!

The KMP plugin provides a DSL to define a custom definition file where we can declare our Objective-C protocol (interface) to be available in the Kotlin iOS source set.

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;
  • Define the interoperability using the KMP plugin DSL
kotlin {
    androidTarget()
    
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { target ->
        target.binaries.framework {
            baseName = "shared"
            isStatic = true
        }
        target.compilations.getByName("main") {
            // The default file path is src/nativeInterop/cinterop/<interop-name>.def
            val nskeyvalueobserving by cinterops.creating
        }
    }
}
  • After compiling the KMP module, we have an interface where we can implement and observe NSObject changes:
internal class AudioPlayerImpl: AudioPlayer {

  private val player = AVPlayer()

  init {
      player.addObserver(
          observer = timeControlObserver,
          forKeyPath = "timeControlStatus",
          options = NSKeyValueObservingOptionNew,
          context = null
      )
  }
  
  private val timeControlObserver: NSObject = object : NSObject(), NSKeyValueObservingProtocol {
  
      override fun observeValueForKeyPath(
              keyPath: String?,
              ofObject: Any?,
              change: Map<Any?, *>?,
              context: COpaquePointer?
          ) {
              println("${keyPath} has been updated to: ${change!![NSKeyValueChangeNewKey]!!}")
          }
      }
  }
}

💡 Remember to properly stop observing a property when you no longer need it to avoid memory leaks.

player.removeObserver(timeControlObserver, "timeControlStatus")

KVO provides a robust mechanism for monitoring property changes in iOS development. By understanding how to utilize KVO effectively within your KMP iOS source set, you can harness the full potential of this approach while leveraging the power of Kotlin Multiplatform.

Take a look at https://github.com/andremion/RetroBeat to see how KVO was used to listen for AVPlayerItem status updates.

Happy coding! 🤓

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu