6

I'm building a QR code reader into my app, so far I have it open as a sheet and close when a qr/barcode is detected. The reader part of the app uses UIKit, I have the file QRCodeScan.swift which is the UIViewControllerRepresentable, the QR scanner returns the value of the code that it has found into the coordinator in this file. I can't seem to find any way to get the found code out of the coordinator into the original view.

This is the file QRCodeScan.

struct QRCodeScan: UIViewControllerRepresentable {
    
    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }
    
    func makeUIViewController(context: Context) -> ScannerViewController {
        let vc = ScannerViewController()
        vc.delegate = context.coordinator
        return vc
    }
    
    func updateUIViewController(_ vc: ScannerViewController, context: Context) {
    }

    class Coordinator: NSObject, QRCodeScannerDelegate {
        @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
        
        func codeDidFind(_ foundCode: String) {
            print(foundCode)
            /*this is where the code comes to, need to return it from here */
            presentationMode.wrappedValue.dismiss()
        }
        
        var parent: QRCodeScan
        
        init(_ parent: QRCodeScan) {
            self.parent = parent
        }

    }
}

This is a cropped down version of the ContentView that calls the qr reader , this is where I need to get the found code back into

struct ContentView: View {
    
    @State var presentQRScanner = false
    
    var body: some View {
        NavigationView{
            Form{
                Section(header: Text("Info")){
                    Button("Scan Barcode"){
                        self.presentQRScanner = true
                    }
                        .sheet(isPresented: $presentQRScanner){QRCodeScan()}
                }
                
            }
            
            .navigationBarTitle(Text("New"), displayMode: .large)
            .navigationBarItems(trailing: Button("Save"){
                print("Button Pressed")
            })
        }
    }
}

I've hit a total roadblock here, I can't find any resources that allow me to pass the data back from the coordinator, maybe I'm implementing something wrong but I can't seem to adapt any other solutions to fit

Any help is much appreciated.

Thanks

Salvatore
  • 10,815
  • 4
  • 31
  • 69
robbo5899
  • 99
  • 9

2 Answers2

4

You may have already solved this, but the solution is to use an @State variable in your ContentView linked to an @Binding variable in your QRCodeScan Struct and the Coordinator Class.

Check out this answer: Accessing MKMapView elements as UIViewRepresentable in the main (ContentView) SwiftUI view

Something like this should do the trick, but I suggest reading over the more detailed answer I linked:

 struct QRCodeScan: UIViewControllerRepresentable {

    @Binding var code: String

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

func makeUIViewController(context: Context) -> ScannerViewController {
    let vc = ScannerViewController()
    vc.delegate = context.coordinator
    return vc
}

func updateUIViewController(_ vc: ScannerViewController, context: Context) {
}

class Coordinator: NSObject, QRCodeScannerDelegate {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>

   @Binding var code: String

   init(code: Binding<String>) {
   _code = code
   }

    func codeDidFind(_ foundCode: String) {
        print(foundCode)
        /*this is where the code comes to, need to return it from here */
        self.code = foundCode
        presentationMode.wrappedValue.dismiss()
    }

    var parent: QRCodeScan

    init(_ parent: QRCodeScan) {
        self.parent = parent
    }
}
}

In your ContentView, create @State var code, then you can call QRCodeScan(code: $code).

lmh
  • 293
  • 1
  • 4
  • 13
0

I already answered this for you in this answer: SwiftUI go back programmatically from representable to View.

Alternatively, you can drive this from the presenter too by creating a closure on the QRCodeScan that gets invoked with the code and you have your presenter dismiss.

EDITS: I have inserted my suggestions into your code to show you who owns what and how to pass information around.

struct QRCodeScan: UIViewControllerRepresentable {

    /// a closure called with the found code
    var onCodeScanned: (String) -> Void

    class Coordinator: NSObject, QRCodeScannerDelegate {

        func codeDidFind(_ foundCode: String) {
            // call the parent's `onCodeScanned` closure here
            parent.onCodeScanned(foundCode)

            presentationMode.wrappedValue.dismiss()
        }

        var parent: QRCodeScan

        // init, makeViewController, etc.
    }
}

and in the presenter:

struct ContentView: View {

    @State var presentQRScanner = false

    var body: some View {
        NavigationView{
            Form{
                Section(header: Text("Info")){
                    Button("Scan Barcode"){
                        self.presentQRScanner = true
                    }
                    .sheet(isPresented: $presentQRScanner){
                        // when you create the VC representable, pass in your closure to handle found codes
                        QRCodeScan(onCodeScanned: {
                            self.processFoundCode($0)
                        })
                    }
                }

            }
            // .theRest()
        }
    }

    func processFoundCode(_ code: String) {
        // process it

        // dimiss sheet
        presentQRScanned = false
    }
}

In other words, your closure will pass that data up the chain to your ContentView.

Procrastin8
  • 4,193
  • 12
  • 25
  • Yeah thanks for the help, I didn't downvote anything, I don't have enough SO points for my upvote to show but defo not downvoted. Your solution doesn't actually help solve the problem of getting the 'code' out of the coordinator, regardless of where i place the 'var onCodeScanned...' line its either not accessible from the coordinator or not an acceptable implementation for the presenter – robbo5899 Oct 17 '19 at 18:42