From e1bfa29ba1e38e1bfc73d1df52126b712dfbf923 Mon Sep 17 00:00:00 2001 From: dl-alexandre <166029845+dl-alexandre@users.noreply.github.com> Date: Wed, 20 May 2026 16:03:36 -0700 Subject: [PATCH] docs: document background execution model Address #18 (MOB as a 'background process'). Adds a 'Background execution' section to the README explaining that the BEAM is suspended when the app is backgrounded, and the push-driven model (APNs/FCM wake -> short handler window) for responding to server events. References the existing Mob.Notify.register_push and Mob.Device :app lifecycle events plus the mob_push package. --- README.md | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/README.md b/README.md index 1dd03e4..6079faf 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,35 @@ see the [Mobile Surface Matrix](https://hexdocs.pm/mob/mobile_surface_matrix.htm Set realistic expectations before starting an app; spot plugin candidates if you want to fill a gap. +## Background execution + +The BEAM runs on the device, but it does **not** keep running once the app is +backgrounded. iOS suspends the whole process within seconds — schedulers stop, +GenServers freeze, and any distribution / socket connections drop. Android does +the same unless you run a foreground service (the persistent-notification kind). +This is an OS constraint every mobile runtime lives with, not a Mob limitation. + +So a server can't push straight into a long-lived GenServer — the OS has to wake +you first, via APNs (iOS) or FCM (Android). The shape is: + +```elixir +# Register for a push token; your server stores it and sends through APNs/FCM. +# See the mob_push package for the server side. +Mob.Notify.register_push(socket) +def handle_info({:push_token, :ios, token}, socket), do: ... + +# React to the OS suspending / resuming the app. A push wakes the app, the BEAM +# resumes, your handler runs in a short window, then the OS suspends you again. +Mob.Device.subscribe([:app]) +def handle_info({:mob_device, :did_enter_background}, socket), do: ... +def handle_info({:mob_device, :will_enter_foreground}, socket), do: ... +``` + +`Mob.Device.foreground?/0` reports the current state. For true always-on (e.g. a +live connection held open), an Android foreground service is the only path; iOS +will not allow it. Otherwise treat the device as push-driven: server → APNs/FCM → +OS wakes app → BEAM handles the event → BEAM suspends again. + ## What's in the box The pre-built OTP runtime that ships with each app includes: