Skip to content

Releases: GenericJam/mob

0.7.5

21 Jun 04:10

Choose a tag to compare

Fixed

  • iOS canvas now delivers finger-drag (on_drag) — at parity with Android.
    The SwiftUI MobCanvasView rendered draw ops but attached no drag recognizer,
    so a canvas's on_drag handle (wired through the NIF to node.onDrag) was
    never invoked — continuous finger-drag was dead on iOS, while Android's
    MobCanvas had detectDragGestures. Added a canvas-scoped
    DragGesture(minimumDistance: 0) that calls node.onDrag with
    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

20 Jun 20:07

Choose a tag to compare

Fixed

  • Tap-handle registry is now double-buffered (Android + iOS) — high-frequency
    events no longer drop during a render.
    clear_taps reset 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. a Canvas after a row of Buttons). register_tap now
    builds into the inactive table while readers keep resolving the last committed
    one; set_root swaps them atomically under tap_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

19 Jun 22:36

Choose a tag to compare

Removed (BREAKING)

  • Mob.Background is no longer in core — it moved to the opt-in
    mob_background plugin.
    Background-execution keep-alive (iOS silent
    AVAudioEngine / Android dataSync foreground service) and its
    background_keep_alive/background_stop NIFs are removed from :mob_nif.
    Apps that call Mob.Background.keep_alive/0 must add
    {:mob_background, "~> 0.1"}, enable it in mob.exs
    (config :mob, :plugins, [:mob_background]), and call
    MobBackground.keep_alive/0 instead. 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 unused dataSync FGS is a policy rejection).
    Verified on Android (physical + emulator) and the iOS simulator via
    mob_plugin_demo.

0.7.2

19 Jun 07:00

Choose a tag to compare

Added

  • Mob.ScreenCase — the blessed way to unit-test a Mob.Screen in-BEAM,
    with an optional device backend. Provides mount_screen/3,
    render_event/render_info, tree queries (find/find_all/text),
    assert_renderable/2, and navigated_to/1. On :beam it runs in
    milliseconds; the same assertions run against real hardware via :device.
    navigated_to/1 returns the destination module on both backends. (#44)

0.7.1

17 Jun 04:00

Choose a tag to compare

Added

  • Collocated screen templates: a Mob.Screen with a sibling <name>.mob.heex
    and no inline render/1 gets render/1 compiled from that template
    (@external_resource, so editing the template recompiles the screen). An
    inline render/1 still wins. Opt-in and additive. (#22)
  • Mob.Files.pick/2 type filtering: :types now 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 via UTType (extensions resolve even for unregistered
    custom types); Android SAF filters by MIME only, so Mob.Files.accept/2 +
    matches?/2 enforce the filter on results for consistent cross-platform
    semantics. Backward-compatible — the default :any preserves the previous
    "offer everything" behavior. See decisions/2026-06-16-files-pick-type-filter.md.

OTP runtime 5c9c69fc (Elixir 1.20.1)

17 Jun 07:34

Choose a tag to compare

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

12 Jun 18:08

Choose a tag to compare

Added

  • Pure-Elixir composite components (Mob.Composite): UI kits register tag-name expanders (the manifest ui_components expand: form, or Mob.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 threading self()). Hot-pushable. See decisions/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 into mount/3 — the enabler for data-driven plugins (mob_ash registers /ash/post as {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 the mob_themes package.
  • Boot-time plugin NIF loading (mob_notify_set_screen_pid seam, host_requirements printing, composites boot registration) — the plugin-system core wiring landed across this cycle; see MOB_PLUGINS.md.

Removed (BREAKING — each capability moves to its plugin package)

  • Mob.Cameramob_camera (the camera_preview node stays in core)
  • Mob.Locationmob_location
  • Mob.Notifymob_notify (delivery plumbing — delegate, push-token forward, launch handoff — stays in core; pairs with the server-side mob_push)
  • Mob.Photosmob_photos
  • Mob.Biometricmob_biometric
  • Mob.Scannermob_scanner (requires mob_camera for the :camera permission)
  • Mob.Btmob_bluetooth (Wave 1)
  • Themes Obsidian/ObsidianGlass/Citrus/Birch/Material3mob_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 in mob.exs; module names change (Mob.CameraMobCamera, Mob.Theme.CitrusMobThemes.Citrus, …).

0.6.26

01 Jun 04:15

Choose a tag to compare

Fixed

  • iOS: stop capping the literal super-carrier at 10 MB. mob_beam.m appended a hardcoded -MIscs 10 after the configured flags; since allocator flags are last-wins, it silently overrode the 0.6.24 -MIscs 128 default (and any mob_beam_flags override), so the literal area was always 10 MB. A large app (e.g. embedded Livebook) plus a notebook's Mix.install filled it and the VM aborted with literal_alloc: Cannot allocate .... Removed the hardcoded cap; the -MIscs 128 default now takes effect (iOS accepts a 128 MB reservation). Verified on a physical iPhone: emu_args shows a single -MIscs 128 and Mix.install returns :ok.

0.6.25

31 May 19:36

Choose a tag to compare

Added

  • "Open with" — receive a file another app opens into yours. New Mob.Files.take_opened_document/0 returns %{path, name, mime, size} (or :none) for a file handed to the app (e.g. a notebook emailed and tapped), parallel to Mob.Files.pick/2's {:files, :picked, …}. Call it from your root screen's mount/3; a file opened while already running arrives as {:files, :opened, item} (iOS). New NIF take_opened_document plus C-export mob_set_opened_document on both platforms (iOS application:openURL:options:mob_handle_opened_url; Android MainActivity reads the ACTION_VIEW/SEND intent → MobBridge.setOpenedDocument). The app declares the document type (iOS CFBundleDocumentTypes, Android <intent-filter>) and forwards the open. Verified end-to-end: a .livemd opened into the embedded-Livebook app opens as a notebook on a physical iPhone and a physical Android (Moto G).

0.6.24

31 May 06:22

Choose a tag to compare

Fixed

  • iOS: enlarge the BEAM literal super-carrier to 128 MB (-MIscs 128 default 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's Mix.install fills that 10 MB and the VM aborts with literal_alloc: Cannot allocate N bytes (of type "literal"). The iOS native launcher's default flags now request a 128 MB literal carrier — a virtual MAP_NORESERVE reservation (commits physical only on use) that iOS accepts where 1 GB fails. Apps no longer need a per-app beam_flags: override for this. iOS-only; Android keeps its normal large carrier. A runtime mob_beam_flags override still wins. Verified on a physical iPhone: embedded Livebook serves and Mix.install([{:short_uuid, "~> 0.1"}]) returns :ok.