gruhn / vue-qrcode-reader

A set of Vue.js components for detecting and decoding QR codes.
https://gruhn.github.io/vue-qrcode-reader
MIT License
2.03k stars 330 forks source link

Camera not working in PWA after app returns from background in iOS #298

Closed tristansb1 closed 4 months ago

tristansb1 commented 2 years ago

Describe the bug The camera stops working after the PWA returns form background on my 12 mini. The camera also doesn't work at all on an iPhone 13 (see videos below). App works perfectly fine in Safari. PWA also works fine on Android devices.

To Reproduce Implement this Demo as PWA: https://gruhn.github.io/vue-qrcode-reader/demos/DecodeAll.html#decode-continuously

<template>
  <div class="scanner">
    <p class="error">{{ error }}</p>

    <p class="decode-result">Last result: <b>{{ error }}</b></p>

    <qrcode-stream @decode="onDecode" @init="onInit" />
  </div>
</template>

<script>
import { QrcodeStream } from 'vue-qrcode-reader'
export default {

  components: { QrcodeStream },

  data () {
    return {
      result: '',
      error: ''
    }
  },

  methods: {
    onDecode (result) {
      this.result = result
    },

    async onInit (promise) {
      try {
        await promise
      } catch (error) {
        if (error.name === 'NotAllowedError') {
          this.error = "ERROR: you need to grant camera access permission"
        } else if (error.name === 'NotFoundError') {
          this.error = "ERROR: no camera on this device"
        } else if (error.name === 'NotSupportedError') {
          this.error = "ERROR: secure context required (HTTPS, localhost)"
        } else if (error.name === 'NotReadableError') {
          this.error = "ERROR: is the camera already in use?"
        } else if (error.name === 'OverconstrainedError') {
          this.error = "ERROR: installed cameras are not suitable"
        } else if (error.name === 'StreamApiNotSupportedError') {
          this.error = "ERROR: Stream API is not supported in this browser"
        } else if (error.name === 'InsecureContextError') {
          this.error = 'ERROR: Camera access is only permitted in secure context. Use HTTPS or localhost rather than HTTP.';
        } else {
          this.error = `ERROR: Camera error (${error.name})`;
        }
      }
    }
  }
}
</script>

<style scoped>
.error {
  font-weight: bold;
  color: red;
}
.scanner {
  position: absolute;
  width: 100%;
  height: 100%;
  overflow: hidden;
  left: 0;
  top: 0;
}
</style>

Screenshots iPhone 12 mini behaviour

https://user-images.githubusercontent.com/69447840/165713527-b694d926-dbfb-4d4b-897a-17391807501d.mp4

iPhone 13 behaviour

https://user-images.githubusercontent.com/69447840/165720558-2ae3086e-e5f2-4d4c-a3d1-d5ced9502b71.mp4

Smartphone:

kikan commented 2 years ago

It seems there's a dependency to https://cdn.jsdelivr.net/npm/jsqr@1.3.1/dist/jsQR.min.js So, when the device if offline, the dependency can't be reached and the QRCode recognition doesn't work. I think you need to copy jsQR.min.js somewhere and manage to have it fetched instead of the cdn.

0tsu0i0ihajime commented 1 year ago

I am using PWA in the same way, but when I use it in the background on iOS, I can't re-use the camera and have no choice but to close it and start over again. I also used jsQR.min.js but that didn't change it. Why is this?

I have this I am incorporating this into the html

SebastianMueller87 commented 1 year ago

It seems there's a dependency to https://cdn.jsdelivr.net/npm/jsqr@1.3.1/dist/jsQR.min.js So, when the device if offline, the dependency can't be reached and the QRCode recognition doesn't work. I think you need to copy jsQR.min.js somewhere and manage to have it fetched instead of the cdn.

I doubt that it is the reason. The devices are online the whole time. It just occurs when moving the app into background and back forward. It also behaves differently on different iOS devices (with same iOS-Version), so i would exclude the internet connection and therefore also the cdn as the reason for this.

@0tsu0i0ihajime the error is still not resolved and also the reason has not be found yet.

0tsu0i0ihajime commented 1 year ago

If I do this on a non-cellular model iPad in an environment with Wi-Fi turned off, the camera will stop and not come back on when I move it to the background and then undo it when using PWA. I was wondering, is it possible that Safari is designed to automatically stop the camera when offline?

mrrrk commented 1 year ago

On an iPhone 12 I was testing on, the PWA app would get in a state where the video element of the reader would refuse to start at all after going into the background and coming back. My eventual workaround had two parts:

In mounted() of the component showing the qr-reader, find the video element of the qr-reader (using getElementsByTagName or whatever), subscribe to the ended event of the video - which seems to fire when the badness happens. Use $router.back() to close the component whenever this fires (this may or may not be appropriate depending on your implementation of the reader!)

Additionally, in the beforeDestroy hook (Vue 2) of the component with the reader, find the video element and do this:

videoElement.pause();
videoElement.src = "";
videoElement.removeAttribute("src"); // can you smell the desperation?
videoElement.load(); // from https://stackoverflow.com/questions/3258587/how-to-properly-unload-destroy-a-video-element
videoElement?.parentNode?.removeChild(this.videoElement);

I was starting to wonder about brushing off my Swift skills, such as they are, and going native at about this point (which presumably is exactly what Apple wants me to do!)

