@symbiote-native/sensors 3.0.1 → 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 +42 -31
- package/package.json +15 -15
package/README.md
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
# @symbiote-native/sensors
|
|
2
2
|
|
|
3
3
|
A wrapper package for [SymbioteNative](../../README.md) that makes
|
|
4
|
-
[`expo-sensors`](https://github.com/expo/expo/tree/main/packages/expo-sensors)
|
|
4
|
+
[`expo-sensors`](https://github.com/expo/expo/tree/main/packages/expo-sensors) - Accelerometer,
|
|
5
5
|
Barometer, DeviceMotion, Gyroscope, LightSensor, Magnetometer, MagnetometerUncalibrated, and
|
|
6
|
-
Pedometer
|
|
6
|
+
Pedometer - usable from **every** adapter, React, Vue, Svelte, Solid, and Angular. Unlike this
|
|
7
7
|
repo's other wrappers ([`@symbiote-native/slider`](../slider), [`@symbiote-native/navigation`](../navigation),
|
|
8
8
|
[`@symbiote-native/splash-screen`](../splash-screen)), `expo-sensors` isn't a plain RN native
|
|
9
|
-
module or view
|
|
9
|
+
module or view - it's built on `expo-modules-core`, so its native code autolinks straight out of
|
|
10
10
|
`node_modules` via `expo-modules-autolinking`, no proxy `react-native.config.cjs`/podspec to ship.
|
|
11
11
|
`expo-sensors`' own JS is never imported (it hard-imports the full `expo` meta-package, which this
|
|
12
|
-
project never depends on)
|
|
12
|
+
project never depends on) - every sensor's logic is hand-ported into this package's own `core/`.
|
|
13
13
|
|
|
14
14
|
## Install
|
|
15
15
|
|
|
@@ -25,41 +25,41 @@ npx @symbiote-native/cli new my-app --sensors
|
|
|
25
25
|
npx @symbiote-native/cli add --sensors
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Either way: installs `@symbiote-native/sensors` and wires the native autolinking automatically
|
|
28
|
+
Either way: installs `@symbiote-native/sensors` and wires the native autolinking automatically - see
|
|
29
29
|
[`@symbiote-native/cli`](../cli).
|
|
30
30
|
|
|
31
31
|
<details>
|
|
32
|
-
<summary>Manual install (no CLI
|
|
32
|
+
<summary>Manual install (no CLI - installing and wiring native autolinking by hand)</summary>
|
|
33
33
|
|
|
34
34
|
```bash
|
|
35
35
|
npm install @symbiote-native/sensors
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
Depends on `expo-sensors` and `expo-modules-core` directly (regular dependencies, pinned to exact
|
|
39
|
-
versions
|
|
39
|
+
versions - never a caret range, since this package's `core/` is hand-ported against one specific
|
|
40
40
|
native API shape and a newer resolve could silently drift the two apart). Never install
|
|
41
41
|
`expo-sensors` yourself, and never add the `expo` package to this project.
|
|
42
42
|
|
|
43
43
|
## Required one-time step: native autolinking wiring
|
|
44
44
|
|
|
45
45
|
Unlike a plain RN native module, `expo-sensors`' native code is discovered by
|
|
46
|
-
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism
|
|
46
|
+
`expo-modules-autolinking`, not RN's own `react-native.config.cjs` mechanism - this needs wiring
|
|
47
47
|
into the native host app **once**, covering this package and every future `expo-modules-core`
|
|
48
48
|
package with zero further changes:
|
|
49
49
|
|
|
50
50
|
| Platform | Touches |
|
|
51
51
|
| -------- | --------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
52
|
-
| iOS | `ios/Podfile`
|
|
53
|
-
| iOS | `AppDelegate.swift`
|
|
54
|
-
| Android | `settings.gradle` / `app/build.gradle`
|
|
55
|
-
| Android | `MainApplication.kt`
|
|
52
|
+
| iOS | `ios/Podfile` - add `use_expo_modules!` |
|
|
53
|
+
| iOS | `AppDelegate.swift` - Expo's runtime-bootstrap hook |
|
|
54
|
+
| Android | `settings.gradle` / `app/build.gradle` - resolve and include the Expo Gradle projects |
|
|
55
|
+
| Android | `MainApplication.kt` - Expo's bootstrap hook, plus a hand-written native-module name map (there's no `expo` meta-package here to auto-generate one) |
|
|
56
56
|
|
|
57
|
-
Full mechanics
|
|
58
|
-
peer-dependency exclusion list, per-sensor permission strings
|
|
57
|
+
Full mechanics - the Podfile pieces that normally ship inside the `expo` package, the `expo`
|
|
58
|
+
peer-dependency exclusion list, per-sensor permission strings - live in the
|
|
59
59
|
`symbiote-expo-native-module` skill. Reference implementation: `examples/expo-react/ios/Podfile`
|
|
60
60
|
and `examples/expo-react/android/app/src/main/java/com/canaryexpo/MainApplication.kt`.
|
|
61
61
|
|
|
62
|
-
Permissions ship with the native module itself
|
|
62
|
+
Permissions ship with the native module itself - nothing to reimplement, just the platform
|
|
63
63
|
permission string each sensor needs (e.g. DeviceMotion/Pedometer need `NSMotionUsageDescription`
|
|
64
64
|
on iOS).
|
|
65
65
|
|
|
@@ -70,18 +70,18 @@ on iOS).
|
|
|
70
70
|
```
|
|
71
71
|
src/core/ DeviceSensor base class + one class per sensor (Accelerometer, Barometer,
|
|
72
72
|
DeviceMotion, Gyroscope, LightSensor, Magnetometer,
|
|
73
|
-
MagnetometerUncalibrated); Pedometer is free functions instead
|
|
73
|
+
MagnetometerUncalibrated); Pedometer is free functions instead - upstream
|
|
74
74
|
has no shared instance for it. native/ resolves each sensor's native
|
|
75
75
|
module by name through expo-modules-core's requireNativeModule.
|
|
76
|
-
src/react/hooks/ @symbiote-native/sensors/react
|
|
77
|
-
src/vue/composables/ @symbiote-native/sensors/vue
|
|
78
|
-
src/svelte/runes/ @symbiote-native/sensors/svelte
|
|
79
|
-
src/solid/primitives/ @symbiote-native/sensors/solid
|
|
80
|
-
src/angular/services/ @symbiote-native/sensors/angular
|
|
76
|
+
src/react/hooks/ @symbiote-native/sensors/react - useAccelerometer, useBarometer, ...
|
|
77
|
+
src/vue/composables/ @symbiote-native/sensors/vue - useAccelerometer, useBarometer, ... (same names)
|
|
78
|
+
src/svelte/runes/ @symbiote-native/sensors/svelte - useAccelerometer, useBarometer, ... (same names)
|
|
79
|
+
src/solid/primitives/ @symbiote-native/sensors/solid - createAccelerometer, createBarometer, ...
|
|
80
|
+
src/angular/services/ @symbiote-native/sensors/angular - AccelerometerService, BarometerService, ...
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
Each adapter's hook/composable/rune/primitive/service is a thin lifecycle wrapper (subscribe on
|
|
84
|
-
mount, unsubscribe on unmount) over the same `core` singleton
|
|
84
|
+
mount, unsubscribe on unmount) over the same `core` singleton - the subscription, permission, and
|
|
85
85
|
update-interval logic is written once and shared by all of them. Solid is the one adapter that
|
|
86
86
|
renames: its ecosystem calls a composable reactive function a **primitive** and reserves `use*` for
|
|
87
87
|
consuming something that already exists, so `useAccelerometer` is `createAccelerometer` there.
|
|
@@ -121,7 +121,7 @@ const accelerometer = useAccelerometer();
|
|
|
121
121
|
```
|
|
122
122
|
|
|
123
123
|
```tsx
|
|
124
|
-
// Solid
|
|
124
|
+
// Solid - a primitive returning an Accessor, read as `accelerometer()`. `Show` must be imported
|
|
125
125
|
// explicitly (see .claude/rules/solid-descriptor-bridge.md), and hands its child an accessor.
|
|
126
126
|
import { Show } from 'solid-js';
|
|
127
127
|
import { createAccelerometer } from '@symbiote-native/sensors/solid';
|
|
@@ -149,22 +149,22 @@ export class SensorsScreen {
|
|
|
149
149
|
}
|
|
150
150
|
```
|
|
151
151
|
|
|
152
|
-
The examples above mirror the real canary demo screens, which exist for all six Expo canaries
|
|
152
|
+
The examples above mirror the real canary demo screens, which exist for all six Expo canaries:
|
|
153
153
|
`examples/expo-react/screens/SensorsScreen.tsx`, `examples/expo-vue-sfc/screens/SensorsScreen.vue`,
|
|
154
154
|
`examples/expo-vue-tsx/screens/SensorsScreen.tsx`, `examples/expo-svelte/screens/SensorsScreen.svelte`,
|
|
155
155
|
`examples/expo-solid/screens/SensorsScreen.tsx`, `examples/expo-angular/src/screens/SensorsScreen.ts`.
|
|
156
156
|
|
|
157
157
|
Every hook/composable/rune/primitive/`connect()` takes an optional `updateIntervalMs` and returns
|
|
158
|
-
`null` until the first native reading arrives
|
|
158
|
+
`null` until the first native reading arrives - check `isAvailableAsync()` separately if you need
|
|
159
159
|
to distinguish "not available on this device" from "no reading yet" (see [Notes](#notes) below).
|
|
160
160
|
|
|
161
161
|
On Solid `updateIntervalMs` accepts an **accessor** as well as a number
|
|
162
162
|
(`createAccelerometer(() => intervalMs())`). A Solid component body runs once, so a plain number is
|
|
163
163
|
read once and pins the sample rate for the primitive's whole life; pass the accessor whenever the
|
|
164
|
-
rate is derived from state and it re-applies on every change
|
|
164
|
+
rate is derived from state and it re-applies on every change - without re-subscribing, since the
|
|
165
165
|
native listener is per-sensor and unaffected by the rate.
|
|
166
166
|
|
|
167
|
-
### Pedometer
|
|
167
|
+
### Pedometer - free functions, no shared instance
|
|
168
168
|
|
|
169
169
|
```ts
|
|
170
170
|
import {
|
|
@@ -186,14 +186,25 @@ const { steps } = await getStepCountAsync(startDate, endDate); // one-shot, iOS
|
|
|
186
186
|
rather than returning frozen zeros. Neither is a wiring bug; verify on a real device.
|
|
187
187
|
- **`DeviceMotion.rotation`/`.acceleration`/`.accelerationIncludingGravity`/`.rotationRate` are
|
|
188
188
|
nested and may be absent from the very first event** (the underlying sensor hasn't reported yet)
|
|
189
|
-
|
|
190
|
-
- **`rotation.beta` can read `NaN` near pitch ±90°**
|
|
189
|
+
- guard the nested field itself (`deviceMotion?.rotation && ...`), not just the top-level object.
|
|
190
|
+
- **`rotation.beta` can read `NaN` near pitch ±90°** - an inherent Euler-angle gimbal-lock
|
|
191
191
|
singularity in `CMAttitude`/the platform's device-orientation math (the same one
|
|
192
192
|
`DeviceOrientationEvent.beta` has on the web), not a bug in this package.
|
|
193
193
|
|
|
194
|
+
## Common questions
|
|
195
|
+
|
|
196
|
+
- **No data in a simulator.** Sensors need a physical device.
|
|
197
|
+
- **iOS DeviceMotion or Pedometer fails.** `NSMotionUsageDescription` must be in Info.plist.
|
|
198
|
+
- **200 Hz cap on Android 12+.** Add `HIGH_SAMPLING_RATE_SENSORS` to AndroidManifest.xml.
|
|
199
|
+
- **Change the rate?** `setUpdateInterval` with milliseconds.
|
|
200
|
+
|
|
201
|
+
Sources: [Expo docs: Accelerometer](https://docs.expo.dev/versions/latest/sdk/accelerometer/),
|
|
202
|
+
[Expo docs: DeviceMotion](https://docs.expo.dev/versions/latest/sdk/devicemotion/),
|
|
203
|
+
[expo/expo#12501](https://github.com/expo/expo/pull/12501).
|
|
204
|
+
|
|
194
205
|
## Test it
|
|
195
206
|
|
|
196
|
-
No Fabric/Descriptor angle at all
|
|
207
|
+
No Fabric/Descriptor angle at all - a sensor is a pure `EventEmitter` + async-function surface,
|
|
197
208
|
never a view. Tests inject a fake native-module object in place of the real
|
|
198
209
|
`requireNativeModule` resolution (`src/core/**/*.test.ts`, `src/{react,vue,svelte,solid,angular}/**/*.test.{ts,tsx}`),
|
|
199
|
-
the same pattern `expo-sensors` itself uses upstream
|
|
210
|
+
the same pattern `expo-sensors` itself uses upstream - no `installFabric()`, no ViewConfig.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@symbiote-native/sensors",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "expo-sensors wrapped for SymbioteNative — one framework-agnostic core, built once and reachable from the React, Vue, Svelte, Solid, and Angular adapters. Accelerometer, Barometer, DeviceMotion, Gyroscope, LightSensor, Magnetometer, MagnetometerUncalibrated, Pedometer.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"keywords": [
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
},
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"expo-sensors": "57.0.2",
|
|
74
|
-
"expo-modules-core": "57.0.
|
|
74
|
+
"expo-modules-core": "57.0.20"
|
|
75
75
|
},
|
|
76
76
|
"peerDependencies": {
|
|
77
77
|
"@angular/core": ">=20",
|
|
@@ -81,12 +81,12 @@
|
|
|
81
81
|
"svelte": ">=5.56.0",
|
|
82
82
|
"solid-js": ">=1.9.0",
|
|
83
83
|
"vue": ">=3.5.0",
|
|
84
|
-
"@symbiote-native/angular": "^3.
|
|
85
|
-
"@symbiote-native/engine": "^1.
|
|
86
|
-
"@symbiote-native/react": "^3.0
|
|
87
|
-
"@symbiote-native/solid": "^3.0
|
|
88
|
-
"@symbiote-native/svelte": "^3.0
|
|
89
|
-
"@symbiote-native/vue": "^3.0
|
|
84
|
+
"@symbiote-native/angular": "^3.2.0",
|
|
85
|
+
"@symbiote-native/engine": "^1.5.0",
|
|
86
|
+
"@symbiote-native/react": "^3.2.0",
|
|
87
|
+
"@symbiote-native/solid": "^3.1.0",
|
|
88
|
+
"@symbiote-native/svelte": "^3.1.0",
|
|
89
|
+
"@symbiote-native/vue": "^3.2.0"
|
|
90
90
|
},
|
|
91
91
|
"peerDependenciesMeta": {
|
|
92
92
|
"@symbiote-native/angular": {
|
|
@@ -134,13 +134,13 @@
|
|
|
134
134
|
"solid-js": "^1.9.14",
|
|
135
135
|
"svelte": "^5.56.0",
|
|
136
136
|
"typescript": "~6.0.0",
|
|
137
|
-
"@symbiote-native/angular": "3.
|
|
138
|
-
"@symbiote-native/engine": "1.
|
|
139
|
-
"@symbiote-native/react": "3.0
|
|
140
|
-
"@symbiote-native/solid": "3.0
|
|
141
|
-
"@symbiote-native/svelte": "3.0
|
|
142
|
-
"@symbiote-native/test-utils": "0.4.
|
|
143
|
-
"@symbiote-native/vue": "3.0
|
|
137
|
+
"@symbiote-native/angular": "3.2.0",
|
|
138
|
+
"@symbiote-native/engine": "1.5.0",
|
|
139
|
+
"@symbiote-native/react": "3.2.0",
|
|
140
|
+
"@symbiote-native/solid": "3.1.0",
|
|
141
|
+
"@symbiote-native/svelte": "3.1.0",
|
|
142
|
+
"@symbiote-native/test-utils": "0.4.6",
|
|
143
|
+
"@symbiote-native/vue": "3.2.0"
|
|
144
144
|
},
|
|
145
145
|
"scripts": {
|
|
146
146
|
"typecheck": "tsc --build",
|