Search code examples
iosreact-nativebackground-processavaudiorecorderexpo-av

Why is my react-native app terminating in the background while recording (iOS, RN 0.63.3, Expo-Av 9.2.3)


Hello

I'm having an issue in my React Native app where audio recordings are sometimes mysteriously terminated while the app is in the background.

This happens only in ~1/10 user sessions after variable amounts of time anywhere from 5 to 50 minutes into the recording.

Additional context is below.

I've been banging my head on this for weeks without progress. Any thoughts on what could be causing this issue or where I should further investigate would be greatly appreciated.

Example Scenario

  1. Release-mode user begins an audio recording in our app, navigating them to the "recording in progress" screen

  2. User returns to the iPhone home screen and locks their phone.

  3. Five minutes later, the user turns on the screen to confirm that the orange active recording indicator is still visible in the status bar.

  4. Another five minutes go by. The user turns on the screen to see that the orange active recording indicator is no longer visible (but this only happens roughly 1 in 10 times)

  5. User unlocks their phone and returns to our app. They are now back to the home screen instead of the active recording screen.

Prior Investigation:

  • Bugsnag has no uncaught errors (nor helpful clues) for this user during this session. This includes no reports of OOM errors.
  • Neither Crashlytics nor Xcode Organizer crash logs reveal any crashes during the time window of the mysterious termination.
  • None of the user's Jetsam event logs for the day mention our app.

Info about how recording works in our app

  • We record audio and upload it to our server in ~1m chunks.
  • Audio chunks are saved to the device before uploading is attempted and deleted upon successful upload.
  • We use ffmpeg to extract 1m chunks of audio without stopping our recording session to avoid being suspended in background.

Additional User Context

  • Problem occurs on both iPad and iPhone. Unknown if it occurs on android.
  • Problem occurs on both old and new devices, from iPhone 7 to iPhone 14.
  • Users anecdotally state that the problem occurs mostly in low-connectivity environments

Environment

expo-env-info 1.0.2 environment info:
System:
OS: macOS 12.3
Shell: 5.8 - /bin/zsh
Binaries:
Node: 15.13.0 - ~/.nvm/versions/node/v15.13.0/bin/node
npm: 7.7.6 - ~/.nvm/versions/node/v15.13.0/bin/npm
Watchman: 2022.03.21.00 - /opt/homebrew/bin/watchman
Managers:
CocoaPods: 1.11.2 - /usr/local/bin/pod
SDKs:
iOS SDK:
Platforms: DriverKit 21.4, iOS 15.4, macOS 12.3, tvOS 15.4, watchOS 8.5
Android SDK:
API Levels: 23, 28, 29, 30, 31
Build Tools: 29.0.2, 30.0.2, 31.0.0
System Images: android-29 | Google Play ARM 64 v8a, android-30 | Google APIs Intel x86 Atom
IDEs:
Android Studio: 2020.3 AI-203.7717.56.2031.7935034
Xcode: 13.3/13E113 - /usr/bin/xcodebuild
npmPackages:
react: 16.13.1 => 16.13.1
react-native: 0.63.3 => 0.63.3
react-native-web: ^0.16.3 => 0.16.5
npmGlobalPackages:
expo-cli: 4.3.4
Expo Workflow: bare

Solution

  • Root causes and solutions

    1) Mixpanel (love them!) iOS SDK had a bug, occasionally causing background terminations after 30 seconds.

    For context, Mixpanel uses a background task to flush event updates to server every time app enters background. Additionally, iOS automatically terminates apps when a background task has not finished after 30 seconds.

    For some reason, these Mixpanel event flushes were sometimes exceeding 30 seconds causing app terminations.

    2) Background Out of Memory terminations.

    Apple gives very few hints when apps are terminated. Using Embrace.io (awesome company!), I was able to begin measuring occurrences of out-of-memory terminations from the background (BOOMs).

    Profiling with react native tools, I was able to identify that even in the background, app re-renders and redux actions in response to Firestore listeners were causing significant CPU usage. Terminating unnecessary listeners and unmounting most screens when entering the background allowed us to significantly decrease background CPU usage, improving battery efficiency and virtually eliminating BOOMs

    Happy to elaborate more if anyone has specific questions. Spent a looooot of time on this problem.