zxing-js / ngx-scanner

Angular QR code, Barcode, DataMatrix, scanner component using ZXing.
https://zxing-js.github.io/ngx-scanner/
MIT License
644 stars 228 forks source link

@zxing/ngx-scanner – "Can't get user media, this is not supported." in WKWebview SwiftUI iOS devices #422

Open Jalil-Irfan opened 3 years ago

Jalil-Irfan commented 3 years ago

Describe the bug Zxing-scanner Fails to open up in iPhone and Mac inside WKWebView. But works well in Safari browser opening it as an individual website.

To Reproduce Create a native SwiftUI webview

`

import AVFoundation
import SwiftUI
import WebKit

struct WebView : UIViewRepresentable {
    @Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
    @AppStorage("loggedinSession") var sessionId = ""

let request: URLRequest
//var webView: WKWebView?
@Binding var resetView:Bool
@Binding var isLoggedIn: Bool
@Binding var isRegistering: Bool

// This has to be inside the representable structure
class Coodinator: NSObject, WKUIDelegate, WKNavigationDelegate,UIScrollViewDelegate {
    var parent: WebView

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

    func scrollViewDidScroll(_ scrollView: UIScrollView) {
        if (scrollView.contentOffset.x > 0){
            scrollView.contentOffset = CGPoint(x: 0, y: scrollView.contentOffset.y)
        }
    }
    // MARK: - Navigation Delegate

    func webView(_ webView: WKWebView,
                 decidePolicyFor navigationAction: WKNavigationAction,
                 decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        let webURL = webView.url?.absoluteString
        print("webURL:", webURL!)

        // Check for logout on the URL
        if (webURL!.hasSuffix("logout.jsp")) {
            print("force logout and close the window")
            // webView.removeFromSuperview()
            self.parent.isLoggedIn = false
            self.parent.isRegistering = false
            self.parent.sessionId = ""
            self.parent.presentationMode.wrappedValue.dismiss()
        }

        let appHost = navigationAction.request.url?.host
        print("appHost:", appHost as Any)
        decisionHandler(.allow)
    }
}

func makeCoordinator() -> Coodinator {
    return Coodinator(self)
}

func makeUIView(context: Context) -> WKWebView  {
   let webview = WKWebView()
    webview.uiDelegate = context.coordinator
    webview.navigationDelegate = context.coordinator
    webview.scrollView.delegate = context.coordinator
    return webview
}

func updateUIView(_ uiView: WKWebView, context: Context) {
    uiView.load(request)
}
}

` Then direct the webview to the Angular project page where we have the Zing-scanner

` HTML :- <zxing-scanner

scanner

    [torch]="torchEnabled"
    (scanSuccess)="onScanSuccess($event)"
    [autofocusEnabled]="true"
    [tryHarder]="tryHarder"
    (permissionResponse)="onHasPermission($event)"
    (camerasFound)="onCamerasFound($event)"
    (torchCompatible)="onTorchCompatible($event)"
    [formats]="['QR_CODE', 'EAN_13']"
    previewFitMode="cover">
    </zxing-scanner> 

    Angular ts:-
     clearResult(): void {
     this.qrResultString = null;
    }

    onCamerasFound(devices: MediaDeviceInfo[]): void {
    this.hasDevices = Boolean(devices && devices.length);
    this.scannerOpen = true;
   }

    onScanFailure($event){
      console.log('inside ScanFailure');
      console.log($event);
      this.showScanError = true;
    }

    onScanSuccess(resultString: string) {
    this.showProductError = false;
    console.log('inside scanSuccess '+resultString);
  }
    onHasPermission(has: boolean) {
       this.hasPermission = has;
   }

    showPermissionDialog(): void{
    const dialogRef = this.dialog.open(DialogPopupComponent, {
      width: '325px',
      data: {forModule : 'scanCameraPermission'}
     });

     dialogRef.afterClosed().subscribe(result => {
      localStorage.setItem('modalShownOnce', 'shown');
      this.popupShown = true;
    });
  }

   ngAfterViewInit() {
    this.delayAndTryHarder();
   }

   async delayAndTryHarder() {
      await this.delay(1000);
     this.toggleTryHarder();
   }

    delay(ms: number) {
     return new Promise( resolve => setTimeout(resolve, ms) );
   }

     toggleTryHarder(): void {
        this.tryHarder = !this.tryHarder;
     }

     onTorchCompatible(isCompatible: boolean): void {
        this.torchAvailable$.next(isCompatible || false);
     }

    toggleTorch(): void {
     this.torchEnabled = !this.torchEnabled;
    }

`

Expected behavior INside webview on clicking Allow for the camera permission we should be able to see the video and do the scanning without any issue in Webview just like any web-browser, since iOS ,webview, safari, all supports WebRTC this shouldnt be an issue

Screenshots Screenshot 2021-07-18 at 12 43 35 AM

iPad

Additional context When its working on all the browsers normally , and when the Zxing even requests for the camera permission , then we shouldnt be facing issues in webview

seanrallSRS commented 2 years ago

I too have this exact issue, @Jalil-Irfan were you able to find a solution?