Open speaking-in-code opened 4 years ago
This issue was automatically closed because it did not follow the issue template.
Reopening. I'd like to understand this issue a bit more, though. I'm guessing this issue happens only if you try to set a timer while audio is not playing? A reproduction project would definitely help me to understand the exact issue.
I'll come up with a repro project. You'll need a real iOS device to see it happen, or I can send you logs.
If you set a timer while audio is playing, the timer might not fire on time. The app can be suspended even while audio plays in the background. My guess is that when the audio completes, the app will be unsuspended... at which point your timer will fire, after much longer than intended.
I'm not sure what happens if the app is killed while suspended. The docs say it can happen, which would be interesting... but the docs don't say anything about how to design your app so that it can recover gracefully after being killed. Maybe it's pretty rare to have the app killed outright.
I'm playing around with a workaround where for small delays (e.g. 10-20 seconds), I play a silent clip of audio, and chain the callback off of audio_service.play().
The silent audio clip is a workaround I've used sometimes on Android to keep the media session alive. If I understand correctly, the issue seems to relate to these small delays. In any case, I look forward to looking at your reproduction project to try to get to the bottom of this.
Reproduced in this example app: https://github.com/speaking-in-code/ios_audio_service.
To use the app:
If everything works, the cowbell dings every 20 seconds.
Instead, it dings after 20 seconds, and then pauses for much longer, until you unlock the device. The screen looks like this:
Possibly related: https://github.com/ryanheise/audio_session/issues/7
Agreed, these are closely connected. For my app, here is what I've settled on:
Which API doesn't behave as documented, and how does it misbehave?
The backgroundTaskEntrypoint parameter to AudioService.start() is documented as being a normal flutter isolate. It's a bit more complicated than that on iOS. Normal flutter isolate methods (e.g. Timer from dart:async) don't work reliably in the background isolate. When iOS suspends the app, the background isolate can be paused.
The issue only seems to happen on physical iOS devices which are not charging. It does happen even when the battery is fairly full, so it's not a low-power situation.
I think the issue occurs as follows:
Workarounds
The first work around I tried was adding the "background processing" capability to the app. This does not work.
The other strategies for background execution don't seem well-suited to the backgroundTaskEntrypoint.
Other flutter plugins could implement one of those strategies. Example: the background_fetch package.
Other ideas? Things I've missed?
It might make sense to simply document the limitations, I can send a pull request for the docs if that makes sense.
Minimal reproduction project
I'm feeling a bit lazy, haven't made a minimal example. Modifying the example app to call a method like this would do it:
The timer will fire every second, until the screen goes dark. Then iOS will suspend the app, and the timer fires after several seconds, or not at all.
Runtime Environment (please complete the following information if relevant):
Flutter SDK version