Packages
mob
0.9.21
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.39
0.7.38
0.7.37
0.7.36
0.7.35
0.7.34
0.7.33
0.7.32
0.7.31
0.7.30
0.7.29
0.7.28
0.7.27
0.7.26
0.7.25
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.4
0.7.3
0.7.2
0.7.1
0.7.0
0.6.26
0.6.25
0.6.24
0.6.23
0.6.22
0.6.21
0.6.20
0.6.19
0.6.18
0.6.17
0.6.16
0.6.15
0.6.14
0.6.13
0.6.12
0.6.11
0.6.10
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.2
0.6.1
0.6.0
0.5.18
0.5.17
0.5.16
0.5.15
0.5.14
0.5.11
0.5.10
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.10
0.3.9
0.3.8
0.3.7
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
BEAM-on-device mobile framework for Elixir
Current section
Files
Jump to
Current section
Files
guides/mobile_surface_matrix.md
# Mobile surface matrix
What Mob covers — what's solid, what's partial, what's missing. Use
this to set realistic expectations before starting an app, and to
spot gaps worth filling (either in mob core, in a plugin, or by
declaring out-of-scope).
The reference surface is the union of **React Native core**,
**Expo SDK modules**, and platform-native capabilities both ecosystems
have converged on as "what mobile apps need." Many missing items are
**pluggable** — see [MOB_PLUGINS.md](../MOB_PLUGINS.md) for the
manifest spec.
This doc is hand-maintained from inspection of `lib/mob/` and
`src/mob_nif.erl`. If you add a capability, update the matching row.
## Legend
| | |
|--|--|
| ✅ | Fully present — public Elixir API, both iOS + Android (unless noted) |
| 🟡 | Partial — works but limited (single platform, narrow API, or known caveats) |
| ❌ | Missing — could be a plugin or future core addition |
| ⛔ | Out of scope — requires separate deployment target (widgets, Watch app), or fundamentally incompatible with Mob's architecture |
Per-platform columns: `✓` = supported, `🟡` = partial or not yet verified on a device, `—` = not supported, `n/a` = not applicable on that platform.
A ✅ capability may live in **core** or in a **first-party plugin**
(0.7.0 extracted camera, photos, location, notifications, biometrics,
scanning, and Bluetooth into `mob_*` capability packages). The Notes
column names the supplying plugin; rows without one are core. Plugins
activate with the dep + `config :mob, :plugins, [...]` in `mob.exs` —
see the [Plugins guide](plugins.md).
---
## UI components (render tree)
Elements you can use inside `~MOB`. The set is intentionally small
and orthogonal — composition over a fat component library.
| Component | Status | iOS | Android | Notes |
|--|--|--|--|--|
| `<Box>` | ✅ | ✓ | ✓ | Container with align, padding, background, corner radius, border |
| `<Column>`, `<Row>` | ✅ | ✓ | ✓ | Flex layouts |
| `<Wrap>` | ✅ | ✓ | ✓ | Measured wrapping layout for chips, tags, and pills |
| `<Text>` | ✅ | ✓ | ✓ | Font, color, size, weight, align, line height, letter spacing |
| `<Button>` | ✅ | ✓ | ✓ | Tap handler, text, background, fill width |
| `<Image>` | ✅ | ✓ | ✓ | Local + remote (Coil on Android, AsyncImage on iOS) |
| `<TextField>` | ✅ | ✓ | ✓ | Keyboard type, return key, placeholder, change events |
| `<Toggle>` | ✅ | ✓ | ✓ | Boolean switch |
| `<Slider>` | ✅ | ✓ | ✓ | Min/max/value, change events |
| `<Progress>` | ✅ | ✓ | ✓ | Linear + circular |
| `<Divider>` | ✅ | ✓ | ✓ | Horizontal line separator |
| `<Spacer>` | ✅ | ✓ | ✓ | Layout filler |
| `<Scroll>` | ✅ | ✓ | ✓ | Vertical or horizontal, scroll observation on iOS 18+ |
| `<List>` | ✅ | ✓ | ✓ | Vertical / horizontal stack with selection |
| `<LazyList>` | ✅ | ✓ | ✓ | Virtualised long-list with on_end_reached pagination |
| `<TabBar>` | ✅ | ✓ | ✓ | Bottom tab bar (Material 3 NavigationBar on Android, SwiftUI Tab on iOS) |
| `<WebView>` | ✅ | ✓ | ✓ | Inline web view, JS bridge, navigation control |
| `<CameraPreview>` | ✅ | ✓ | ✓ | Live preview with frame stream |
| `<Video>` | 🟡 | ✓ | 🟡 | Android: ExoPlayer integration pending |
| `<GpuView>` | ✅ | ✓ | ✓ | Metal (iOS) / GLES 3.0 (Android) fragment shader surface |
| Custom views via `<NativeView>` | ✅ | ✓ | ✓ | Register a plugin-defined render-tree node type |
| Date / Time / Color pickers | ❌ | — | — | Plugin candidate |
| `<SearchBar>` | ❌ | — | — | Native search bar (UISearchBar / SearchBar). Plugin candidate |
| `<DatePicker>` | ❌ | — | — | Plugin candidate |
| `<Modal>` (sheet presentation) | ✅ | ✓ | ✓ | `Mob.UI.sheet/2` — native modal sheet composing ordinary Mob nodes (iOS `.sheet`, Android Material 3 `ModalBottomSheet`). Android needs an app generated by `mob_new` ≥ 0.4.24. Programmatic alerts + action sheets also available via `Mob.Alert` |
| Pull-to-refresh | ❌ | — | — | Missing; commonly requested. Plugin candidate |
| Bottom sheets | ✅ | ✓ | ✓ | Same primitive as `<Modal>` above — `Mob.UI.sheet/2` with `:detents` (`[:medium, :large]` subset). iOS cannot set the system dimming opacity, so `:scrim` is applied exactly on Android and approximated on iOS |
| Drawer navigation | ❌ | — | — | Plugin candidate; mob's nav model is stack-based today |
## Touch, gesture, input
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Tap / double-tap / long-press | ✅ | ✓ | ✓ | `on_tap`, `on_double_tap`, `on_long_press` props |
| Press in / out (hold) | ✅ | ✓ | ✓ | `on_press_in`, `on_press_out` props (MOB-380); `Mob.Test.hold_xy/4` drives a real held touch on Android |
| Swipe (l/r/u/d) | ✅ | ✓ | ✓ | `on_swipe_left`, etc. |
| Pan / drag gesture | 🟡 | 🟡 | 🟡 | Tap-based; full pan-responder system (like react-native-gesture-handler) is missing |
| Pinch / zoom | ❌ | — | — | Plugin candidate; common for image/map views |
| Rotation gesture | ❌ | — | — | Plugin candidate |
| Hardware keyboard events | ❌ | — | — | `key_press/1` exists for the test harness but not as a user-facing API |
| Keyboard show/hide events | ❌ | — | — | Missing; commonly needed for keyboard-aware layouts |
| `<KeyboardAvoidingView>` equivalent | ❌ | — | — | Plugin / core candidate |
| Apple Pencil / stylus events | ❌ | — | n/a | Plugin territory |
| 3D Touch / Force Touch | ❌ | — | n/a | Deprecated by Apple; low priority |
| Drag and drop (cross-app) | ❌ | — | — | Plugin candidate |
| Haptic feedback | ✅ | ✓ | ✓ | `Mob.Haptic.trigger/2` |
## Device + system info
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Platform detection | ✅ | ✓ | ✓ | `Mob.Device.platform/0` returns `:ios` or `:android` |
| OS version | ✅ | ✓ | ✓ | `Mob.Device.os_version/0` |
| App version | ✅ | ✓ | ✓ | `Mob.Device.app_version/0` — `CFBundleShortVersionString` / `PackageInfo.versionName`; `nil` if unavailable |
| Device model | ✅ | ✓ | ✓ | `Mob.Device.model/0` |
| Foreground / background state | ✅ | ✓ | ✓ | `Mob.Device.foreground?/0` + `{:device, :foreground/:background, ...}` events |
| Battery level + state | ✅ | ✓ | ✓ | `Mob.Device.battery_level/0`, `battery_state/0` |
| Thermal state | ✅ | ✓ | ✓ | `Mob.Device.thermal_state/0` |
| Low-power mode | ✅ | ✓ | ✓ | `Mob.Device.low_power_mode?/0` |
| Color scheme (light/dark) | ✅ | ✓ | ✓ | `Mob.Theme.color_scheme/0` + `Mob.Theme.Adaptive` (auto-watch) |
| Safe area insets | ✅ | ✓ | ✓ | `Mob.Device.safe_area/0` |
| Screen dimensions / pixel ratio | ✅ | ✓ | ✓ | `Mob.Device.screen_info/0` |
| Window size class (compact/regular) | ✅ | ✓ | ✓ | `assigns.size_class` + `handle_info({:mob_size_class_changed, new}, socket)` — iOS trait collection; Android `Configuration` dp with Material breakpoints. See `Mob.SizeClass` |
| Locale / language | 🟡 | 🟡 | 🟡 | Derivable from system; no first-class API |
| Time zone | 🟡 | 🟡 | 🟡 | Use Erlang's `:calendar` directly |
| Network info (cell vs wifi, type) | ❌ | — | — | Plugin candidate (NetInfo equivalent) |
| Network reachability | ❌ | — | — | Plugin candidate |
| Screen brightness | ❌ | — | — | Plugin candidate |
| Screen orientation lock | ✅ | ✓ | ✓ | `Mob.Device.lock_orientation/1` + `unlock_orientation/0` (Android `setRequestedOrientation`; iOS supported-orientations + geometry request) |
| Idle timer / screen wake | ✅ | ✓ | ✓ | `Mob.Device.keep_awake/1` — prevent auto-dim/lock (iOS `isIdleTimerDisabled`; Android `FLAG_KEEP_SCREEN_ON`) |
| Exit app | ✅ | n/a | ✓ | Android only; iOS forbids programmatic exit |
## Storage
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Key-value store | ✅ | ✓ | ✓ | `Mob.Storage` with typed schemas, namespacing |
| Files API | ✅ | ✓ | ✓ | `Mob.Files`, plus `Mob.Storage.dir/1` for app-private paths |
| External files dir (Android) | ✅ | n/a | ✓ | `storage_external_files_dir/1` |
| Save to photo library | ✅ | ✓ | ✓ | `Mob.Storage.save_to_photo_library/1` + Android MediaStore equivalent |
| SQLite | ✅ | ✓ | ✓ | Via `:ecto_sqlite3` + bundled `libsqlite3_nif.so` |
| Keychain / Keystore | ❌ | — | — | Plugin candidate (standalone API beyond biometric) |
| Secure-storage / encrypted-storage | ❌ | — | — | Plugin candidate |
## Camera + microphone
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Capture photo | ✅ | ✓ | ✓ | `MobCamera.capture_photo/2` (`mob_camera` plugin) |
| Capture video | ✅ | ✓ | ✓ | `MobCamera.capture_video/2` (`mob_camera` plugin) |
| Live preview | ✅ | ✓ | ✓ | `<CameraPreview>` component (core; session API in `mob_camera`) |
| Per-frame stream | ✅ | ✓ | ✓ | `MobCamera.start_frame_stream/2` (`mob_camera` plugin) — pushes RGBA frames to `handle_info` |
| Photo library picker | ✅ | ✓ | ✓ | `MobPhotos.pick/2` (`mob_photos` plugin) |
| Audio recording | ✅ | ✓ | ✓ | `Mob.Audio.start_recording/2` |
| Audio playback | ✅ | ✓ | ✓ | `Mob.Audio.play/3`, stop, volume |
| Text-to-speech | ✅ | ✓ | ✓ | `Mob.Speech.speak/3` + `stop_speaking/1` (AVSpeechSynthesizer / TextToSpeech) |
| Speech recognition | 🟡 | 🟡 | ✓ | `mob_speech` plugin (`MobSpeech.listen/2`; platform recognizer by default, or offline whisper.cpp via `mob_whisper`). Android verified; iOS builds, not yet run on a device |
| Voice activity detection | ❌ | — | — | Plugin candidate |
| Audio effects (reverb, EQ) | ❌ | — | — | Plugin candidate |
| Camera zoom / focus / exposure | 🟡 | 🟡 | 🟡 | Basic capture works; fine-grained control missing |
| Torch / flashlight | ✅ | ✓ | ✓ | `Mob.Torch.on/1`, `off/1`, `set/2` — core, no camera session or permission. On/off only (brightness level is a follow-up) |
## Connectivity
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Bluetooth Classic | ✅ | n/a | ✓ | `MobBluetooth` plugin (extracted; Hfp / Spp sub-modules) — central/host role, Android only |
| Bluetooth Low Energy (BLE) | 🟡 | ✓ | ✓ | `MobBluetooth.Le` (mob_bluetooth 0.3.0) — GATT **peripheral** role only (advertise + notify + receive writes), iOS + Android; BLE central (scan/connect) is a future addition |
| NFC | ✅ | ✓ | ✓ | `mob_nfc` plugin — read/write NDEF tags, raw tag UIDs (CoreNFC / `NfcAdapter`); tag emulation (HCE) is Android-only |
| WiFi info / scanning | ❌ | — | — | Plugin candidate; OS restrictions apply |
| USB host | ✅ | n/a | ✓ | `Mob.VendorUsb` — bulk read/write, custom devices |
| WebSocket client | 🟡 | n/a | n/a | Use Elixir libs directly (e.g. `:gun`) |
| HTTP client | 🟡 | n/a | n/a | Use Elixir libs (`:req`, `:finch`) |
| File upload progress | ❌ | — | — | Plugin candidate |
| Background download/upload | ❌ | — | — | Plugin candidate |
| mDNS / Bonjour | ❌ | — | — | Plugin candidate |
| Sockets (raw TCP/UDP) | 🟡 | n/a | n/a | Use Erlang's `:gen_tcp`/`:gen_udp` |
| Mob.Dist (BEAM clustering) | ✅ | ✓ | ✓ | Hot-push, device → desktop connection |
## Sensors
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Accelerometer | ✅ | ✓ | ✓ | `Mob.Motion.start(:accelerometer, ...)` |
| Gyroscope | ✅ | ✓ | ✓ | `Mob.Motion.start(:gyro, ...)` |
| Magnetometer | ✅ | ✓ | ✓ | `Mob.Motion.start(:magnetometer, ...)` — µT-calibrated `mag` + fused `heading` (0.7.14); keys present only when `:magnetometer` requested |
| Barometer | 🟡 | 🟡 | ✓ | `MobSensors.read/2` / `start/2` with `:pressure` (`mob_sensors` plugin), in hPa. iOS uses `CMAltimeter` (kPa converted to hPa) and hasn't been verified on a device yet |
| Proximity | 🟡 | 🟡 | ✓ | `:proximity` (`mob_sensors`), in cm. On iOS it's iPhone-only `UIDevice` proximity monitoring: near is `[0.0]`, far is `[5.0]`, and the screen blanks while something is near. Not yet verified on a device |
| Ambient light | 🟡 | — | ✓ | `:light` (`mob_sensors`), in lux. iOS has no public ambient-light API |
| Pedometer / step counter | 🟡 | 🟡 | 🟡 | `:step_counter` and `MobSensors.steps/2` (`mob_sensors`); both need `:activity_recognition`. Android counts steps since boot and keeps no history (`steps/2` → `{:error, :history_unavailable}`). iOS uses `CMPedometer`: steps since midnight plus history queries. Not yet verified on hardware |
| Other sensors (humidity, ambient temperature, gravity, rotation vectors, hinge angle, heart rate, vendor sensors) | 🟡 | — | ✓ | `mob_sensors`: `MobSensors.list/0` returns every Android sensor; vendor sensors are read by their string type |
| Compass / heading | ✅ | ✓ | ✓ | Fused `heading` (degrees from magnetic north) via `Mob.Motion` `:magnetometer` — same request as above |
## Location
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| One-shot location | ✅ | ✓ | ✓ | `MobLocation.get_once/1` (`mob_location` plugin) |
| Continuous updates | ✅ | ✓ | ✓ | `MobLocation.start/2`, stop (`mob_location` plugin) |
| Background location | 🟡 | 🟡 | 🟡 | Mob's foreground-service keep-alive lets updates continue while backgrounded; not a true background-location API |
| Geofencing | ❌ | — | — | Plugin candidate (`CLCircularRegion` / `Geofencing API`) |
| Significant-change updates | ❌ | — | — | Plugin candidate (iOS) |
| Mock-location detection | ❌ | — | — | Plugin candidate |
| Reverse geocoding | ❌ | — | — | Use third-party API for now (e.g. Mapbox) |
## Notifications
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Local notification scheduling | ✅ | ✓ | ✓ | `MobNotify.schedule/2`, cancel (`mob_notify` plugin) |
| Push notification registration | ✅ | ✓ | ✓ | `MobNotify.register_push/1` (`mob_notify` plugin) → token to `handle_info` |
| Push delivery via APNs / FCM | ✅ | ✓ | ✓ | Via `mob_push` Hex package |
| Notification tap handling | 🟡 | ✓ | 🟡 | `{:notification, %{presentation: :foreground \| :tap}}` (`Mob.Notification`), including the tap that cold-launches the app, delivered once. Android: local notifications, and taps on pushes that carry mob_push's `mob_notification_json` data key (per FCM's documented behaviour, not device-verified); a push sent without it carries no mob payload |
| Notification actions (buttons) | ❌ | — | — | Plugin / core candidate |
| Critical / time-sensitive flags (iOS) | ❌ | — | n/a | Plugin candidate |
| Notification grouping / threading | ❌ | — | — | Plugin candidate |
| Badge management | 🟡 | 🟡 | 🟡 | Basic only |
## Background tasks
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Foreground service / keep-alive | ✅ | ✓ | ✓ | `MobBackground.keep_alive/0` (mob_background plugin) |
| Background fetch (silent periodic) | ✅ | ✓ | ✓ | `mob_wake` plugin — `register/3` + `schedule/2` over iOS `BGTaskScheduler` / Android `WorkManager`; the OS decides when it fires |
| Silent push handling | ✅ | ✓ | ✓ | `mob_wake` plugin — silent APNs / FCM data message wakes a registered handler (`MobWake.wake_payload/2` builds the payload for `mob_push`) |
| Background URL session | ❌ | — | — | Plugin candidate |
| Scheduled jobs (periodic / one-shot) | ❌ | — | — | Plugin candidate (WorkManager equivalent) |
## Auth + payment
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Biometric auth (Face ID / fingerprint) | ✅ | ✓ | ✓ | `MobBiometric.authenticate/2` (`mob_biometric` plugin) |
| Apple Sign-In | ❌ | — | n/a | Plugin candidate (common requirement for App Store) |
| Google Sign-In | ❌ | — | — | Plugin candidate |
| Sign in with X / Facebook / etc. | ❌ | — | — | Plugin candidate |
| OAuth flow helpers | ❌ | — | — | Plugin candidate; can mostly be done from Elixir |
| In-app purchase (StoreKit / Play Billing) | ❌ | — | — | Plugin candidate; sensitive — needs receipt validation |
| Apple Pay | ❌ | — | n/a | Plugin candidate |
| Google Pay | ❌ | — | — | Plugin candidate |
| Passkeys / WebAuthn | ❌ | — | — | Plugin candidate |
## ML / Vision
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| QR / barcode scanning | ✅ | ✓ | ✓ | `MobScanner.scan/2` (`mob_scanner` plugin; activate `mob_camera` too — it owns `:camera`) — full-screen scanner with format filtering |
| TFLite model inference | ✅ | ✓ | ✓ | Via `mix mob.enable tflite` (mob_dev 0.5.7+) — NNAPI/MTK on Android, Core ML delegate on iOS. The `nx_tflite_mob` package runs `.tflite` models with vendor accelerators (Apple Neural Engine, MediaTek/Qualcomm GPU+NPU) |
| Nx-based inference | 🟡 | 🟡 | 🟡 | `mob_nx_eigen` plugin — Eigen CPU Nx backend; spike, API surface still narrow |
| Apple Vision framework wrappers | 🟡 | 🟡 | n/a | `mob_vision` wraps `VNRecognizeTextRequest` (text recognition); face detection and image classification are plugin candidates |
| Apple Foundation Models (LLM) | ❌ | — | n/a | Plugin in flight — see mob PR #8 (DRAFT) |
| MLKit wrappers (Android) | 🟡 | n/a | 🟡 | Barcode scanning uses it under the hood; `mob_vision` uses ML Kit text recognition; face and pose models are plugin territory |
| OCR (text recognition) | 🟡 | 🟡 | 🟡 | `MobVision` (`mob_vision` plugin) — still-image text recognition via iOS Vision / Android ML Kit, no permission needed; no device run recorded yet |
| Face detection | ❌ | — | — | Plugin candidate |
| Pose detection | ❌ | — | — | Plugin candidate |
| Speech-to-text | 🟡 | 🟡 | ✓ | `mob_speech` + `mob_whisper` (on-device whisper.cpp); see Speech recognition above |
| Translation | ❌ | — | — | Plugin candidate |
| Smart Reply | ❌ | — | — | Plugin candidate |
## Maps
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Native map view | ❌ | — | — | Plugin candidate (Apple Maps / Google Maps) |
| Annotations / markers | ❌ | — | — | Plugin candidate |
| Polylines / polygons | ❌ | — | — | Plugin candidate |
| User location display | ❌ | — | — | Plugin candidate |
| Map tile providers (Mapbox, etc.) | ❌ | — | — | Plugin candidate |
## System integration
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Clipboard | ✅ | ✓ | ✓ | `Mob.Clipboard.put/1`, `get/0` |
| Open URL (outgoing: browser, mail, tel, another app) | ✅ | ✓ | ✓ | `Mob.Device.open_url/1` — picks browser, mail, tel, etc. |
| Opened by a URL (incoming deep link, custom scheme) | 🟡 | 🟡 | ✓ | `{:link, %{url: url, source: :launch \| :running}}` (`Mob.Link`), including the link that cold-launches the app, delivered once; `Mob.Link.register/1` routes elsewhere. Schemes from `config :mob_dev, url_schemes: [...]` in `mob.exs` (mob_dev 0.7.12+); Android VIEW intents and iOS scene URL contexts forwarded by mob_new 0.6.4+ templates. iOS not yet run on a simulator or device |
| Share sheet (text) | ✅ | ✓ | ✓ | `Mob.Share.text/1` |
| Share sheet (image / file) | ❌ | — | — | Plugin candidate |
| Document picker | ✅ | ✓ | ✓ | `Mob.Files.pick/1` |
| Action sheet (iOS-style menu) | ✅ | ✓ | ✓ | `action_sheet_show/2` via Mob.Alert |
| Toast (Android-style) | ✅ | ✓ | ✓ | `toast_show/2` — implemented on both platforms |
| Alert dialog | ✅ | ✓ | ✓ | `Mob.Alert.alert/2` |
| Vibration patterns | ✅ | ✓ | ✓ | Via `Mob.Haptic` |
| App settings page (open) | ❌ | — | — | Plugin candidate |
| Calendar events | ❌ | — | — | Plugin candidate |
| Contacts | ❌ | — | — | Plugin candidate |
| Reminders (iOS) | ❌ | — | n/a | Plugin candidate |
| Permissions facade | ✅ | ✓ | ✓ | `Mob.Permissions.request/2` for camera, microphone, photos, location, notifications |
## Accessibility
| Capability | Status | iOS | Android | Notes |
|--|--|--|--|--|
| Accessibility labels / hints | 🟡 | 🟡 | 🟡 | Some component props expose this; not uniform across all components |
| Screen reader announcements (imperative) | ❌ | — | — | Plugin / core candidate |
| Focus management (programmatic) | ❌ | — | — | Plugin / core candidate |
| Reduce-motion preference | ❌ | — | — | Plugin candidate |
| Bold-text preference | ❌ | — | — | Plugin candidate |
| Dynamic type / font scaling | 🟡 | 🟡 | 🟡 | iOS automatic via system size; explicit override needed |
| RTL support | 🟡 | 🟡 | 🟡 | Layout-engine level; no explicit `I18nManager` equivalent |
| Accessibility test inspection | ✅ | ✓ | ✓ | `Mob.Test` reads the AX tree for assertion-based UI testing |
## iOS-only platform features
| Capability | Status | Notes |
|--|--|--|
| Live Activities / Dynamic Island | ⛔ | Requires Widget Extension target — separate from main app; not a Mob template today |
| Widgets (home + lock screen) | ⛔ | Same — Widget Extension target |
| App Clips | ⛔ | App Clip target; not in Mob templates |
| Watch app companion | ⛔ | WatchKit target; not in Mob templates |
| Share extensions | ⛔ | Share Extension target; not in Mob templates |
| Today extensions (deprecated by Apple) | ⛔ | — |
| Background App Refresh | ❌ | Plugin candidate |
| Apple Pencil events | ❌ | Plugin candidate |
| Multi-window (iPad) | 🟡 | App runs but no first-class multi-scene API |
| Split View / Slide Over | 🟡 | Same as multi-window |
| Picture in Picture (video) | ❌ | Plugin candidate |
| Universal Links / Custom URL Scheme | 🟡 | Custom schemes: `Mob.Link` (see System integration). Universal links need an associated-domains entitlement and arrive as `NSUserActivity`, which the template does not forward |
| Handoff / NSUserActivity | ❌ | Plugin candidate |
| Spotlight indexing | ❌ | Plugin candidate |
| App Shortcuts (Siri integration) | ❌ | Plugin candidate |
## Android-only platform features
| Capability | Status | Notes |
|--|--|--|
| Home screen widgets | ⛔ | AppWidgetProvider — separate component, not in Mob templates |
| Quick Settings tiles | ⛔ | TileService — separate component |
| App Shortcuts (long-press launcher) | ❌ | Plugin candidate |
| Picture in Picture | ❌ | Plugin candidate |
| Multi-window | ✅ | Works via resizable activity flag |
| Split-screen | ✅ | Works via resizable activity flag |
| Foldable / large-screen support | 🟡 | Layout adapts; no first-class foldable APIs |
| Auto Backup | ✅ | Honored by default per AndroidManifest |
| Doze mode handling | ❌ | Plugin candidate (alarm/wake-up scheduling) |
| Direct Share | ❌ | Plugin candidate |
| Notification channels (configurable) | 🟡 | Default channel works; per-app multi-channel API is partial |
## Architecturally not present (and probably shouldn't be)
| Item | Why |
|--|--|
| JavaScript / TypeScript runtime | Mob's host language is Elixir/Erlang/Gleam on BEAM; bridging JS would defeat the architecture |
| React reconciler | Mob has its own render tree; no React VDOM under it |
| CSS / Yoga flexbox engine | iOS uses SwiftUI layout; Android uses Compose layout; both are native flexbox-equivalents |
| `XMLHttpRequest` / `fetch` polyfill | Use Erlang/Elixir HTTP libraries directly (`:req`, `:finch`, `:gun`) |
| Babel / Metro / bundler | BEAM bytecode replaces JS bundling; `mix mob.push` ships `.beam` directly |
---
## How to use this matrix
- **Starting a new app**: scan the ❌ rows first to see what would need to be a plugin or worked around.
- **Reporting a gap**: if something here is wrong (mob has a capability I missed, or partial that's actually full), the doc is hand-maintained — please open a PR or flag it.
- **Filling a gap as a plugin**: see [MOB_PLUGINS.md](../MOB_PLUGINS.md) for the manifest spec. Most ❌ rows are plugin candidates (tier 1 or tier 2 depending on whether they ship UI).
- **Filling a gap in core**: when a capability is universal enough (every app needs it, both platforms support it cleanly) it's worth landing in core rather than as a plugin. The boundary is fuzzy; raising a discussion before doing the work is the right call.
This matrix isn't a roadmap commitment — it's a snapshot of reality.
Some ❌ rows may stay ❌ for a long time because no one's asked.
Others will land via community plugins. The intent is honest
disclosure, not a promise of feature parity with React Native.
---
## Related docs
- [`MOB_PLUGINS.md`](../MOB_PLUGINS.md) — plugin manifest spec for
filling missing capabilities without merging into core
- [`RELEASE.md`](https://github.com/GenericJam/mob/blob/master/RELEASE.md) — release process if you're shipping a
new capability that lands in core
- [`guides/styling.md`](styling.md) — visual styling for the
components above (tokens, themes, dark mode)
- [`guides/support_matrix.md`](support_matrix.md) — minimum
OS / ABI / SDK supported (a different "support matrix" —
platform versions rather than capabilities)