Background execution keep-alive for Mob apps — keep the BEAM node running when the screen locks or the app is backgrounded.

  • iOS: a silent AVAudioEngine session (MixWithOthers) so the OS keeps the app alive. The user hears nothing; any music already playing is undisturbed.
  • Android: a dataSync foreground service with a low-priority persistent notification. The OS won't kill it under memory pressure or when the screen locks.

Usage

# Keep the app alive when the screen locks (e.g. in mount/2):
MobBackground.keep_alive()

# Allow suspension again when no longer needed:
MobBackground.stop()

keep_alive/0 is idempotent — safe to call multiple times. status/0 reports, without changing anything, whether keep-alive can work in this host and whether it's on.

Run mix mob.selftest from a host app to check on a device that the native side is linked and wired up (MobBackground.SelfTest, a read-only background_status/0 call that never starts the keep-alive).

Install

# mix.exs
{:mob_background, "~> 0.1"}

# mob.exs
config :mob, :plugins, [:mob_background]
config :mob, :trusted_plugins, %{mob_background: "ed25519:<fingerprint>"}

mix mob.plugin.trust mob_background records the fingerprint, then mix mob.deploy --native.

Host requirements (the native build warns about these)

On Android the build adds everything when the plugin is activated: the FOREGROUND_SERVICE permissions, the BeamForegroundService class (copied into io/mob/background/ next to the bridge) and its <service android:foregroundServiceType="dataSync"> declaration. If you copied BeamForegroundService.kt into your app for plugin 0.1.x somewhere other than io/mob/background/, delete that copy.

The plugin deliberately doesn't add this one to your Info.plist (Apple rejects the mode in apps with no audio feature, so it's the app author's call); it prints as a warning on every mix mob.deploy --native of the host:

  • iOS UIBackgroundModes. Info.plist must declare the audio mode (the keep-alive uses a silent audio session). mix mob.new adds this; for Xcode projects use Signing & Capabilities → Background Modes → Audio, AirPlay, and Picture in Picture. Apple rejects this mode for apps with no audio feature — only use this plugin in apps that legitimately use audio.

Notes

  • Coexistence with Mob.Audio: playback mixes transparently (both use MixWithOthers); recording temporarily takes the audio session, and the keep-alive engine restarts automatically when recording (or a phone call) ends.
  • Android notification: Android requires every foreground service to post a visible notification ("Running in background", IMPORTANCE_LOW, no sound). There is no API to hide it.
  • Android 15 time limit: with targetSdk 35+, Android allows a dataSync foreground service at most 6 hours per 24 hours while the app is in the background. When the limit is hit the service stops itself (logged under the MobBackground tag) and the notification disappears; the BEAM is not told. Call keep_alive/0 again once the app returns to the foreground.

License

MIT.