1
votes

Sorry for what seems like a simple question/answer, but I haven't been able to figure this out nor find a solution. I need to use AVFoundation for an app I'm writing. Basically scanning a barcode, and sending that information back to another ViewController. This was pretty easy/straight forward with Swift and UIKit and I had this working.

Now, I launch the ViewController using a sheet (passing in the @State variable so I can dismiss the sheet later):

.sheet(isPresented: $isShowingCamera, content: {
    ScanItem(isPresented: self.$isShowingCamera)
})

ScanItem is a UIViewRepresentable Here are the functions in ScanItem:

func makeCoordinator() -> ScanItem.Coordinator {
    return Coordinator(self)
}

public typealias UIViewControllerType = ScanBarcodeViewController

func makeUIViewController(context: UIViewControllerRepresentableContext<ScanItem>) -> ScanBarcodeViewController {
    return ScanBarcodeViewController()
}

func updateViewController(_ uiViewController: ScanBarcodeViewController, context: UIViewControllerRepresentableContext<ScanItem>) {
}

Inside I have the required methods and its displaying another UIViewController I created that uses AVFoundation to display the camera, and look for the barcode. Where I believe I need to progress is making the Coordinator handle the AVCaptureMetaData. I have the Coordinator like below:

class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
    let parent: ScanItem
    init(_ parent: ScanItem) {
        self.parent = parent
    }

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
    let metaDataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
    if metaDataObj.stringValue != nil {
        self.parent.$metadata = metadataObj.stringValue
        self.parent.$isPresented = false
    }
}

I think I'm on the right track here. This function gets called normally in the extension of the viewcontroller as AVCaptureMetadataOutputObjectsDelegate. I think I need to set the Coordinator as the delegate, call the function, set some bindable variable (@Bindable var metadata: String) and handle it in the SwiftUI view.

My current errors: ScanBarcodeViewController (my viewcontoller to load the camera) cannot be constructed because it has no accessible initializers Which goes along with class ScanBarcodeViewController has not initializers self.parent.$metaData = metaDataObj.stringValue -> cannot assign value of type String to type Binding -- fixed self.parent.$isPresented = false -> cannot assign value of type Bool to type Binding -- fixed

Thank you in advance. If you need me to post more code, I'd be happy to do so.

1

1 Answers

1
votes

Instead of passing self in Coordinator (which is struct, so copied), ie

Coordinator(self)

use binding to your model directly, so you can modify it, ie like

func makeCoordinator() -> Coordinator {
    return Coordinator(data: $metadata)
}

and in Coordinator..

final class Coordinator: NSObject, AVCaptureMetadataOutputObjectsDelegate {
    var data: Binding<String>

...

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) {
    let metaDataObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
    if metaDataObj.stringValue != nil {
        ...
        data.wrappedValue = metadataObj.stringValue
        ...
    }
}