feat(background): cross-platform background tasks (iOS + Android)#29
Open
A-Legg wants to merge 11 commits into
Open
feat(background): cross-platform background tasks (iOS + Android)#29A-Legg wants to merge 11 commits into
A-Legg wants to merge 11 commits into
Conversation
- Remove 'static' from mob_iap_send* helpers in mob_nif.m so Swift @_silgen_name exports resolve correctly at link time. - Fix ObjC selector generation: restorePurchases(_:) and currentEntitlements(_:) instead of restorePurchases(pidBytes:). - Correct Nif.ex moduledoc: 5 NIFs, not 6 (iap_verify_receipt is pure Elixir HTTP, not a NIF).
Native C bridges (iOS mob_nif.m + Android iap.c) were producing nested
2-tuples {{:iap, :products}, json} instead of the 3-tuple
{:iap, :products, json} that all Elixir handle_info clauses expect.
- mob_iap_send_products: enif_make_tuple2(tag, json) →
enif_make_tuple3(:iap, :products, json)
- mob_iap_send_transaction: enif_make_tuple2(inner, json) →
enif_make_tuple3(:iap, tag, json)
- Android sendToBeam: same 2→3 tuple fix
This makes the message format consistent between iOS, Android, and
the documented API contract in MobIap.StoreScreen etc.
- mob_erts.zig: add missing enif_alloc/enif_free/enif_get_list_length/ enif_get_list_cell declarations required by IAP NIF stubs. - mob_nif.zig IAP stubs: replace std.heap.c_allocator with erts.enif_alloc to avoid libc dependency in build-obj mode; fix buf size (256→4096); fix pointer/slice/alignment casts for Zig 0.16; remove pointless _ = env discard. - iap.c: replace free(p) with enif_free(p) so allocation/free pair both go through the BEAM allocator. These fix the Android native build so mob_iap_init is compiled and linked into lib<app>.so (verified on emulator).
The Zig NIF stubs allocate pid_ptr and product-id strings via erts.enif_alloc(), but the C JNI wrappers in iap.c were freeing them with libc free(). This allocator mismatch caused Scudo heap corruption on Android, crashing the BEAM during distribution or when IAP NIFs were called. Files changed: - iap.c: 10 calls to free() -> enif_free() in the JNI wrappers and iap_jni_build_string_list
The plugin code (Elixir + native bridges + tests + manifest) now lives in its own repository at https://github.com/A-Legg/mob_iap. This commit removes the embedded plugins/mob_iap/ directory from the mob repo. The runtime NIF stubs in src/mob_nif.erl and the Zig/ObjC wrappers in android/jni/ and ios/ remain in mob — those are the host runtime hooks that any IAP plugin calls via :mob_nif. Changes: - Delete plugins/mob_iap/ (moved to A-Legg/mob_iap) - Update guides/mob_dev_mob_new_iap_patches.md to reference the new repo
The plugin code (Elixir + native bridges + tests + manifest) now lives in its own repository at https://github.com/A-Legg/mob_iap. This commit removes the embedded plugins/mob_iap/ directory from the mob repo. The runtime NIF stubs in src/mob_nif.erl and the Zig/ObjC wrappers in android/jni/ and ios/ remain in mob — those are the host runtime hooks that any IAP plugin calls via :mob_nif. Changes: - Delete plugins/mob_iap/ (moved to A-Legg/mob_iap) - Update guides/mob_dev_mob_new_iap_patches.md to reference the new repo
- Remove stray empty EOF file accidentally created at repo root
(heredoc leak from local scripting)
- IAP NIFs: validate argc, return badarg on bad input
- nif_iap_fetch_products: badarg if iap_extract_product_ids returns 0
(was: continued with empty array, allocated pid, dispatched
no-op bridge call)
- all 5 IAP NIFs: explicit argc check
- Document pid_bytes lifetime contract above the mob_iap_send_* helpers:
one allocation, one send, one free. Storing pid_bytes after a send or
calling two helpers with the same pid_bytes is a use-after-free.
Also document why these symbols are intentionally non-static.
- Auto-format ios/mob_nif.m (pre-existing clang-format violations in
mob_iap_send_products / mob_iap_send_transaction)
- Delete guides/mob_dev_mob_new_iap_patches.md — it was a design spec
for the mob_new/mob_dev follow-up PRs, which are now in flight. The
doc disagrees with the merged direction in places (e.g. recommends
conditional BILLING permission; mob_new#9 chose unconditional).
Keeping it would mislead future readers.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…e 1) Implements the background task registry and completion bridge for iOS silent push and background fetch, per docs/designs/background_tasks.md. Changes: - ios/mob_nif.m: add mob_begin_background_task() C function and nif_background_task_complete/2 NIF with UUID-based completion handler registry (NSMutableDictionary + ErlNifMutex) - ios/mob_beam.h: declare mob_begin_background_task for AppDelegate - src/mob_nif.erl: add background_task_complete/2 export, -nif, stub - android/jni/mob_nif.zig: add no-op nif_background_task_complete for cross-platform API parity - lib/mob/background/task.ex: new module — complete(id, result) with :new_data | :no_data | :failed - test/mob/background/task_test.exs: unit + on-device integration tests Refs: docs/designs/background_tasks.md Phase 1
Adds ergonomic convenience API to Mob.Background.Task:
- — runs a function inside a supervised Task and
auto-calls the iOS completion handler with mapped result
- , , — shortcuts for the current task
- New NIF on iOS (reads g_current_bg_task_id)
and Android (no-op returning :none)
iOS: g_current_bg_task_id stores the most-recently-started task UUID
under the existing g_bg_tasks_mutex. mob_begin_background_task sets it.
nif_background_task_current reads it and returns {:ok, id} or :none.
Android: no-op — FCM data messages don't use completion handlers.
Tests: 8 unit tests (0 failures), 5 on-device integration tests.
All 805 tests pass. mix format + mix credo --strict clean.
Refs: docs/designs/background_tasks_phase3.md
- Export mob_begin_background_task from mob_nif.zig (creates
{:background_task, uuid, type, payload, deadline_us} tuple and
sends to device dispatcher).
- Declare mob_begin_background_task in android/jni/mob_beam.h.
- Add enif_monotonic_time and ErlNifTimeUnit constants to mob_erts.zig.
- Add design doc: docs/designs/background_tasks_phase2.md.
Author
Template PR
|
Author
Related PRs
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements Phase 1–3 of the cross-platform background task system for Mob:
application(_:didReceiveRemoteNotification:)andapplication(_:performFetchWithCompletionHandler:)→mob_begin_background_task.MobBackgroundWorkertriggered by FCM data messages (mob_background_taskkey) → JNImob_begin_background_task.Mob.Background.Task.run_and_complete/1,new_data/0,no_data/0,failed/0.Changes
docs/designs/background_tasks.md— Phase 1 design docdocs/designs/background_tasks_phase2.md— Phase 2 design docdocs/designs/background_tasks_phase3.md— Phase 3 design doclib/mob/background.ex,lib/mob/background/task.ex— Elixir public APIlib/mob/device.ex— iOS silent push / Android FCM device-side wiringsrc/mob_nif.erl— NIF exports (background_task_complete/2,background_task_current/0)ios/mob_nif.m—nif_background_task_complete/nif_background_task_currentwithg_current_bg_task_idandroid/jni/mob_nif.zig—mob_begin_background_taskexport + NIF stubsandroid/jni/mob_beam.h—mob_begin_background_taskdeclarationandroid/jni/mob_erts.zig—enif_monotonic_time/ErlNifTimeUnittest/mob/background_test.exs,test/mob/background/task_test.exsRelated PRs
mob_newtemplate changes: (see PR link below)