Releases: GenericJam/mob
Releases · GenericJam/mob
0.7.5
Fixed
- iOS canvas now delivers finger-drag (
on_drag) — at parity with Android.
The SwiftUIMobCanvasViewrendered draw ops but attached no drag recognizer,
so a canvas'son_draghandle (wired through the NIF tonode.onDrag) was
never invoked — continuous finger-drag was dead on iOS, while Android's
MobCanvashaddetectDragGestures. Added a canvas-scoped
DragGesture(minimumDistance: 0)that callsnode.onDragwith
began/dragging/ended phases; the gesture's local-space location is already in
canvas logical units (the frame is sized to the declared width/height), so no
rescale is needed. Verified on a physical iPhone (iOS 26.5): a finger-drawing
screen with a color picker and thickness control routes drags and renders
strokes correctly.
0.7.4
Fixed
- Tap-handle registry is now double-buffered (Android + iOS) — high-frequency
events no longer drop during a render.clear_tapsreset the handle count
to 0 and re-registered every handler in tree order, so a drag/scroll firing
from the UI thread while a render rebuilt the table saw a transiently-small
count and a half-built table and got dropped — worse the later a widget
registered (e.g. aCanvasafter a row ofButtons).register_tapnow
builds into the inactive table while readers keep resolving the last committed
one;set_rootswaps them atomically undertap_mutex. A concurrent event
always sees a complete table on either side of the swap. No API change.
Verified on-device (moto, finger-drag canvas).
0.7.3
Removed (BREAKING)
Mob.Backgroundis no longer in core — it moved to the opt-in
mob_backgroundplugin. Background-execution keep-alive (iOS silent
AVAudioEngine / AndroiddataSyncforeground service) and its
background_keep_alive/background_stopNIFs are removed from:mob_nif.
Apps that callMob.Background.keep_alive/0must add
{:mob_background, "~> 0.1"}, enable it inmob.exs
(config :mob, :plugins, [:mob_background]), and call
MobBackground.keep_alive/0instead. Most apps never used it; the default is
now that an app ships no foreground service unless it opts in — which is
also what Google Play wants (an unuseddataSyncFGS is a policy rejection).
Verified on Android (physical + emulator) and the iOS simulator via
mob_plugin_demo.
0.7.2
Added
Mob.ScreenCase— the blessed way to unit-test aMob.Screenin-BEAM,
with an optional device backend. Providesmount_screen/3,
render_event/render_info, tree queries (find/find_all/text),
assert_renderable/2, andnavigated_to/1. On:beamit runs in
milliseconds; the same assertions run against real hardware via:device.
navigated_to/1returns the destination module on both backends. (#44)
0.7.1
Added
- Collocated screen templates: a
Mob.Screenwith a sibling<name>.mob.heex
and no inlinerender/1getsrender/1compiled from that template
(@external_resource, so editing the template recompiles the screen). An
inlinerender/1still wins. Opt-in and additive. (#22) Mob.Files.pick/2type filtering::typesnow limits what the document
picker offers — extension strings ("livemd"), MIME strings ("application/pdf",
"text/*"), semantic atoms (:images,:video,:audio,:pdf,:text),
explicit{:extension|:mime|:uti, value}tuples, or:any(default).
iOS filters strictly viaUTType(extensions resolve even for unregistered
custom types); Android SAF filters by MIME only, soMob.Files.accept/2+
matches?/2enforce the filter on results for consistent cross-platform
semantics. Backward-compatible — the default:anypreserves the previous
"offer everything" behavior. Seedecisions/2026-06-16-files-pick-type-filter.md.
OTP runtime 5c9c69fc (Elixir 1.20.1)
Pre-built OTP-29 / erts-17.0 runtime tarballs with Elixir 1.20.1 stdlib (elixir/logger/eex). Same OTP base as otp-7d46fdd4, Elixir bumped rc.5 -> 1.20.1. Consumed by mob_dev via @otp_hash.
0.7.0
Added
- Pure-Elixir composite components (
Mob.Composite): UI kits register tag-name expanders (the manifestui_componentsexpand:form, orMob.Composite.register/2) and<MyTag …/>expands to built-in widget trees in a new FIRST render pass — fixpoint with a depth guard, crash-isolated.on_*props written as bare strings/atoms are auto-injected as{screen_pid, tag}(no more threadingself()). Hot-pushable. Seedecisions/2026-06-11-composite-expansion-pass.md. - Route-bound navigation params (
Mob.Nav.Registry.register/3+lookup_route/1): a registered route can carry a params map merged under push params intomount/3— the enabler for data-driven plugins (mob_ash registers/ash/postas{MobAsh.ListScreen, %{resource: …}}). Screen-manifest entries take an optional:params. - Style packages, tokens-only tier (MOB_STYLES.md implemented in part): the runtime manifest carries
styles/default_style; boot applies the default style's theme (Mob.Plugins.apply_default_style/0). The five preset themes ship in themob_themespackage. - Boot-time plugin NIF loading (
mob_notify_set_screen_pidseam,host_requirementsprinting,compositesboot registration) — the plugin-system core wiring landed across this cycle; see MOB_PLUGINS.md.
Removed (BREAKING — each capability moves to its plugin package)
Mob.Camera→mob_camera(thecamera_previewnode stays in core)Mob.Location→mob_locationMob.Notify→mob_notify(delivery plumbing — delegate, push-token forward, launch handoff — stays in core; pairs with the server-sidemob_push)Mob.Photos→mob_photosMob.Biometric→mob_biometricMob.Scanner→mob_scanner(requiresmob_camerafor the:camerapermission)Mob.Bt→mob_bluetooth(Wave 1)- Themes
Obsidian/ObsidianGlass/Citrus/Birch/Material3→mob_themes(light/dark/adaptive remain the neutral baseline)
No deprecation shims (see plugin_extraction_plan.md for the policy rationale). Migration: add the package dep + activate inmob.exs; module names change (Mob.Camera→MobCamera,Mob.Theme.Citrus→MobThemes.Citrus, …).
0.6.26
Fixed
- iOS: stop capping the literal super-carrier at 10 MB.
mob_beam.mappended a hardcoded-MIscs 10after the configured flags; since allocator flags are last-wins, it silently overrode the 0.6.24-MIscs 128default (and anymob_beam_flagsoverride), so the literal area was always 10 MB. A large app (e.g. embedded Livebook) plus a notebook'sMix.installfilled it and the VM aborted withliteral_alloc: Cannot allocate .... Removed the hardcoded cap; the-MIscs 128default now takes effect (iOS accepts a 128 MB reservation). Verified on a physical iPhone:emu_argsshows a single-MIscs 128andMix.installreturns:ok.
0.6.25
Added
- "Open with" — receive a file another app opens into yours. New
Mob.Files.take_opened_document/0returns%{path, name, mime, size}(or:none) for a file handed to the app (e.g. a notebook emailed and tapped), parallel toMob.Files.pick/2's{:files, :picked, …}. Call it from your root screen'smount/3; a file opened while already running arrives as{:files, :opened, item}(iOS). New NIFtake_opened_documentplus C-exportmob_set_opened_documenton both platforms (iOSapplication:openURL:options:→mob_handle_opened_url; AndroidMainActivityreads the ACTION_VIEW/SEND intent →MobBridge.setOpenedDocument). The app declares the document type (iOSCFBundleDocumentTypes, Android<intent-filter>) and forwards the open. Verified end-to-end: a.livemdopened into the embedded-Livebook app opens as a notebook on a physical iPhone and a physical Android (Moto G).
0.6.24
Fixed
- iOS: enlarge the BEAM literal super-carrier to 128 MB (
-MIscs 128default flag). iOS can't reserve the OTP default 1 GB literal virtual area and falls back to ~10 MB. A large app such as an embedded Livebook plus a notebook'sMix.installfills that 10 MB and the VM aborts withliteral_alloc: Cannot allocate N bytes (of type "literal"). The iOS native launcher's default flags now request a 128 MB literal carrier — a virtualMAP_NORESERVEreservation (commits physical only on use) that iOS accepts where 1 GB fails. Apps no longer need a per-appbeam_flags:override for this. iOS-only; Android keeps its normal large carrier. A runtimemob_beam_flagsoverride still wins. Verified on a physical iPhone: embedded Livebook serves andMix.install([{:short_uuid, "~> 0.1"}])returns:ok.