Jannis033 commented 1 year ago

I have the same problem, using iOS 16.4 on an iPhone 13. After reopening the app from the background, the camera remains black. The red camera indicator appears in the top left corner and then immediately disappears again. navigator.mediaDevices.getUserMedia() returns successfully… Even closing the app in the app switcher does not work. Neither does closing all apps, including Safari and all other PWAs (maybe leaving an instance of safari running leads to the issue). Sometimes it fixes itself after a couple minutes. Restarting my iPhone always fixes the issue, but that’s not how it should be… Btw. iPadOS 16.4 also has the issue. Did you come to a solution?

github-actions[bot] commented 11 months ago

This issue has been marked as stale. If there is no further activity it will be closed.

mattwhitchurch commented 11 months ago

Posting to keep the issue open - same issue on iPhone 14. Same issue exactly as Jannis033.

gruhn commented 11 months ago

I deployed a PWA installable version of the demo page (without fix) but I can't reproduce the issue on iOS 16.

https://deploy-preview-351--vue-qrcode-reader.netlify.app/

Can someone confirm whether this is reproducible on the demo page.

mattwhitchurch commented 11 months ago

Just tried on iPhone 14 with iOS 16.5.1 and worked initially. However when I closed the app and reopened it, it did not work - as per the issue.

For information it worked fine on Pixel 6 / Chrome.

gruhn commented 11 months ago

Ah I see, I can reproduce it too now. You really have to close the app. I thought putting it into background already has this effect.

Also thanks @mrrrk. The hints are very helpful. Sorry for the long wait.

Looks like the issue corresponds to this Webktit bug. In the thread they are saying a fix has been merged literally last week. But I guess that will only benefit (far?) future iOS versions.

gruhn commented 11 months ago

Ok, so until someone finds a workaround, I implemented what they suggested in the WebKit issue thread: throw an error when the problem occurs, so at least the application can respond to it. You should be able to do something like this in v5.1.0:

<qrcode-stream @error="onError" />
const onError = event => {
  if (event.name === "StreamLoadTimeoutError") {
    // handle error
  }
}

Also, check out the corresponding demo.

holgerschillack commented 11 months ago

So I added the onError method to show a toast, that the camera initialisation was not successful.

But is there a workaround to restart the camera initialisation or reset it somehow?

I already tried a combination of different approaches, but nothing seems to work. If the user closes the PWA once (with swiping it up from the background app panel), the App is broken and needs to be re-installed.

This is my current try of a workaround:

onError() {
    this.$toast.error('Error opening camera!');
    this.antiBugMethod();
    this.reloadCameraFeed();
    this.requestCameraAccess();
    this.reloadCameraFeed();
    setTimeout(() => {}, 2500);
},
async requestCameraAccess() {
    await navigator.mediaDevices.getUserMedia({ video: true });
},
antiBugMethod() {
    const videoElement = document.querySelector('video.qrcode-stream-camera');
    if (videoElement) {
        videoElement.pause();
        videoElement.src = '';
        videoElement.removeAttribute('src');
        videoElement.load();
        videoElement.parentNode?.removeChild(videoElement);
    }
},
reloadCameraFeed() {
    this.paused = true;
    setTimeout(() => {
        this.paused = false;
        console.log('Restarted camera feed.');
    }, 200);
}
gruhn commented 11 months ago

I also tried a lot of things but nothing worked unfortunately. In the webkit issue thread I linked above, also nobody had a workaround.

Jannis033 commented 10 months ago

In IOS 17 Beta Public Beta 2 the issue seems to be resolved. I have tried it multiple times with several PWAs and could not reproduce the issue. In IOS 17 Beta 1 the issue was still present for me. The only thing I noticed was that the camera image is small for some milliseconds and then gets the full size. Only on first load of the PWA. This is since Beta 1... // edit: I just got the issue again but I stressed the PWA with closing and reopening very often.

Robcodeandcopy commented 10 months ago

I also have this issue, so do we need to wait for IOS to update to IOS 17 before this is fixed?

Jannis033 commented 10 months ago

I also have this issue, so do we need to wait for IOS to update to IOS 17 before this is fixed?

Yes, I doubt so. But iOS 17 will be released in a few weeks already and if you cannot wait, there are some pretty stable public betas available.

brianoflondon commented 9 months ago

Thank you for being friendly with the error messages 😀 (once I made sure to add the code to look at them properly. I'll wait for iOS 17 to hopefully fix.

IMG_1808

geri777 commented 8 months ago

Since today the scanner exits with the StreamLoadTimeoutError everytime - also when newly started and not activating from the background. I have not changed anything - neither on the web nor on my phone. Same issue on 3 client's phones. The PWA askes for camera permission every time.

I then updated from 16.4.1 to 16.4.2 - when I started the PWA again for the first time on the new system, the camera access worked. But this was the only time it worked - later tests did not work any more. When switching off/on the device it works for one single time again.

I also found out that the PWA camera access works when the camera app has just been put to background.

geri777 commented 8 months ago

I have tested my PWA on iOS 17.2 and everything works. I hope it will be live soon.

github-actions[bot] commented 6 months ago

This issue has been marked as stale. If there is no further activity it will be closed.

github-actions[bot] commented 4 months ago

This issue has been marked as stale. If there is no further activity it will be closed.