ngx-scanner icon indicating copy to clipboard operation
ngx-scanner copied to clipboard

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

Open Jalil-Irfan opened this issue 4 years ago • 1 comments

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">

    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

  • Device: iPad 8th Gen 14.6inch
  • OS: [iOS 14.6]
  • Browser [WKWebview]

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

Jalil-Irfan avatar Jul 17 '21 20:07 Jalil-Irfan

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

seanrallSRS avatar Feb 08 '22 22:02 seanrallSRS