ioskotlinkotlin-multiplatformphpickerviewcontroller

How to handle callback from PHPickerViewController in a KMM project?


I'm working on a KMM application with a video picking feature. For the iOS implementation, I'm using a PHPickerViewController. The view controller is well presented but I don't know how to handle the callback param didFinishPicking from PHPickerViewControllerDelegateProtocol.

My goal is to extract the byte array from the file given by picker

Here's my WIP:

private val delegate = object : NSObject(), PHPickerViewControllerDelegateProtocol,
    UINavigationControllerDelegateProtocol {

    override fun picker(picker: PHPickerViewController, didFinishPicking: List<*>) {
        // TODO extract the byte array form the file referenced in the picker param
        picker.dismissViewControllerAnimated(flag = false) {}
    }
} 

Solution

  • When implementing some iOS specific features from Kotlin part, I always start with looking for examples on GitHub, in this case you can find a couple of examples with this query.

    In some cases when this search doesn't give you any results, you can look for iOS examples and figure out how to convert it to Kotlin. It's easier to do with ObjC examples, since that's what Kotlin uses as the source. Using such method you could've user this article as reference.

    KMP has some limitations related to ObjC generics, and also some naming could be confusing, so it's always a good idea to take a look at ObjC headers, using Xcode or documentation.

    - (void)picker:(PHPickerViewController *)picker didFinishPicking:(NSArray<PHPickerResult *> *)results
    

    I think from this code it should be cleaner that you need results here, but in kotlin this argument is called didFinishPicking - that's because of how ObjC is built, function arguments have kind of two names, sometimes they're the same, but Kotlin can only take one of them and it takes first one.

    And because of ObjC generic, type is lost so you need to cast it:

    val result = didFinishPicking.firstOrNull() as? PHPickerResult
    

    At the end you can use this object to get your data. Since you're looking for videos, use UTTypeMovie:

    result.itemProvider().loadDataRepresentationForContentType(UTTypeMovie) { data, error ->
        if (data != null) {
            val bytes = data.toByteArray()
        }
    }