Hello all, working on a project to scan QR using c...
# multiplatform
s
Hello all, working on a project to scan QR using compose multiplatform in iOS. The scanner works fine, but for some reason I am not receiving any callbacks from captureOutput even though i point the scanner right at the QR. Like the callbacks are received and suddenly it stops. Any idea why this issue is happening?
đź§µ 1
👍 1
Code:
Copy code
@Composable
private fun RealDeviceCamera(
    camera: AVCaptureDevice,
    onQrCodeScanned: (String) -> Boolean
) {
    val capturePhotoOutput = remember { AVCapturePhotoOutput() }
    var actualOrientation by remember {
        mutableStateOf(
            AVCaptureVideoOrientationPortrait
        )
    }

    val captureSession: AVCaptureSession = remember {
        AVCaptureSession().also { captureSession ->
            captureSession.sessionPreset = AVCaptureSessionPresetPhoto
            val captureDeviceInput: AVCaptureDeviceInput =
                deviceInputWithDevice(device = camera, error = null)!!
            captureSession.addInput(captureDeviceInput)
            captureSession.addOutput(capturePhotoOutput)

            //Initialize an AVCaptureMetadataOutput object and set it as the output device to the capture session.
            val metadataOutput = AVCaptureMetadataOutput()
            if (captureSession.canAddOutput(metadataOutput)) {
                captureSession.addOutput(metadataOutput)
                metadataOutput.setMetadataObjectsDelegate(objectsDelegate = object : NSObject(),
                    AVCaptureMetadataOutputObjectsDelegateProtocol {
                    override fun captureOutput(
                        output: AVCaptureOutput,
                        didOutputMetadataObjects: List<*>,
                        fromConnection: AVCaptureConnection
                    ) {
                        didOutputMetadataObjects.firstOrNull()?.let { metadataObject ->
                            val readableObject =
                                metadataObject as? AVMetadataMachineReadableCodeObject
                            if (readableObject?.type == AVMetadataObjectTypeQRCode) {
                                val code = readableObject?.stringValue ?: ""
                                if (onQrCodeScanned.invoke(code)) {
                                    AudioServicesPlaySystemSound(kSystemSoundID_Vibrate)
                                    captureSession.stopRunning()
                                }
                            }
                        }
                    }
                }, queue = dispatch_get_main_queue())

                metadataOutput.metadataObjectTypes = listOf(AVMetadataObjectTypeQRCode)
                /*
                Commenting this line as this provides support for many ISO Image formats.
                Later if needed to extend QR scanner to support other formats comment this line and uncomment the above line.
                 */
//                metadataOutput.metadataObjectTypes = metadataOutput.availableMetadataObjectTypes()
            }
        }
    }
    val cameraPreviewLayer = remember {
        AVCaptureVideoPreviewLayer(session = captureSession)
    }

    DisposableEffect(Unit) {
        class OrientationListener : NSObject() {
            @Suppress("UNUSED_PARAMETER")
            @ObjCAction
            fun orientationDidChange(arg: NSNotification) {
                val cameraConnection = cameraPreviewLayer.connection
                if (cameraConnection != null) {
                    actualOrientation = when (UIDevice.currentDevice.orientation) {
                        UIDeviceOrientation.UIDeviceOrientationPortrait ->
                            AVCaptureVideoOrientationPortrait

                        UIDeviceOrientation.UIDeviceOrientationLandscapeLeft ->
                            AVCaptureVideoOrientationLandscapeRight

                        UIDeviceOrientation.UIDeviceOrientationLandscapeRight ->
                            AVCaptureVideoOrientationLandscapeLeft

                        UIDeviceOrientation.UIDeviceOrientationPortraitUpsideDown ->
                            AVCaptureVideoOrientationPortrait

                        else -> cameraConnection.videoOrientation
                    }
                    cameraConnection.videoOrientation = actualOrientation
                }
                capturePhotoOutput.connectionWithMediaType(AVMediaTypeVideo)
                    ?.videoOrientation = actualOrientation
            }
        }

        val listener = OrientationListener()
        val notificationName = UIDeviceOrientationDidChangeNotification
        NSNotificationCenter.defaultCenter.addObserver(
            observer = listener,
            selector = NSSelectorFromString(
                OrientationListener::orientationDidChange.name + ":"
            ),
            name = notificationName,
            `object` = null
        )
        onDispose {
            NSNotificationCenter.defaultCenter.removeObserver(
                observer = listener,
                name = notificationName,
                `object` = null
            )
        }
    }

    Box(modifier = Modifier.fillMaxSize()) {
        UIKitView(
            modifier = Modifier.fillMaxSize(),
            factory = {
                val cameraContainer = UIView()
                cameraContainer.layer.addSublayer(cameraPreviewLayer)
                cameraPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
                captureSession.startRunning()
                cameraContainer
            },
            onResize = { view: UIView, rect: CValue<CGRect> ->
                CATransaction.begin()
                CATransaction.setValue(true, kCATransactionDisableActions)
                view.layer.setFrame(rect)
                cameraPreviewLayer.setFrame(rect)
                CATransaction.commit()
            },
        )
    }
}
t
Having the exact same problem. Sometimes I am able to scan 2 or 3 codes and then it stops for some reason. No exception or anything.
@Sreeramktm 27 Have you been able to solve this in the meantime?
s
@Tobias I was not able to solve this issue. I took the native approach.
t
@Sreeramktm 27 How does that look like if I may ask? I mean, if you implemented the logic from above in Swift and put it into the iOS-project, how can you call it from the common Kotlin code?
s
I split the workflow into two, the QR part was handled in the application whereas the processing of QR data happens in the Kotlin Multi-platform code.
t
@Sreeramktm 27 Can you recommend some documentation or a tutorial on how to do that? What I don’t get is “QR part was handled in the application”, because first you come from iOS / Swift and call Common / Kotlin and then it has to go “back” to iOS / Swift to handle the QR part, then back to common / Kotlin for the QR processing.
s
Sorry for the delay @Tobias , I think I may have explained it wrong. I use Kotlin Multi-platform as a dependency in my iOS standalone application. The app scans the QR in the native way and sends the data to the framework.
👍 1
h
hello, having the same problem, is there any chance that you found the cause?
just found solution, based on this
👍 1
283 Views