Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ Full module documentation: [hexdocs.pm/mob](https://hexdocs.pm/mob).

---

## [Unreleased]

### Fixed
- **Cold-start launch notifications are no longer dropped (Android + iOS).**
`mob_set_launch_notification` (Android) and `mob_set_launch_notification_json`
(iOS) bailed out when called before `nif_load` had created their mutex — which
is exactly when the cold-start path runs (MainActivity.onCreate / the app
delegate store the tapped notification before the BEAM boots). The payload
was silently discarded, so tap-to-open from a killed app never worked; a
warm/backgrounded tap (delivered via `onNewIntent` / the running delegate)
was unaffected. Both setters now store before the mutex exists, the same
pre-mutex pattern `mob_set_opened_document` has always used — safe because
nothing reads the global until `take_launch_notification`, which can only
run post-`nif_load`.

## [0.7.20] - 2026-07-11

### Changed
Expand Down
15 changes: 11 additions & 4 deletions android/jni/mob_nif.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2238,17 +2238,24 @@ export fn nif_share_text(
// ── Launch notification (written from Kotlin on cold start) ──────────────
// MobBridge.setLaunchNotification(json) → mob_set_launch_notification(json).
// Apps call Mob.Device.take_launch_notification/0 → nif_take_launch_notification
// to consume it. Guarded by g_launch_notif_mutex (lazily created in nif_load).
// to consume it. Guarded by g_launch_notif_mutex once nif_load created it;
// stores before that are unguarded on purpose (see below).

var g_launch_notif_json: ?[*:0]u8 = null;
var g_launch_notif_mutex: ?*erts.ErlNifMutex = null;

pub export fn mob_set_launch_notification(json: ?[*:0]const u8) callconv(.c) void {
const mutex = g_launch_notif_mutex orelse return;
erts.enif_mutex_lock(mutex);
defer erts.enif_mutex_unlock(mutex);
// Store even before nif_load created the mutex: on a cold start from a
// notification tap, MainActivity.onCreate calls this before the BEAM
// thread starts, and nothing reads the global until take_launch_notification
// (post-nif_load), so there's no concurrent access in that window. The
// previous `orelse return` silently dropped exactly that cold-start
// payload — tap-to-open from a killed app never worked. Same pattern as
// mob_set_opened_document below.
if (g_launch_notif_mutex) |mutex| erts.enif_mutex_lock(mutex);
if (g_launch_notif_json) |old| jni.free(@as(?*anyopaque, @ptrCast(old)));
g_launch_notif_json = if (json) |j| jni.strdup(j) else null;
if (g_launch_notif_mutex) |mutex| erts.enif_mutex_unlock(mutex);
}

export fn nif_take_launch_notification(
Expand Down
14 changes: 10 additions & 4 deletions ios/mob_nif.m
Original file line number Diff line number Diff line change
Expand Up @@ -2383,12 +2383,18 @@ void mob_send_push_token(const char *hex_token) {
}

void mob_set_launch_notification_json(const char *json) {
if (!g_launch_notif_mutex)
return;
enif_mutex_lock(g_launch_notif_mutex);
// Store even before nif_load created the mutex: on a cold start from a
// notification tap, the app delegate calls this before the BEAM starts,
// and nothing reads the global until take_launch_notification
// (post-nif_load), so there's no concurrent access in that window. The
// previous early return silently dropped exactly that cold-start payload
// — tap-to-open from a killed app never worked.
if (g_launch_notif_mutex)
enif_mutex_lock(g_launch_notif_mutex);
free(g_launch_notification_json);
g_launch_notification_json = json ? strdup(json) : NULL;
enif_mutex_unlock(g_launch_notif_mutex);
if (g_launch_notif_mutex)
enif_mutex_unlock(g_launch_notif_mutex);
}

static ERL_NIF_TERM nif_take_launch_notification(ErlNifEnv *env, int argc,
Expand Down