Current section
Files
Jump to
Current section
Files
README.md
# mob_sms
SMS composer for [Mob](https://github.com/GenericJam/mob) apps. Opens the user's default messaging app pre-filled with recipient + body; the user taps Send to actually send. No dangerous permissions on either platform. Cross-platform surface (`MobSms.compose/2`), per-platform delivery asymmetry documented honestly.
## Install
Requires mob 0.9.0 or newer.
```elixir
def deps do
[
{:mob, "~> 0.9.0"},
{:mob_sms, "~> 0.1"}
]
end
```
In `mob.exs`, activate the plugin:
```elixir
config :mob, :plugins, [:mob_sms]
```
## Usage
```elixir
MobSms.compose(socket,
to: "+15551234567",
body: "Join me on Sample App: https://example.com/i/ABC123"
)
```
Both `:to` and `:body` are optional. Omit `:to` to let the user pick a recipient from Contacts inside the composer.
Result arrives as a message to the calling process:
```elixir
def handle_info({:sms, :sent}, socket), do: ... # iOS
def handle_info({:sms, :cancelled}, socket), do: ... # iOS
def handle_info({:sms, :composer_opened}, socket), do: ... # Android
def handle_info({:sms, :not_available}, socket), do: ... # both
```
## Per-platform delivery — the asymmetry to know
* **iOS** — `MFMessageComposeViewController` is presented as a sheet inside your app. The system delegate reports `:sent`, `:cancelled`, or `:failed` reliably. `:not_available` means the device cannot send SMS (simulator, no cellular / iMessage capability, no SIM).
* **Android** — `Intent.ACTION_SENDTO` with `smsto:` opens the user's default SMS app externally. Android does not reliably tell the host app whether the user tapped Send or cancelled — most SMS apps do not set an activity result. The plugin delivers `:composer_opened` once the composer is showing (or `:not_available` if no SMS-capable app is installed) and stops there. If knowing what the user did afterwards matters to your flow, plan around this asymmetry rather than assuming a `:sent`.
This asymmetry is by design at the platform layer, not something the plugin can paper over. Android intentionally hands the user off to a separate app rather than embedding a sheet; iOS presents inline and can observe the outcome.
## Common use cases
- **Invite codes** — "Join me on X, here's the link" — the canonical fit
- **Share a link / share a file** — user picks a friend inside the composer
- **Tell a friend** — referral flows without your app needing to know the friend's number
- **Verification workaround** — send a code to a manually-provided number without SMS gateway costs
## What this plugin is NOT
Not silent-send. iOS has no public API for sending SMS without the user tapping Send; Apple treats that as a bright-line spam surface. Android *can* silent-send via `SmsManager.sendTextMessage` under the `SEND_SMS` runtime permission, but Google Play rejects that permission request for most non-SMS-centric apps (the "Permissions Declaration" gate).
If you have a real reason to bypass the sheet on Android — and can accept iOS being permanently in composer mode — a separate `send/2` on this module can land in a later release. Talk to us before designing around it.
## Related
- [MOB epic MOB-257](https://linear.app/mobframework/issue/MOB-257) — the mob_wake plugin arc, which covers the *receive* side of push notifications for apps that want the "wake me when something happens" flow that people often reach for background tasks to build.
## License
MIT.