I'm trying to use the UiKit API PHPickerViewController using KMM and Compose for iOS.
import androidx.compose.runtime.Composable
import androidx.compose.ui.interop.LocalUIViewController
import platform.PhotosUI.PHPickerConfiguration
import platform.PhotosUI.PHPickerViewController
import platform.PhotosUI.PHPickerViewControllerDelegateProtocol
import platform.darwin.NSObject
@Composable
actual fun pickerController() {
val uiViewController = LocalUIViewController.current
val configuration = PHPickerConfiguration()
val pickerController = PHPickerViewController(configuration)
val pickerDelegate = object : NSObject(), PHPickerViewControllerDelegateProtocol {
override fun picker(picker: PHPickerViewController, didFinishPicking: List<*>) {
println("didFinishPicking: $didFinishPicking")
picker.dismissViewControllerAnimated(flag = false, completion = {})
uiViewController.dismissModalViewControllerAnimated(false)
}
}
pickerController.setDelegate(pickerDelegate)
uiViewController.presentViewController(pickerController, animated = false, completion = null)
}
This displays the image picker:

Unfortunately, when clicking on Cancel, the delegate callback is not called, and I get the following message on the console:
[Picker] PHPickerViewControllerDelegate doesn't respond to picker:didFinishPicking:
Is it possible to implement the callback in Kotlin?
What am I missing?
Since
pickerDelegateisNSObject, it's lifecycle follows ObjC rules, not KMM memory model.So as soon as the execution leaves composable block, this objects gets released - as
setDelegatetakes it as weak reference.You can fix it by storing it using
remember.Also using your function is dangerous because you're gonna call
presentViewControlleron each recomposition - e.g. if some of your reactive data changes on the calling side.You can update it to return an action that will present it, but store delegate and the action itself using
remember:Usage: