The plugin's on-device proof (Mob.Plugin.SelfTest), run by
mix mob.selftest and mob_ci for every activated plugin.
One synchronous, read-only native call: :mob_sms_nif.sms_available/0.
It opens no composer, registers no receiver, sends nothing and needs no
permission.
- iOS — the Objective-C NIF answers
[MFMessageComposeViewController canSendText], the same checkMobSms.compose/2makes before presenting the sheet. - Android — the zig NIF calls
MobSmsBridge.sms_available()over JNI, which answersTelephonyManager.isSmsCapablethrough the Activity the plugin bootstrap handed the bridge.
What each answer maps to:
true→:pass. The NIF is linked and initialised (Android: the Kotlin bridge is registered and has its Activity), and this device can send SMS, socompose/2would present the composer.false→:pass. The same native path answered; this device has no SMS service (the iOS Simulator, an emulator or tablet without telephony), socompose/2delivers{:sms, :not_available}, which is the documented behaviour there. Sending needs telephony and a person tapping Send, so the composer itself is never the proof.{:error, :bridge_not_registered}→ fail:MobSmsBridge.register()never ran or thesms_availablemethod-ID lookup failed, so every call into the bridge is dead in this host.{:error, :no_activity}→ fail: the bootstrap never calledsetActivity, socompose/2andOneTimeCode.arm/2would always give up.{:error, :no_jni_env}/{:error, :query_failed}→ fail: no JNIEnv for the scheduler thread, or theTelephonyManagerlookup threw, a Java exception escaped the bridge or it answered an unknown code.- The host stub's
nif_not_loaded(anErlangError) → fail: the native library was not linked into this build. - Anything else → fail, naming what came back.