Fourty-Three
Fourty-Three

Reputation: 41

Implementing Cross-Platform Camera Functionality in KMM: Saving Images on iOS

I'm working on a Kotlin Multiplatform Mobile (KMM) application and am trying to implement unified camera functionality that allows users to take photos and save them to the device's filesystem, both on Android and iOS. While I've successfully completed the Android implementation, I'm encountering difficulties finding and implementing an equivalent approach for iOS. For the iOS implementation, I'm attempting to use UIImagePickerController to capture photos and subsequently save the captured image to the device's filesystem. My main goal is to integrate this functionality as seamlessly as possible into the shared KMM code to ensure reusability and consistency across both platforms.

I've made an attempt by creating a CameraManager class in iosMain utilizing UIImagePickerController, but I'm facing issues on how to correctly save the captured image and pass the result back to the shared code.

The CameraManager looks like this:

actual class CameraManager {
    private var onImagePicked: (ByteArray) -> Unit = {}
    @Composable
    actual fun registerCameraManager(onImagePicked: (ByteArray) -> Unit) {
        this.onImagePicked = onImagePicked
    }
    @OptIn(ExperimentalForeignApi::class)
    actual fun takeImage() {
        val picker = UIImagePickerController().apply {
            sourceType =  UIImagePickerControllerSourceType.UIImagePickerControllerSourceTypeCamera
            cameraCaptureMode = UIImagePickerControllerCameraCaptureMode.UIImagePickerControllerCameraCaptureModePhoto
            delegate = object : NSObject(), UIImagePickerControllerDelegateProtocol,
                UINavigationControllerDelegateProtocol {
                override fun imagePickerController(
                    picker: UIImagePickerController,
                    didFinishPickingMediaWithInfo: Map<Any?, *>
                ) {
                    val originalImage = didFinishPickingMediaWithInfo.getValue(
                        UIImagePickerControllerOriginalImage
                    ) as? UIImage

                    originalImage?.let { image ->
                        // Convert image to JPEG data
                        val data = UIImageJPEGRepresentation(image, 1.0)

                        // Save to documents directory
                        val path = NSSearchPathForDirectoriesInDomains(
                            NSDocumentDirectory,
                            NSUserDomainMask,
                            true
                        ).first().toString()
                        val filePath = "$path/" + NSUUID.UUID().UUIDString + ".jpg"
                        data?.writeToFile(filePath, atomically = true)

                        // Convert to bytes
                        val bytes = ByteArray(data!!.length.toInt())
                        //data?.copyBytes(to: bytes)
                        memcpy(bytes.refTo(0), data!!.bytes, data!!.length)

                        // Return image bytes
                        onImagePicked(bytes)
                    }
                    picker.dismissViewControllerAnimated(true, null)
                }
            }
        }
        UIApplication.sharedApplication.keyWindow?.rootViewController?.presentViewController(
            picker, true, null
        )
    }
}

An object of this class is only created shortly before usage in my UI-Component. There it is created with

cameraManager.registerCameraManager { imageBytes ->
    onEvent(NoteListEvent.OnPhotoPicked(imageBytes))
} 

which is a little helper function

actual fun createCameraManager(): CameraManager {
    return remember{
        CameraManagerOld(rootController)
    }
}

Can anyone assist me in implementing such a mechanism or share best practices for handling camera operations and image saving in a KMM application? Handling and implementing the Photo-Library on iOS was way easier then this..

Upvotes: 4

Views: 1867

Answers (0)

Related Questions