@symbiote-native/sms 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -25
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# @symbiote-native/sms
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
[
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
[
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
Open the system SMS composer with recipients, a message and optionally an attachment already
|
|
4
|
+
filled in. One API for every [SymbioteNative](../../README.md) adapter (React, Vue, Svelte, Solid
|
|
5
|
+
and Angular).
|
|
6
|
+
|
|
7
|
+
It wraps [`expo-sms`](https://github.com/expo/expo/tree/main/packages/expo-sms) the same way
|
|
8
|
+
[`@symbiote-native/secure-store`](../secure-store) wraps its upstream: `expo-modules-core` is a
|
|
9
|
+
direct dependency, never the `expo` meta-package, and the upstream JS is hand-ported into `core/`.
|
|
10
|
+
The mechanics live in the `symbiote-expo-native-module` project skill.
|
|
11
11
|
|
|
12
12
|
Nothing is ever sent on the user's behalf. Both platforms open their own composer with the draft
|
|
13
13
|
filled in; the user presses send, edits, or discards it.
|
|
@@ -26,35 +26,35 @@ npx @symbiote-native/cli new my-app --sms
|
|
|
26
26
|
npx @symbiote-native/cli add --sms
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Either way: installs `@symbiote-native/sms` and wires the native autolinking automatically
|
|
29
|
+
Either way: installs `@symbiote-native/sms` and wires the native autolinking automatically - see
|
|
30
30
|
[`@symbiote-native/cli`](../cli).
|
|
31
31
|
|
|
32
32
|
<details>
|
|
33
|
-
<summary>Manual install (no CLI
|
|
33
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
36
|
npm install @symbiote-native/sms
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
`expo-sms` and `expo-modules-core` come along as regular, pinned dependencies
|
|
39
|
+
`expo-sms` and `expo-modules-core` come along as regular, pinned dependencies - never install
|
|
40
40
|
either yourself, and never add the `expo` meta-package to this project (it bundles its own
|
|
41
41
|
Metro/Babel pipeline that conflicts with this project's own).
|
|
42
42
|
|
|
43
43
|
### Required one-time step: native autolinking wiring
|
|
44
44
|
|
|
45
45
|
Unlike a plain RN native module, `expo-sms`'s native code is discovered by
|
|
46
|
-
`expo-modules-autolinking`
|
|
46
|
+
`expo-modules-autolinking` - this needs wiring into the native host app **once**, covering this
|
|
47
47
|
package and every other `expo-modules-core` package with zero further changes:
|
|
48
48
|
|
|
49
49
|
| Platform | Touches |
|
|
50
50
|
| -------- | ------------------------------------------------------------------------------------- |
|
|
51
|
-
| iOS | `ios/Podfile`
|
|
52
|
-
| iOS | `AppDelegate.swift`
|
|
53
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
54
|
-
| Android | `MainApplication.kt`
|
|
51
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
52
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
53
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
54
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a native-module name map |
|
|
55
55
|
|
|
56
56
|
Full mechanics live in the `symbiote-expo-native-module` skill. The per-package half of that
|
|
57
|
-
table
|
|
57
|
+
table - the Gradle dependency and the module map entry - is generated by
|
|
58
58
|
[`@symbiote-native/expo-modules-link`](../expo-modules-link) from this package's
|
|
59
59
|
`native-link.json` on every install.
|
|
60
60
|
|
|
@@ -63,7 +63,7 @@ table — the Gradle dependency and the module map entry — is generated by
|
|
|
63
63
|
This is the least invasive of the Expo wrappers here:
|
|
64
64
|
|
|
65
65
|
- **No runtime permission on either platform.** Opening the composer is an intent
|
|
66
|
-
(`ACTION_SENDTO`/`ACTION_SEND`) on Android and an `MFMessageComposeViewController` on iOS
|
|
66
|
+
(`ACTION_SENDTO`/`ACTION_SEND`) on Android and an `MFMessageComposeViewController` on iOS -
|
|
67
67
|
the user's own app does the sending, so neither platform asks for one. `expo-sms` requests no
|
|
68
68
|
permission anywhere in its native code, and reading the _outcome_ of a sent message on Android
|
|
69
69
|
would need `READ_SMS`, which is exactly why it doesn't try (see the `unknown` result below).
|
|
@@ -83,9 +83,9 @@ src/core/ the whole API: sendSMSAsync + isAvailableAsync. native
|
|
|
83
83
|
src/angular/ @symbiote-native/sms/angular
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
-
`./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto `src/core/`
|
|
87
|
-
no physical per-framework file. Both exports are stateless free functions
|
|
88
|
-
once the composer closes and holds nothing afterwards, and there is no event stream
|
|
86
|
+
`./react`, `./vue`, `./svelte`, and `./solid` are `exports`-map aliases straight onto `src/core/` -
|
|
87
|
+
no physical per-framework file. Both exports are stateless free functions - `sendSMSAsync` resolves
|
|
88
|
+
once the composer closes and holds nothing afterwards, and there is no event stream - so there is
|
|
89
89
|
nothing for a hook, composable, or service to wrap, the same reason
|
|
90
90
|
[`@symbiote-native/secure-store`](../secure-store) does the same. `./angular` stays a physical
|
|
91
91
|
file/subpath since Angular ships through a separate `ngc`/AOT build (`build-ngc/`). Import from
|
|
@@ -106,7 +106,7 @@ if (await isAvailableAsync()) {
|
|
|
106
106
|
}
|
|
107
107
|
```
|
|
108
108
|
|
|
109
|
-
A single recipient may be passed as a bare string
|
|
109
|
+
A single recipient may be passed as a bare string - it is normalised into an array before the
|
|
110
110
|
native call:
|
|
111
111
|
|
|
112
112
|
```ts
|
|
@@ -125,7 +125,7 @@ await sendSMSAsync('0123456789', 'Here is the receipt', {
|
|
|
125
125
|
});
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
The `uri` has to be a **content** URI
|
|
128
|
+
The `uri` has to be a **content** URI - the composer runs in another app's process, and a plain
|
|
129
129
|
file path is not readable from there.
|
|
130
130
|
|
|
131
131
|
## API
|
|
@@ -135,12 +135,14 @@ file path is not readable from there.
|
|
|
135
135
|
| `isAvailableAsync` | `() => Promise<boolean>` | `false` on the iOS simulator, which has no Messages app, and on Android devices without telephony hardware. |
|
|
136
136
|
| `sendSMSAsync` | `(addresses, message, options?) => Promise<ISmsResponse>` | Opens the composer. Resolves when it closes. Throws if the device has no messaging app. |
|
|
137
137
|
|
|
138
|
-
`ISmsOptions`: `attachments`
|
|
138
|
+
`ISmsOptions`: `attachments` - one `ISmsAttachment` or a list of them.
|
|
139
139
|
`ISmsAttachment`: `uri` (content URI), `mimeType`, `filename`.
|
|
140
140
|
`ISmsResponse`: `{ result: 'sent' | 'cancelled' | 'unknown' }`.
|
|
141
141
|
|
|
142
142
|
## Notes
|
|
143
143
|
|
|
144
|
+
- **Check `isAvailableAsync()` first.** It resolves `false` on devices that cannot send text, such as
|
|
145
|
+
an iOS simulator or a tablet without telephony, and `sendSMSAsync` rejects there.
|
|
144
146
|
- **Android always resolves `unknown`.** The only way to learn whether a message actually left
|
|
145
147
|
the device is to query the SMS database, which needs the `READ_SMS` permission Google restricts
|
|
146
148
|
to default-SMS-app publishers. Treat `unknown` as "the composer closed", not as a failure.
|
|
@@ -150,12 +152,25 @@ file path is not readable from there.
|
|
|
150
152
|
- **`sendSMSAsync` can reject.** Android throws when no messaging application is installed; iOS
|
|
151
153
|
throws when the device cannot send text at all, or when a composer is already open.
|
|
152
154
|
|
|
155
|
+
## Common questions
|
|
156
|
+
|
|
157
|
+
- **`isAvailableAsync()` is `false` on the iOS Simulator.** Expected: it cannot send text.
|
|
158
|
+
- **Android: "No messaging application available".** No installed app handled the intent; the
|
|
159
|
+
package ships the Android 11 `<queries>` entries, so check a messaging app exists.
|
|
160
|
+
- **Silent send?** No: the user presses send in the system composer.
|
|
161
|
+
- **Result is not `sent` on Android.** It always resolves `unknown`.
|
|
162
|
+
- **One attachment arrives on Android.** The composer takes one; iOS attaches all.
|
|
163
|
+
|
|
164
|
+
Sources: [Expo docs: SMS](https://docs.expo.dev/versions/latest/sdk/sms/),
|
|
165
|
+
[expo/expo#13277](https://github.com/expo/expo/issues/13277),
|
|
166
|
+
[expo/expo#2384](https://github.com/expo/expo/issues/2384).
|
|
167
|
+
|
|
153
168
|
## Test it
|
|
154
169
|
|
|
155
170
|
```bash
|
|
156
171
|
pnpm vitest run packages/sms
|
|
157
172
|
```
|
|
158
173
|
|
|
159
|
-
The core tests fake the native module in place of `requireNativeModule`'s runtime resolution
|
|
174
|
+
The core tests fake the native module in place of `requireNativeModule`'s runtime resolution -
|
|
160
175
|
`ExpoSMS` only exists on a device, so a headless run would otherwise throw at import. The
|
|
161
176
|
composer itself, and the `sent`/`cancelled` distinction, can only be verified on a device.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/sms",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "expo-sms wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Opens the system SMS composer prefilled with recipients and a message.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"expo-sms": "57.0.1",
|
|
73
|
-
"expo-modules-core": "57.0.
|
|
73
|
+
"expo-modules-core": "57.0.20"
|
|
74
74
|
},
|
|
75
75
|
"peerDependencies": {
|
|
76
76
|
"@angular/core": ">=20",
|
|
@@ -80,12 +80,12 @@
|
|
|
80
80
|
"solid-js": ">=1.9.0",
|
|
81
81
|
"svelte": ">=5.56.0",
|
|
82
82
|
"vue": ">=3.5.0",
|
|
83
|
-
"@symbiote-native/angular": "^3.
|
|
84
|
-
"@symbiote-native/engine": "^1.
|
|
85
|
-
"@symbiote-native/react": "^3.0
|
|
86
|
-
"@symbiote-native/solid": "^3.0
|
|
87
|
-
"@symbiote-native/svelte": "^3.0
|
|
88
|
-
"@symbiote-native/vue": "^3.0
|
|
83
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
84
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
85
|
+
"@symbiote-native/react": "^3.2.0",
|
|
86
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
87
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
88
|
+
"@symbiote-native/vue": "^3.2.0"
|
|
89
89
|
},
|
|
90
90
|
"peerDependenciesMeta": {
|
|
91
91
|
"@symbiote-native/angular": {
|
|
@@ -133,13 +133,13 @@
|
|
|
133
133
|
"solid-js": "^1.9.14",
|
|
134
134
|
"svelte": "^5.56.0",
|
|
135
135
|
"typescript": "~6.0.0",
|
|
136
|
-
"@symbiote-native/angular": "3.
|
|
137
|
-
"@symbiote-native/engine": "1.
|
|
138
|
-
"@symbiote-native/react": "3.0
|
|
139
|
-
"@symbiote-native/solid": "3.0
|
|
140
|
-
"@symbiote-native/svelte": "3.0
|
|
141
|
-
"@symbiote-native/test-utils": "0.4.
|
|
142
|
-
"@symbiote-native/vue": "3.0
|
|
136
|
+
"@symbiote-native/angular": "3.2.0",
|
|
137
|
+
"@symbiote-native/engine": "1.5.0",
|
|
138
|
+
"@symbiote-native/react": "3.2.0",
|
|
139
|
+
"@symbiote-native/solid": "3.1.0",
|
|
140
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
141
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
142
|
+
"@symbiote-native/vue": "3.2.0"
|
|
143
143
|
},
|
|
144
144
|
"scripts": {
|
|
145
145
|
"typecheck": "tsc --build",
|