@wayq/beekon-rn 0.1.0 → 0.1.2
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/CHANGELOG.md +41 -0
- package/README.md +28 -2
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +10 -0
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +105 -1
- package/ios/BeekonRn.swift +60 -10
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +5472 -2309
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +87 -3
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +5472 -2309
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +87 -3
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +5472 -2309
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +87 -3
- package/lib/module/NativeBeekonRn.js +8 -0
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +14 -1
- package/lib/module/beekon.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/mappers.js +83 -2
- package/lib/module/internal/mappers.js.map +1 -1
- package/lib/typescript/src/NativeBeekonRn.d.ts +20 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +11 -1
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +2 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/mappers.d.ts +3 -2
- package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
- package/lib/typescript/src/types/geofence.d.ts +37 -0
- package/lib/typescript/src/types/geofence.d.ts.map +1 -1
- package/lib/typescript/src/types/permission.d.ts +36 -0
- package/lib/typescript/src/types/permission.d.ts.map +1 -1
- package/package.json +1 -1
- package/scripts/fetch-beekonkit.sh +4 -4
- package/src/NativeBeekonRn.ts +24 -0
- package/src/beekon.ts +20 -1
- package/src/index.tsx +7 -0
- package/src/internal/mappers.ts +114 -0
- package/src/types/geofence.ts +41 -0
- package/src/types/permission.ts +43 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,47 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
Beekon is pre-1.0 — releases may contain breaking changes.
|
|
8
8
|
|
|
9
|
+
## [0.1.2] - 2026-06-28
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- **Per-geofence local notifications.** A `BeekonGeofence` may now carry an
|
|
14
|
+
optional `notification` (`GeofenceNotification`) that the SDK renders natively
|
|
15
|
+
at crossing time — offline and even when the app is killed. It takes a
|
|
16
|
+
required `delivery` (`'local'` is the only implemented mode; `'cloud'` is
|
|
17
|
+
reserved) plus optional `onEnter` / `onExit` `NotificationContent` (`title`
|
|
18
|
+
and `body` required; optional `importance` defaulting to `'high'`, `deepLink`,
|
|
19
|
+
and a flat `data` string map). `onEnter` requires `notifyOnEntry` and `onExit`
|
|
20
|
+
requires `notifyOnExit`; in self-managed mode `delivery` must be `'local'`.
|
|
21
|
+
Adds the `GeofenceNotification`, `NotificationContent`, `NotificationDelivery`,
|
|
22
|
+
and `NotificationImportance` types. Requires the native SDKs ≥ 0.1.2.
|
|
23
|
+
- **`getRequiredPermissions()` — config-aware permission doctor.** Resolves the
|
|
24
|
+
OS permissions the *current configuration* needs as a
|
|
25
|
+
`PermissionRequirement[]`, each marked `satisfied` against the live grant —
|
|
26
|
+
the companion to the location-only `getPermissionStatus()` snapshot. Each
|
|
27
|
+
entry carries `permission` (`BeekonPermission`: `location` /
|
|
28
|
+
`backgroundLocation` / `activityRecognition` / `notifications`), `importance`
|
|
29
|
+
(`required` / `recommended`), `satisfied`, and a human-readable `rationale`,
|
|
30
|
+
reporting activity-recognition and, on Android, notifications too. Read-only;
|
|
31
|
+
never prompts — Beekon still never *requests* permission. Call after
|
|
32
|
+
`configure()`; in cloud mode it returns the conservative list. Adds the
|
|
33
|
+
`PermissionRequirement` and `PermissionImportance` types. Requires the native
|
|
34
|
+
SDKs ≥ 0.1.2.
|
|
35
|
+
|
|
36
|
+
## [0.1.1] - 2026-06-23
|
|
37
|
+
|
|
38
|
+
### Changed
|
|
39
|
+
|
|
40
|
+
- **Native SDK pins bumped to 0.1.1** — Android Maven coordinate and iOS
|
|
41
|
+
xcframework (`fetch-beekonkit.sh` `VERSION` + `EXPECTED_SHA`) now track
|
|
42
|
+
BeekonKit / `beekon` 0.1.1 (device control plane, build-time license gate,
|
|
43
|
+
and related native fixes since 0.1.0).
|
|
44
|
+
|
|
45
|
+
### Added
|
|
46
|
+
|
|
47
|
+
- **Build-gate product id** — native registration now declares the React Native
|
|
48
|
+
wrapper product id for build-time license enforcement (build-gate-v1).
|
|
49
|
+
|
|
9
50
|
## [0.1.0] - 2026-06-15
|
|
10
51
|
|
|
11
52
|
### Changed
|
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ Capture location in the foreground and background, keep a queryable history on t
|
|
|
30
30
|
- Foreground and background tracking that survives app termination
|
|
31
31
|
- Battery-aware capture: time/distance filtering, accuracy presets, auto-pause while stationary
|
|
32
32
|
- On-device history you can query at any time
|
|
33
|
-
- Geofencing with enter and exit events
|
|
33
|
+
- Geofencing with enter and exit events, plus optional per-geofence local notifications that fire offline and even when the app is killed
|
|
34
34
|
- Optional batched server sync with automatic retry
|
|
35
35
|
- Diagnostic logging: query/export an on-device buffer, stream live entries
|
|
36
36
|
- Activity detection (walking, running, cycling, automotive)
|
|
@@ -107,12 +107,38 @@ await Beekon.addGeofences([
|
|
|
107
107
|
]);
|
|
108
108
|
const offGeofence = Beekon.onGeofenceEvent((e) => console.log(e.geofenceId, e.type));
|
|
109
109
|
|
|
110
|
+
// Per-geofence local notification — rendered natively at crossing time,
|
|
111
|
+
// offline and even when the app is killed. Add `notification` to a geofence:
|
|
112
|
+
await Beekon.addGeofences([
|
|
113
|
+
{
|
|
114
|
+
id: 'home',
|
|
115
|
+
latitude: 37.331,
|
|
116
|
+
longitude: -122.031,
|
|
117
|
+
radiusMeters: 150,
|
|
118
|
+
notification: {
|
|
119
|
+
delivery: 'local', // 'local' is the only implemented mode ('cloud' reserved)
|
|
120
|
+
onEnter: { title: 'Welcome home', body: 'You arrived', deepLink: 'myapp://home' },
|
|
121
|
+
onExit: { title: 'On the move', body: 'You left home', importance: 'default' },
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
]);
|
|
125
|
+
|
|
110
126
|
// One-shot fix, independent of tracking (null on timeout)
|
|
111
127
|
const fix = await Beekon.getCurrentLocation();
|
|
112
128
|
|
|
113
129
|
// Pre-start permission check (the app still owns the request)
|
|
114
130
|
const status = await Beekon.getPermissionStatus();
|
|
115
131
|
|
|
132
|
+
// Config-aware "permission doctor" — the OS permissions the CURRENT config
|
|
133
|
+
// needs, each marked `satisfied` against the live grant (never prompts).
|
|
134
|
+
// Call after configure(). Reports activity-recognition and, on Android,
|
|
135
|
+
// notifications — not just location.
|
|
136
|
+
const required = await Beekon.getRequiredPermissions();
|
|
137
|
+
for (const r of required) {
|
|
138
|
+
// r.permission, r.importance ('required' | 'recommended'), r.satisfied, r.rationale
|
|
139
|
+
if (!r.satisfied) console.log(`${r.permission} (${r.importance}): ${r.rationale}`);
|
|
140
|
+
}
|
|
141
|
+
|
|
116
142
|
// Diagnostics — runtime level, live tail, NDJSON export for bug reports
|
|
117
143
|
await Beekon.setLogLevel('debug');
|
|
118
144
|
const logPath = await Beekon.exportLog();
|
|
@@ -180,7 +206,7 @@ func application(
|
|
|
180
206
|
|
|
181
207
|
## API at a glance
|
|
182
208
|
|
|
183
|
-
Methods: `configure` · `start` · `stop` · `resumeIfNeeded` · `getCurrentLocation` · `getLocations` · `deleteLocations` · `pendingUploadCount` · `sync` · `setExtras` · `addGeofences` · `removeGeofences` · `listGeofences` · `getPermissionStatus` · `licenseStatus` · `getLog` · `exportLog` · `clearLog` · `setLogLevel` · `log`.
|
|
209
|
+
Methods: `configure` · `start` · `stop` · `resumeIfNeeded` · `getCurrentLocation` · `getLocations` · `deleteLocations` · `pendingUploadCount` · `sync` · `setExtras` · `addGeofences` · `removeGeofences` · `listGeofences` · `getPermissionStatus` · `getRequiredPermissions` · `licenseStatus` · `getLog` · `exportLog` · `clearLog` · `setLogLevel` · `log`.
|
|
184
210
|
|
|
185
211
|
Subscriptions (each returns an unsubscribe function): `onState` · `onLocation` · `onGeofenceEvent` · `onSyncStatus` · `onAuthTokens` · `onLicenseStatus` · `onLog`.
|
|
186
212
|
|
package/android/build.gradle
CHANGED
|
@@ -79,7 +79,7 @@ dependencies {
|
|
|
79
79
|
// the SDK reaches v1 stability. 0.0.7 is the release that ships the license
|
|
80
80
|
// API (setWrapperInfo / BeekonConfig.licenseKey / Beekon.licenseStatus);
|
|
81
81
|
// 0.0.8 embeds the production ES256 verification keyset.
|
|
82
|
-
implementation "io.github.beekonlabs:beekon:0.1.
|
|
82
|
+
implementation "io.github.beekonlabs:beekon:0.1.2"
|
|
83
83
|
// Kotlin coroutines — required for collecting Beekon's StateFlow/SharedFlow.
|
|
84
84
|
// Beekon already depends on coroutines transitively, but declaring it here
|
|
85
85
|
// makes the dependency intent explicit.
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
2
|
+
<!--
|
|
3
|
+
Declares the build-time license-gate product id (build-gate-v1 §6): a static
|
|
4
|
+
<meta-data> that manifest-merges into the host so the gate validates against
|
|
5
|
+
the `rn` product, mirroring the runtime setWrapperInfo("rn", …). No runtime effect.
|
|
6
|
+
-->
|
|
7
|
+
<application>
|
|
8
|
+
<meta-data
|
|
9
|
+
android:name="in.wayq.beekon.product"
|
|
10
|
+
android:value="rn" />
|
|
11
|
+
</application>
|
|
2
12
|
</manifest>
|
|
@@ -18,8 +18,10 @@ import `in`.wayq.beekon.Beekon
|
|
|
18
18
|
import `in`.wayq.beekon.BeekonConfig
|
|
19
19
|
import `in`.wayq.beekon.BeekonException
|
|
20
20
|
import `in`.wayq.beekon.BeekonGeofence
|
|
21
|
+
import `in`.wayq.beekon.BeekonPermission
|
|
21
22
|
import `in`.wayq.beekon.BeekonState
|
|
22
23
|
import `in`.wayq.beekon.GeofenceEvent
|
|
24
|
+
import `in`.wayq.beekon.GeofenceNotification
|
|
23
25
|
import `in`.wayq.beekon.LicenseStatus
|
|
24
26
|
import `in`.wayq.beekon.Location
|
|
25
27
|
import `in`.wayq.beekon.LocationQuality
|
|
@@ -29,6 +31,11 @@ import `in`.wayq.beekon.LogEntry
|
|
|
29
31
|
import `in`.wayq.beekon.LogLevel
|
|
30
32
|
import `in`.wayq.beekon.MotionState
|
|
31
33
|
import `in`.wayq.beekon.NotificationConfig
|
|
34
|
+
import `in`.wayq.beekon.NotificationContent
|
|
35
|
+
import `in`.wayq.beekon.NotificationDelivery
|
|
36
|
+
import `in`.wayq.beekon.NotificationImportance
|
|
37
|
+
import `in`.wayq.beekon.PermissionImportance
|
|
38
|
+
import `in`.wayq.beekon.PermissionRequirement
|
|
32
39
|
import `in`.wayq.beekon.PermissionStatus
|
|
33
40
|
import `in`.wayq.beekon.StationaryMode
|
|
34
41
|
import `in`.wayq.beekon.StopReason
|
|
@@ -38,6 +45,7 @@ import `in`.wayq.beekon.SyncStatus
|
|
|
38
45
|
import `in`.wayq.beekon.Transition
|
|
39
46
|
import java.time.Instant
|
|
40
47
|
import kotlinx.coroutines.CoroutineScope
|
|
48
|
+
import org.json.JSONObject
|
|
41
49
|
import kotlinx.coroutines.Dispatchers
|
|
42
50
|
import kotlinx.coroutines.SupervisorJob
|
|
43
51
|
import kotlinx.coroutines.cancel
|
|
@@ -241,6 +249,13 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
241
249
|
promise.resolve(permissionStatusToWire(Beekon.getPermissionStatus()))
|
|
242
250
|
}
|
|
243
251
|
|
|
252
|
+
override fun getRequiredPermissions(promise: Promise) {
|
|
253
|
+
// Synchronous, non-throwing read on the native SDK.
|
|
254
|
+
val arr: WritableArray = Arguments.createArray()
|
|
255
|
+
for (r in Beekon.getRequiredPermissions()) arr.pushMap(permissionRequirementToWire(r))
|
|
256
|
+
promise.resolve(arr)
|
|
257
|
+
}
|
|
258
|
+
|
|
244
259
|
// ---------------------------------------------------------------------------
|
|
245
260
|
// Diagnostic logs (spec diagnostics/log-format-v1)
|
|
246
261
|
// ---------------------------------------------------------------------------
|
|
@@ -446,6 +461,7 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
446
461
|
radiusMeters = m.getDouble("radiusMeters"),
|
|
447
462
|
notifyOnEntry = m.getBoolean("notifyOnEntry"),
|
|
448
463
|
notifyOnExit = m.getBoolean("notifyOnExit"),
|
|
464
|
+
notification = GeofenceNotificationWire.decode(m.getString("notificationJson")),
|
|
449
465
|
)
|
|
450
466
|
}
|
|
451
467
|
return out
|
|
@@ -494,6 +510,7 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
494
510
|
m.putDouble("radiusMeters", g.radiusMeters)
|
|
495
511
|
m.putBoolean("notifyOnEntry", g.notifyOnEntry)
|
|
496
512
|
m.putBoolean("notifyOnExit", g.notifyOnExit)
|
|
513
|
+
g.notification?.let { m.putString("notificationJson", GeofenceNotificationWire.encode(it)) }
|
|
497
514
|
return m
|
|
498
515
|
}
|
|
499
516
|
|
|
@@ -540,6 +557,27 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
540
557
|
PermissionStatus.Accuracy.Reduced -> "reduced"
|
|
541
558
|
}
|
|
542
559
|
|
|
560
|
+
private fun permissionRequirementToWire(r: PermissionRequirement): WritableMap {
|
|
561
|
+
val m = Arguments.createMap()
|
|
562
|
+
m.putString("permission", permissionKindToWire(r.permission))
|
|
563
|
+
m.putString("importance", permissionImportanceToWire(r.importance))
|
|
564
|
+
m.putBoolean("satisfied", r.satisfied)
|
|
565
|
+
m.putString("rationale", r.rationale)
|
|
566
|
+
return m
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
private fun permissionKindToWire(p: BeekonPermission): String = when (p) {
|
|
570
|
+
BeekonPermission.Location -> "location"
|
|
571
|
+
BeekonPermission.BackgroundLocation -> "backgroundLocation"
|
|
572
|
+
BeekonPermission.ActivityRecognition -> "activityRecognition"
|
|
573
|
+
BeekonPermission.Notifications -> "notifications"
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private fun permissionImportanceToWire(i: PermissionImportance): String = when (i) {
|
|
577
|
+
PermissionImportance.Required -> "required"
|
|
578
|
+
PermissionImportance.Recommended -> "recommended"
|
|
579
|
+
}
|
|
580
|
+
|
|
543
581
|
private fun syncStatusToWire(s: SyncStatus): WritableMap {
|
|
544
582
|
val m = Arguments.createMap()
|
|
545
583
|
when (s) {
|
|
@@ -705,6 +743,72 @@ class BeekonRnModule(reactContext: ReactApplicationContext) :
|
|
|
705
743
|
// Reported to the native verifier via setWrapperInfo (diagnostics only — the
|
|
706
744
|
// verifier consumes only the product). Keep in sync with package.json
|
|
707
745
|
// "version" on release.
|
|
708
|
-
private const val WRAPPER_VERSION = "0.1.
|
|
746
|
+
private const val WRAPPER_VERSION = "0.1.2"
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
/**
|
|
751
|
+
* Wrapper-local (de)serialization for [GeofenceNotification] in the canonical JSON shape shared
|
|
752
|
+
* with the native SDKs. The SDK's own `GeofenceNotificationJson` is `internal`, so the module
|
|
753
|
+
* cannot reuse it; this mirrors it exactly. Tolerant: unknown enums fall back to defaults and a
|
|
754
|
+
* malformed/empty payload yields null.
|
|
755
|
+
*/
|
|
756
|
+
private object GeofenceNotificationWire {
|
|
757
|
+
fun encode(n: GeofenceNotification): String {
|
|
758
|
+
val obj = JSONObject()
|
|
759
|
+
obj.put("delivery", if (n.delivery == NotificationDelivery.Cloud) "cloud" else "local")
|
|
760
|
+
n.onEnter?.let { obj.put("onEnter", contentToJson(it)) }
|
|
761
|
+
n.onExit?.let { obj.put("onExit", contentToJson(it)) }
|
|
762
|
+
return obj.toString()
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
fun decode(json: String?): GeofenceNotification? {
|
|
766
|
+
if (json.isNullOrBlank()) return null
|
|
767
|
+
return runCatching {
|
|
768
|
+
val obj = JSONObject(json)
|
|
769
|
+
val onEnter = contentFromJson(obj.optJSONObject("onEnter"))
|
|
770
|
+
val onExit = contentFromJson(obj.optJSONObject("onExit"))
|
|
771
|
+
if (onEnter == null && onExit == null) {
|
|
772
|
+
null
|
|
773
|
+
} else {
|
|
774
|
+
val delivery = if (obj.optString("delivery", "local") == "cloud") {
|
|
775
|
+
NotificationDelivery.Cloud
|
|
776
|
+
} else {
|
|
777
|
+
NotificationDelivery.Local
|
|
778
|
+
}
|
|
779
|
+
GeofenceNotification(onEnter = onEnter, onExit = onExit, delivery = delivery)
|
|
780
|
+
}
|
|
781
|
+
}.getOrNull()
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
private fun contentToJson(c: NotificationContent): JSONObject {
|
|
785
|
+
val obj = JSONObject()
|
|
786
|
+
obj.put("title", c.title)
|
|
787
|
+
obj.put("body", c.body)
|
|
788
|
+
obj.put("importance", if (c.importance == NotificationImportance.Default) "default" else "high")
|
|
789
|
+
c.deepLink?.let { obj.put("deepLink", it) }
|
|
790
|
+
if (c.data.isNotEmpty()) {
|
|
791
|
+
val data = JSONObject()
|
|
792
|
+
for ((k, v) in c.data) data.put(k, v)
|
|
793
|
+
obj.put("data", data)
|
|
794
|
+
}
|
|
795
|
+
return obj
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
private fun contentFromJson(obj: JSONObject?): NotificationContent? {
|
|
799
|
+
if (obj == null) return null
|
|
800
|
+
val title = obj.optString("title", "")
|
|
801
|
+
val body = obj.optString("body", "")
|
|
802
|
+
if (title.isEmpty() || body.isEmpty()) return null
|
|
803
|
+
val importance = if (obj.optString("importance", "high") == "default") {
|
|
804
|
+
NotificationImportance.Default
|
|
805
|
+
} else {
|
|
806
|
+
NotificationImportance.High
|
|
807
|
+
}
|
|
808
|
+
val deepLink = if (obj.has("deepLink") && !obj.isNull("deepLink")) obj.optString("deepLink") else null
|
|
809
|
+
val data = obj.optJSONObject("data")?.let { d ->
|
|
810
|
+
buildMap { for (key in d.keys()) put(key, d.optString(key)) }
|
|
811
|
+
} ?: emptyMap()
|
|
812
|
+
return NotificationContent(title = title, body = body, importance = importance, deepLink = deepLink, data = data)
|
|
709
813
|
}
|
|
710
814
|
}
|
package/ios/BeekonRn.swift
CHANGED
|
@@ -37,7 +37,7 @@ import BeekonKit
|
|
|
37
37
|
// Reported to the native verifier via setWrapperInfo (diagnostics only — the
|
|
38
38
|
// verifier consumes only the product). Keep in sync with package.json
|
|
39
39
|
// "version" on release.
|
|
40
|
-
private static let wrapperVersion = "0.1.
|
|
40
|
+
private static let wrapperVersion = "0.1.2"
|
|
41
41
|
|
|
42
42
|
@objc public init(
|
|
43
43
|
onState: @escaping (NSDictionary) -> Void,
|
|
@@ -353,6 +353,18 @@ import BeekonKit
|
|
|
353
353
|
resolve(permissionStatusToWire(Beekon.shared.getPermissionStatus()))
|
|
354
354
|
}
|
|
355
355
|
|
|
356
|
+
@objc public func getRequiredPermissionsWithResolver(
|
|
357
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
358
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
359
|
+
) {
|
|
360
|
+
// `getRequiredPermissions()` is actor-isolated (`await`).
|
|
361
|
+
Task { [weak self] in
|
|
362
|
+
guard let self = self else { return }
|
|
363
|
+
let requirements = await Beekon.shared.getRequiredPermissions()
|
|
364
|
+
resolve(requirements.map { self.permissionRequirementToWire($0) })
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
356
368
|
// MARK: - Diagnostic logs (spec diagnostics/log-format-v1)
|
|
357
369
|
|
|
358
370
|
@objc public func getLogFromMs(
|
|
@@ -504,7 +516,12 @@ import BeekonKit
|
|
|
504
516
|
longitude: lng,
|
|
505
517
|
radiusMeters: radius,
|
|
506
518
|
notifyOnEntry: (m["notifyOnEntry"] as? NSNumber)?.boolValue ?? true,
|
|
507
|
-
notifyOnExit: (m["notifyOnExit"] as? NSNumber)?.boolValue ?? true
|
|
519
|
+
notifyOnExit: (m["notifyOnExit"] as? NSNumber)?.boolValue ?? true,
|
|
520
|
+
// Native `GeofenceNotification` is Codable in the canonical shape; a malformed
|
|
521
|
+
// string degrades to nil rather than failing the add.
|
|
522
|
+
notification: (m["notificationJson"] as? String).flatMap {
|
|
523
|
+
try? JSONDecoder().decode(GeofenceNotification.self, from: Data($0.utf8))
|
|
524
|
+
}
|
|
508
525
|
)
|
|
509
526
|
)
|
|
510
527
|
}
|
|
@@ -573,14 +590,19 @@ import BeekonKit
|
|
|
573
590
|
}
|
|
574
591
|
|
|
575
592
|
private func geofenceToWire(_ g: BeekonGeofence) -> NSDictionary {
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
593
|
+
let m = NSMutableDictionary()
|
|
594
|
+
m["id"] = g.id
|
|
595
|
+
m["lat"] = g.latitude
|
|
596
|
+
m["lng"] = g.longitude
|
|
597
|
+
m["radiusMeters"] = g.radiusMeters
|
|
598
|
+
m["notifyOnEntry"] = g.notifyOnEntry
|
|
599
|
+
m["notifyOnExit"] = g.notifyOnExit
|
|
600
|
+
if let notification = g.notification,
|
|
601
|
+
let data = try? JSONEncoder().encode(notification),
|
|
602
|
+
let json = String(data: data, encoding: .utf8) {
|
|
603
|
+
m["notificationJson"] = json
|
|
604
|
+
}
|
|
605
|
+
return m
|
|
584
606
|
}
|
|
585
607
|
|
|
586
608
|
private func geofenceEventToWire(_ e: GeofenceEvent) -> NSDictionary {
|
|
@@ -630,6 +652,34 @@ import BeekonKit
|
|
|
630
652
|
}
|
|
631
653
|
}
|
|
632
654
|
|
|
655
|
+
private func permissionRequirementToWire(_ r: PermissionRequirement) -> NSDictionary {
|
|
656
|
+
return [
|
|
657
|
+
"permission": permissionKindToWire(r.permission),
|
|
658
|
+
"importance": permissionImportanceToWire(r.importance),
|
|
659
|
+
"satisfied": r.satisfied,
|
|
660
|
+
"rationale": r.rationale,
|
|
661
|
+
]
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
private func permissionKindToWire(_ p: PermissionRequirement.Permission) -> String {
|
|
665
|
+
switch p {
|
|
666
|
+
case .location: return "location"
|
|
667
|
+
case .backgroundLocation: return "backgroundLocation"
|
|
668
|
+
case .activityRecognition: return "activityRecognition"
|
|
669
|
+
case .notifications: return "notifications"
|
|
670
|
+
// BeekonKit enums are non-frozen (library-evolution binary); handle unknowns.
|
|
671
|
+
@unknown default: return "location"
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
private func permissionImportanceToWire(_ i: PermissionRequirement.Importance) -> String {
|
|
676
|
+
switch i {
|
|
677
|
+
case .required: return "required"
|
|
678
|
+
case .recommended: return "recommended"
|
|
679
|
+
@unknown default: return "recommended"
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
|
|
633
683
|
private func stateToWire(_ s: BeekonState) -> NSDictionary {
|
|
634
684
|
switch s {
|
|
635
685
|
case .idle:
|