@wayq/beekon-rn 0.0.3 → 0.0.5

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.
Files changed (77) hide show
  1. package/BeekonRn.podspec +1 -1
  2. package/README.md +91 -39
  3. package/android/build.gradle +9 -4
  4. package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +306 -60
  5. package/ios/BeekonRn.mm +90 -24
  6. package/ios/BeekonRn.swift +360 -60
  7. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
  8. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Info.plist +0 -0
  9. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +7521 -1312
  10. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftdoc +0 -0
  11. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.swiftinterface +191 -40
  12. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/BeekonKit +0 -0
  13. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Info.plist +0 -0
  14. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +7521 -1312
  15. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftdoc +0 -0
  16. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.swiftinterface +191 -40
  17. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +7521 -1312
  18. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftdoc +0 -0
  19. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.swiftinterface +191 -40
  20. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +2 -80
  21. package/lib/module/NativeBeekonRn.js +22 -7
  22. package/lib/module/NativeBeekonRn.js.map +1 -1
  23. package/lib/module/beekon.js +198 -46
  24. package/lib/module/beekon.js.map +1 -1
  25. package/lib/module/index.js.map +1 -1
  26. package/lib/module/internal/mappers.js +122 -28
  27. package/lib/module/internal/mappers.js.map +1 -1
  28. package/lib/module/types/config.js +2 -0
  29. package/lib/module/types/enums.js +2 -0
  30. package/lib/module/types/enums.js.map +1 -0
  31. package/lib/module/types/error.js +10 -4
  32. package/lib/module/types/error.js.map +1 -1
  33. package/lib/module/types/geofence.js +2 -0
  34. package/lib/module/types/geofence.js.map +1 -0
  35. package/lib/module/types/location.js +2 -0
  36. package/lib/module/types/sync.js +2 -0
  37. package/lib/module/types/sync.js.map +1 -0
  38. package/lib/typescript/src/NativeBeekonRn.d.ts +102 -20
  39. package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
  40. package/lib/typescript/src/beekon.d.ts +81 -33
  41. package/lib/typescript/src/beekon.d.ts.map +1 -1
  42. package/lib/typescript/src/index.d.ts +5 -2
  43. package/lib/typescript/src/index.d.ts.map +1 -1
  44. package/lib/typescript/src/internal/mappers.d.ts +12 -6
  45. package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
  46. package/lib/typescript/src/types/config.d.ts +50 -20
  47. package/lib/typescript/src/types/config.d.ts.map +1 -1
  48. package/lib/typescript/src/types/enums.d.ts +48 -0
  49. package/lib/typescript/src/types/enums.d.ts.map +1 -0
  50. package/lib/typescript/src/types/error.d.ts +11 -5
  51. package/lib/typescript/src/types/error.d.ts.map +1 -1
  52. package/lib/typescript/src/types/geofence.d.ts +36 -0
  53. package/lib/typescript/src/types/geofence.d.ts.map +1 -0
  54. package/lib/typescript/src/types/location.d.ts +22 -8
  55. package/lib/typescript/src/types/location.d.ts.map +1 -1
  56. package/lib/typescript/src/types/state.d.ts +13 -4
  57. package/lib/typescript/src/types/state.d.ts.map +1 -1
  58. package/lib/typescript/src/types/sync.d.ts +27 -0
  59. package/lib/typescript/src/types/sync.d.ts.map +1 -0
  60. package/package.json +4 -5
  61. package/scripts/fetch-beekonkit.sh +5 -5
  62. package/src/NativeBeekonRn.ts +110 -20
  63. package/src/beekon.ts +219 -49
  64. package/src/index.tsx +21 -2
  65. package/src/internal/mappers.ts +187 -30
  66. package/src/types/config.ts +52 -20
  67. package/src/types/enums.ts +64 -0
  68. package/src/types/error.ts +11 -8
  69. package/src/types/geofence.ts +37 -0
  70. package/src/types/location.ts +28 -8
  71. package/src/types/state.ts +13 -3
  72. package/src/types/sync.ts +23 -0
  73. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeDirectory +0 -0
  74. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeRequirements +0 -0
  75. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeResources +0 -296
  76. package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeSignature +0 -0
  77. package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/_CodeSignature/CodeResources +0 -146
@@ -3,54 +3,65 @@ package `in`.wayq.beekonrn
3
3
  import com.facebook.react.bridge.Arguments
4
4
  import com.facebook.react.bridge.Promise
5
5
  import com.facebook.react.bridge.ReactApplicationContext
6
+ import com.facebook.react.bridge.ReadableArray
6
7
  import com.facebook.react.bridge.ReadableMap
7
8
  import com.facebook.react.bridge.WritableArray
8
9
  import com.facebook.react.bridge.WritableMap
10
+ import `in`.wayq.beekon.AccuracyMode
11
+ import `in`.wayq.beekon.ActivityType
9
12
  import `in`.wayq.beekon.Beekon
10
13
  import `in`.wayq.beekon.BeekonConfig
11
- import `in`.wayq.beekon.BeekonError
14
+ import `in`.wayq.beekon.BeekonException
15
+ import `in`.wayq.beekon.BeekonGeofence
12
16
  import `in`.wayq.beekon.BeekonState
17
+ import `in`.wayq.beekon.GeofenceEvent
13
18
  import `in`.wayq.beekon.Location
19
+ import `in`.wayq.beekon.LocationQuality
20
+ import `in`.wayq.beekon.LocationTrigger
21
+ import `in`.wayq.beekon.MotionState
22
+ import `in`.wayq.beekon.NotificationConfig
23
+ import `in`.wayq.beekon.StationaryMode
14
24
  import `in`.wayq.beekon.StopReason
25
+ import `in`.wayq.beekon.SyncConfig
26
+ import `in`.wayq.beekon.SyncFailure
27
+ import `in`.wayq.beekon.SyncStatus
28
+ import `in`.wayq.beekon.Transition
15
29
  import java.time.Instant
16
30
  import kotlinx.coroutines.CoroutineScope
17
31
  import kotlinx.coroutines.Dispatchers
18
- import kotlinx.coroutines.Job
19
32
  import kotlinx.coroutines.SupervisorJob
20
33
  import kotlinx.coroutines.cancel
21
34
  import kotlinx.coroutines.launch
22
35
 
23
- class BeekonRnModule(private val reactContext: ReactApplicationContext) :
36
+ class BeekonRnModule(reactContext: ReactApplicationContext) :
24
37
  NativeBeekonRnSpec(reactContext) {
25
38
 
26
39
  // Default dispatcher (not Main.immediate) — Codegen's emitOnX is thread-safe
27
- // and marshals to JS internally, so collecting on Main buys nothing and
28
- // risks jank if any mapper does work. SupervisorJob so a single failure
29
- // doesn't tear down siblings.
40
+ // and marshals to JS internally, so collecting on Main buys nothing and risks
41
+ // jank if a mapper does work. SupervisorJob so one failing collector doesn't
42
+ // tear down its siblings.
30
43
  private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
31
- private var stateJob: Job? = null
32
- private var locationsJob: Job? = null
33
44
 
34
45
  init {
35
- // Beekon.initialize is idempotent and does not start tracking. Safe to call
36
- // from module construction the application context is already alive.
37
- Beekon.initialize(reactContext.applicationContext)
38
- stateJob = scope.launch {
39
- Beekon.state.collect { s -> emitOnState(stateToWire(s)) }
40
- }
41
- locationsJob = scope.launch {
42
- Beekon.locations.collect { loc -> emitOnLocation(locationToWire(loc)) }
46
+ // No Beekon.initialize() the SDK auto-initializes via AndroidX Startup.
47
+ scope.launch { Beekon.state.collect { emitOnState(stateToWire(it)) } }
48
+ scope.launch { Beekon.locations.collect { emitOnLocation(locationToWire(it)) } }
49
+ scope.launch {
50
+ Beekon.geofenceEvents.collect { emitOnGeofenceEvent(geofenceEventToWire(it)) }
43
51
  }
52
+ scope.launch { Beekon.syncStatus.collect { emitOnSyncStatus(syncStatusToWire(it)) } }
44
53
  }
45
54
 
55
+ // ---------------------------------------------------------------------------
56
+ // Lifecycle (non-suspend native calls resolve directly)
57
+ // ---------------------------------------------------------------------------
58
+
46
59
  override fun configure(config: ReadableMap, promise: Promise) {
47
- scope.launch {
48
- try {
49
- Beekon.configure(wireToConfig(config))
50
- promise.resolve(null)
51
- } catch (t: Throwable) {
52
- promise.reject(errorCode(t), t.message ?: "configure failed", t)
53
- }
60
+ try {
61
+ Beekon.configure(wireToConfig(config))
62
+ promise.resolve(null)
63
+ } catch (t: Throwable) {
64
+ promise.reject(errorCode(t), t.message ?: "configure failed", t)
54
65
  }
55
66
  }
56
67
 
@@ -76,17 +87,108 @@ class BeekonRnModule(private val reactContext: ReactApplicationContext) :
76
87
  }
77
88
  }
78
89
 
79
- override fun history(fromMs: Double, toMs: Double, promise: Promise) {
90
+ // Android has no native resumeIfNeeded Beekon.start() rehydrates the
91
+ // persisted intent (mirrors the Flutter plugin).
92
+ override fun resumeIfNeeded(promise: Promise) {
93
+ scope.launch {
94
+ try {
95
+ Beekon.start()
96
+ promise.resolve(null)
97
+ } catch (t: Throwable) {
98
+ promise.reject(errorCode(t), t.message ?: "resumeIfNeeded failed", t)
99
+ }
100
+ }
101
+ }
102
+
103
+ // ---------------------------------------------------------------------------
104
+ // History
105
+ // ---------------------------------------------------------------------------
106
+
107
+ override fun getLocations(fromMs: Double, toMs: Double, promise: Promise) {
80
108
  scope.launch {
81
109
  try {
82
110
  val from = Instant.ofEpochMilli(fromMs.toLong())
83
111
  val to = Instant.ofEpochMilli(toMs.toLong())
84
- val locations = Beekon.history(from, to)
85
112
  val arr: WritableArray = Arguments.createArray()
86
- for (loc in locations) arr.pushMap(locationToWire(loc))
113
+ for (loc in Beekon.getLocations(from, to)) arr.pushMap(locationToWire(loc))
87
114
  promise.resolve(arr)
88
115
  } catch (t: Throwable) {
89
- promise.reject(errorCode(t), t.message ?: "history failed", t)
116
+ promise.reject(errorCode(t), t.message ?: "getLocations failed", t)
117
+ }
118
+ }
119
+ }
120
+
121
+ override fun deleteLocations(beforeMs: Double, promise: Promise) {
122
+ scope.launch {
123
+ try {
124
+ // Negative is the wire sentinel for "delete all" (no cutoff).
125
+ val before = if (beforeMs < 0) null else Instant.ofEpochMilli(beforeMs.toLong())
126
+ promise.resolve(Beekon.deleteLocations(before))
127
+ } catch (t: Throwable) {
128
+ promise.reject(errorCode(t), t.message ?: "deleteLocations failed", t)
129
+ }
130
+ }
131
+ }
132
+
133
+ override fun pendingUploadCount(promise: Promise) {
134
+ scope.launch {
135
+ try {
136
+ promise.resolve(Beekon.pendingUploadCount())
137
+ } catch (t: Throwable) {
138
+ promise.reject(errorCode(t), t.message ?: "pendingUploadCount failed", t)
139
+ }
140
+ }
141
+ }
142
+
143
+ // ---------------------------------------------------------------------------
144
+ // Sync
145
+ // ---------------------------------------------------------------------------
146
+
147
+ override fun sync(promise: Promise) {
148
+ Beekon.sync()
149
+ promise.resolve(null)
150
+ }
151
+
152
+ override fun setExtras(entries: ReadableArray, promise: Promise) {
153
+ Beekon.setExtras(entriesToMap(entries))
154
+ promise.resolve(null)
155
+ }
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // Geofences
159
+ // ---------------------------------------------------------------------------
160
+
161
+ override fun addGeofences(geofences: ReadableArray, promise: Promise) {
162
+ scope.launch {
163
+ try {
164
+ Beekon.addGeofences(wireToGeofences(geofences))
165
+ promise.resolve(null)
166
+ } catch (t: Throwable) {
167
+ promise.reject(errorCode(t), t.message ?: "addGeofences failed", t)
168
+ }
169
+ }
170
+ }
171
+
172
+ override fun removeGeofences(ids: ReadableArray, promise: Promise) {
173
+ scope.launch {
174
+ try {
175
+ val list = (0 until ids.size()).mapNotNull { ids.getString(it) }
176
+ Beekon.removeGeofences(list)
177
+ promise.resolve(null)
178
+ } catch (t: Throwable) {
179
+ promise.reject(errorCode(t), t.message ?: "removeGeofences failed", t)
180
+ }
181
+ }
182
+ }
183
+
184
+ override fun listGeofences(promise: Promise) {
185
+ scope.launch {
186
+ try {
187
+ val arr: WritableArray = Arguments.createArray()
188
+ for (g in Beekon.listGeofences()) arr.pushMap(geofenceToWire(g))
189
+ promise.resolve(arr)
190
+ } catch (t: Throwable) {
191
+ promise.reject(errorCode(t), t.message ?: "listGeofences failed", t)
90
192
  }
91
193
  }
92
194
  }
@@ -97,54 +199,126 @@ class BeekonRnModule(private val reactContext: ReactApplicationContext) :
97
199
  }
98
200
 
99
201
  // ---------------------------------------------------------------------------
100
- // Mappers (Wire Kotlin types)
202
+ // Mappers: wire (ReadableMap/Array) Kotlin
101
203
  // ---------------------------------------------------------------------------
102
204
 
103
205
  private fun wireToConfig(map: ReadableMap): BeekonConfig {
104
- val intervalSeconds = map.getDouble("intervalSeconds").toLong()
105
- val distanceMeters = map.getDouble("distanceMeters")
106
- val notification = map.getMap("androidNotification")
107
- ?.let { wireToNotification(it) }
108
- ?: BeekonConfig.Notification()
206
+ val sync =
207
+ if (map.hasKey("sync") && !map.isNull("sync")) {
208
+ map.getMap("sync")?.let { wireToSyncConfig(it) }
209
+ } else {
210
+ null
211
+ }
212
+ val notification =
213
+ if (map.hasKey("notification") && !map.isNull("notification")) {
214
+ map.getMap("notification")?.let { wireToNotification(it) }
215
+ } else {
216
+ null
217
+ }
109
218
  return BeekonConfig(
110
- intervalSeconds = intervalSeconds,
111
- distanceMeters = distanceMeters,
112
- notification = notification,
219
+ minTimeBetweenLocationsSeconds =
220
+ map.getDouble("minTimeBetweenLocationsSeconds").toLong(),
221
+ minDistanceBetweenLocationsMeters =
222
+ map.getDouble("minDistanceBetweenLocationsMeters"),
223
+ accuracyMode = toAccuracyMode(map.getString("accuracyMode")),
224
+ whenStationary = toStationaryMode(map.getString("whenStationary")),
225
+ stationaryRadiusMeters = map.getDouble("stationaryRadiusMeters"),
226
+ detectActivity = map.getBoolean("detectActivity"),
227
+ sync = sync,
228
+ notification = notification ?: NotificationConfig(),
113
229
  )
114
230
  }
115
231
 
116
- private fun wireToNotification(map: ReadableMap): BeekonConfig.Notification {
117
- val title = if (map.hasKey("title") && !map.isNull("title")) map.getString("title") else null
118
- val text = if (map.hasKey("text") && !map.isNull("text")) map.getString("text") else null
119
- val resName =
120
- if (map.hasKey("smallIconResName") && !map.isNull("smallIconResName"))
121
- map.getString("smallIconResName")
122
- else null
123
- val smallIcon: Int? = resName?.let {
124
- val pkg = reactContext.packageName
125
- val id = reactContext.resources.getIdentifier(it, "drawable", pkg)
126
- if (id == 0) {
127
- throw IllegalArgumentException("drawable resource '$it' not found in package '$pkg'")
128
- }
129
- id
232
+ private fun wireToSyncConfig(map: ReadableMap): SyncConfig =
233
+ SyncConfig(
234
+ url = map.getString("url") ?: "",
235
+ headers = entriesToMap(map.getArray("headers")),
236
+ intervalSeconds = map.getDouble("intervalSeconds").toLong(),
237
+ batchSize = map.getDouble("batchSize").toInt(),
238
+ )
239
+
240
+ private fun wireToNotification(map: ReadableMap): NotificationConfig {
241
+ val title =
242
+ if (map.hasKey("title") && !map.isNull("title")) map.getString("title") else null
243
+ val text =
244
+ if (map.hasKey("text") && !map.isNull("text")) map.getString("text") else null
245
+ // Use the data-class default for `title` when the wire value is absent.
246
+ return if (title != null) {
247
+ NotificationConfig(title = title, text = text)
248
+ } else {
249
+ NotificationConfig(text = text)
130
250
  }
131
- return BeekonConfig.Notification(title = title, text = text, smallIcon = smallIcon)
132
251
  }
133
252
 
253
+ private fun wireToGeofences(arr: ReadableArray): List<BeekonGeofence> {
254
+ val out = ArrayList<BeekonGeofence>(arr.size())
255
+ for (i in 0 until arr.size()) {
256
+ val m = arr.getMap(i) ?: continue
257
+ out += BeekonGeofence(
258
+ id = m.getString("id") ?: "",
259
+ latitude = m.getDouble("lat"),
260
+ longitude = m.getDouble("lng"),
261
+ radiusMeters = m.getDouble("radiusMeters"),
262
+ notifyOnEntry = m.getBoolean("notifyOnEntry"),
263
+ notifyOnExit = m.getBoolean("notifyOnExit"),
264
+ )
265
+ }
266
+ return out
267
+ }
268
+
269
+ private fun entriesToMap(arr: ReadableArray?): Map<String, String> {
270
+ if (arr == null) return emptyMap()
271
+ val out = LinkedHashMap<String, String>(arr.size())
272
+ for (i in 0 until arr.size()) {
273
+ val e = arr.getMap(i) ?: continue
274
+ val k = e.getString("key") ?: continue
275
+ val v = e.getString("value") ?: continue
276
+ out[k] = v
277
+ }
278
+ return out
279
+ }
280
+
281
+ // ---------------------------------------------------------------------------
282
+ // Mappers: Kotlin → wire (WritableMap)
283
+ // ---------------------------------------------------------------------------
284
+
134
285
  private fun locationToWire(loc: Location): WritableMap {
135
286
  val m = Arguments.createMap()
136
- m.putDouble("lat", loc.lat)
137
- m.putDouble("lng", loc.lng)
287
+ m.putString("id", loc.id)
288
+ m.putDouble("lat", loc.latitude)
289
+ m.putDouble("lng", loc.longitude)
138
290
  m.putDouble("timestampMs", loc.timestamp.toEpochMilli().toDouble())
139
291
  putNullableDouble(m, "accuracy", loc.accuracy)
140
292
  putNullableDouble(m, "speed", loc.speed)
141
293
  putNullableDouble(m, "bearing", loc.bearing)
142
294
  putNullableDouble(m, "altitude", loc.altitude)
295
+ m.putString("quality", qualityToWire(loc.quality))
296
+ m.putString("trigger", triggerToWire(loc.trigger))
297
+ m.putString("motion", motionToWire(loc.motion))
298
+ val activity = loc.activity
299
+ if (activity != null) m.putString("activity", activityToWire(activity)) else m.putNull("activity")
300
+ m.putBoolean("isMock", loc.isMock)
143
301
  return m
144
302
  }
145
303
 
146
- private fun putNullableDouble(map: WritableMap, key: String, value: Double?) {
147
- if (value == null) map.putNull(key) else map.putDouble(key, value)
304
+ private fun geofenceToWire(g: BeekonGeofence): WritableMap {
305
+ val m = Arguments.createMap()
306
+ m.putString("id", g.id)
307
+ m.putDouble("lat", g.latitude)
308
+ m.putDouble("lng", g.longitude)
309
+ m.putDouble("radiusMeters", g.radiusMeters)
310
+ m.putBoolean("notifyOnEntry", g.notifyOnEntry)
311
+ m.putBoolean("notifyOnExit", g.notifyOnExit)
312
+ return m
313
+ }
314
+
315
+ private fun geofenceEventToWire(e: GeofenceEvent): WritableMap {
316
+ val m = Arguments.createMap()
317
+ m.putString("id", e.id)
318
+ m.putString("geofenceId", e.geofenceId)
319
+ m.putString("type", transitionToWire(e.type))
320
+ m.putDouble("timestampMs", e.timestamp.toEpochMilli().toDouble())
321
+ return m
148
322
  }
149
323
 
150
324
  private fun stateToWire(s: BeekonState): WritableMap {
@@ -160,19 +334,91 @@ class BeekonRnModule(private val reactContext: ReactApplicationContext) :
160
334
  return m
161
335
  }
162
336
 
337
+ private fun syncStatusToWire(s: SyncStatus): WritableMap {
338
+ val m = Arguments.createMap()
339
+ when (s) {
340
+ SyncStatus.Idle -> m.putString("type", "idle")
341
+ SyncStatus.Pending -> m.putString("type", "pending")
342
+ is SyncStatus.Failed -> {
343
+ m.putString("type", "failed")
344
+ m.putString("failure", syncFailureToWire(s.reason))
345
+ }
346
+ }
347
+ return m
348
+ }
349
+
350
+ private fun putNullableDouble(map: WritableMap, key: String, value: Double?) {
351
+ if (value == null) map.putNull(key) else map.putDouble(key, value)
352
+ }
353
+
354
+ // ---------------------------------------------------------------------------
355
+ // Enum mappers
356
+ // ---------------------------------------------------------------------------
357
+
358
+ private fun toAccuracyMode(s: String?): AccuracyMode = when (s) {
359
+ "high" -> AccuracyMode.High
360
+ "low" -> AccuracyMode.Low
361
+ else -> AccuracyMode.Balanced
362
+ }
363
+
364
+ private fun toStationaryMode(s: String?): StationaryMode = when (s) {
365
+ "keepTracking" -> StationaryMode.KeepTracking
366
+ "pauseWithCheckIns" -> StationaryMode.PauseWithCheckIns
367
+ else -> StationaryMode.Pause
368
+ }
369
+
163
370
  private fun stopReasonToWire(r: StopReason): String = when (r) {
164
371
  StopReason.User -> "user"
165
372
  StopReason.PermissionDenied -> "permissionDenied"
166
373
  StopReason.LocationServicesDisabled -> "locationServicesDisabled"
374
+ StopReason.LocationUnavailable -> "locationUnavailable"
167
375
  StopReason.System -> "system"
168
376
  }
169
377
 
170
- // Maps native error sealed types to stable JS-side codes. Same code strings
171
- // on iOS so JS can switch on them platform-agnostically.
378
+ private fun syncFailureToWire(f: SyncFailure): String = when (f) {
379
+ SyncFailure.Auth -> "auth"
380
+ SyncFailure.Rejected -> "rejected"
381
+ }
382
+
383
+ private fun qualityToWire(q: LocationQuality): String = when (q) {
384
+ LocationQuality.Ok -> "ok"
385
+ LocationQuality.LowAccuracy -> "lowAccuracy"
386
+ LocationQuality.ImplausibleSpeed -> "implausibleSpeed"
387
+ }
388
+
389
+ private fun triggerToWire(t: LocationTrigger): String = when (t) {
390
+ LocationTrigger.Interval -> "interval"
391
+ LocationTrigger.Motion -> "motion"
392
+ LocationTrigger.CheckIn -> "checkIn"
393
+ LocationTrigger.Geofence -> "geofence"
394
+ LocationTrigger.Manual -> "manual"
395
+ }
396
+
397
+ private fun motionToWire(m: MotionState): String = when (m) {
398
+ MotionState.Moving -> "moving"
399
+ MotionState.Stationary -> "stationary"
400
+ MotionState.Unknown -> "unknown"
401
+ }
402
+
403
+ private fun activityToWire(a: ActivityType): String = when (a) {
404
+ ActivityType.Stationary -> "stationary"
405
+ ActivityType.Walking -> "walking"
406
+ ActivityType.Running -> "running"
407
+ ActivityType.Cycling -> "cycling"
408
+ ActivityType.Automotive -> "automotive"
409
+ ActivityType.Unknown -> "unknown"
410
+ }
411
+
412
+ private fun transitionToWire(t: Transition): String = when (t) {
413
+ Transition.Enter -> "enter"
414
+ Transition.Exit -> "exit"
415
+ }
416
+
417
+ // Maps native exceptions to stable JS-side codes, shared with iOS so JS can
418
+ // switch on them platform-agnostically.
172
419
  private fun errorCode(t: Throwable): String = when (t) {
173
- is BeekonError.PermissionDenied -> "PERMISSION_DENIED"
174
- is BeekonError.LocationServicesDisabled -> "LOCATION_SERVICES_DISABLED"
175
- is BeekonError.StorageFailure -> "STORAGE_FAILURE"
420
+ is BeekonException.StorageException -> "STORAGE_FAILURE"
421
+ is BeekonException.InvalidGeofence -> "INVALID_GEOFENCE"
176
422
  else -> "INTERNAL_ERROR"
177
423
  }
178
424
 
package/ios/BeekonRn.mm CHANGED
@@ -1,8 +1,13 @@
1
1
  #import "BeekonRn.h"
2
- // Auto-generated bridging header from the Swift impl. With `use_frameworks!`
3
- // in the host Podfile (required for Swift pods), CocoaPods generates this
4
- // inside the BeekonRn framework, so the framework-style import is needed.
2
+ // Auto-generated Swift interop header for the BeekonRnImpl class. Its path
3
+ // depends on how the host links pods: under `<BeekonRn/…>` with
4
+ // `use_frameworks!` (framework), or as a same-target generated header with
5
+ // static linking (the React Native default). Support both.
6
+ #if __has_include(<BeekonRn/BeekonRn-Swift.h>)
5
7
  #import <BeekonRn/BeekonRn-Swift.h>
8
+ #else
9
+ #import "BeekonRn-Swift.h"
10
+ #endif
6
11
 
7
12
  @implementation BeekonRn {
8
13
  BeekonRnImpl *_impl;
@@ -13,32 +18,43 @@
13
18
  if (self) {
14
19
  __weak __typeof(self) weakSelf = self;
15
20
  _impl = [[BeekonRnImpl alloc]
16
- initOnState:^(NSDictionary *_Nonnull s) {
17
- [weakSelf emitOnState:s];
18
- }
19
- onLocation:^(NSDictionary *_Nonnull l) {
20
- [weakSelf emitOnLocation:l];
21
- }];
21
+ initOnState:^(NSDictionary *_Nonnull s) {
22
+ [weakSelf emitOnState:s];
23
+ }
24
+ onLocation:^(NSDictionary *_Nonnull l) {
25
+ [weakSelf emitOnLocation:l];
26
+ }
27
+ onGeofenceEvent:^(NSDictionary *_Nonnull e) {
28
+ [weakSelf emitOnGeofenceEvent:e];
29
+ }
30
+ onSyncStatus:^(NSDictionary *_Nonnull st) {
31
+ [weakSelf emitOnSyncStatus:st];
32
+ }];
22
33
  }
23
34
  return self;
24
35
  }
25
36
 
37
+ // Called by React Native on teardown (RCTInvalidating). The Codegen base class
38
+ // derives from NSObject, which has no `invalidate`, so there's no super to call.
26
39
  - (void)invalidate {
27
40
  [_impl invalidate];
28
- [super invalidate];
29
41
  }
30
42
 
31
- - (void)configure:(JS::NativeBeekonRn::WireConfig &)config
43
+ /// Registers Beekon's background tasks. Call from the host AppDelegate's
44
+ /// `application:didFinishLaunchingWithOptions:` (it must run before that method
45
+ /// returns). Exposed as a class method so hosts don't import BeekonKit directly.
46
+ + (void)registerBackgroundTasks {
47
+ [BeekonRnImpl registerBackgroundTasks];
48
+ }
49
+
50
+ // MARK: - Lifecycle
51
+
52
+ - (void)configure:(NSDictionary *)config
32
53
  resolve:(RCTPromiseResolveBlock)resolve
33
54
  reject:(RCTPromiseRejectBlock)reject {
34
- // Codegen passes the struct; round-trip through NSDictionary so the Swift
35
- // side can decode generically without an iOS-specific spec import. iOS
36
- // ignores `androidNotification` — passed through but unused.
37
- NSDictionary *dict = @{
38
- @"intervalSeconds": @(config.intervalSeconds()),
39
- @"distanceMeters": @(config.distanceMeters()),
40
- };
41
- [_impl configure:dict resolver:resolve rejecter:reject];
55
+ // The config crosses as a plain dictionary (UnsafeObject); the Swift side
56
+ // parses it. iOS ignores `notification` (Android-only).
57
+ [_impl configure:config resolver:resolve rejecter:reject];
42
58
  }
43
59
 
44
60
  - (void)start:(RCTPromiseResolveBlock)resolve
@@ -51,11 +67,61 @@
51
67
  [_impl stopWithResolver:resolve rejecter:reject];
52
68
  }
53
69
 
54
- - (void)history:(double)fromMs
55
- toMs:(double)toMs
56
- resolve:(RCTPromiseResolveBlock)resolve
57
- reject:(RCTPromiseRejectBlock)reject {
58
- [_impl historyFromMs:fromMs toMs:toMs resolver:resolve rejecter:reject];
70
+ - (void)resumeIfNeeded:(RCTPromiseResolveBlock)resolve
71
+ reject:(RCTPromiseRejectBlock)reject {
72
+ [_impl resumeIfNeededWithResolver:resolve rejecter:reject];
73
+ }
74
+
75
+ // MARK: - History
76
+
77
+ - (void)getLocations:(double)fromMs
78
+ toMs:(double)toMs
79
+ resolve:(RCTPromiseResolveBlock)resolve
80
+ reject:(RCTPromiseRejectBlock)reject {
81
+ [_impl getLocationsFromMs:fromMs toMs:toMs resolver:resolve rejecter:reject];
82
+ }
83
+
84
+ - (void)deleteLocations:(double)beforeMs
85
+ resolve:(RCTPromiseResolveBlock)resolve
86
+ reject:(RCTPromiseRejectBlock)reject {
87
+ [_impl deleteLocationsBeforeMs:beforeMs resolver:resolve rejecter:reject];
88
+ }
89
+
90
+ - (void)pendingUploadCount:(RCTPromiseResolveBlock)resolve
91
+ reject:(RCTPromiseRejectBlock)reject {
92
+ [_impl pendingUploadCountWithResolver:resolve rejecter:reject];
93
+ }
94
+
95
+ // MARK: - Sync
96
+
97
+ - (void)sync:(RCTPromiseResolveBlock)resolve
98
+ reject:(RCTPromiseRejectBlock)reject {
99
+ [_impl syncWithResolver:resolve rejecter:reject];
100
+ }
101
+
102
+ - (void)setExtras:(NSArray *)entries
103
+ resolve:(RCTPromiseResolveBlock)resolve
104
+ reject:(RCTPromiseRejectBlock)reject {
105
+ [_impl setExtras:entries resolver:resolve rejecter:reject];
106
+ }
107
+
108
+ // MARK: - Geofences
109
+
110
+ - (void)addGeofences:(NSArray *)geofences
111
+ resolve:(RCTPromiseResolveBlock)resolve
112
+ reject:(RCTPromiseRejectBlock)reject {
113
+ [_impl addGeofences:geofences resolver:resolve rejecter:reject];
114
+ }
115
+
116
+ - (void)removeGeofences:(NSArray *)ids
117
+ resolve:(RCTPromiseResolveBlock)resolve
118
+ reject:(RCTPromiseRejectBlock)reject {
119
+ [_impl removeGeofences:ids resolver:resolve rejecter:reject];
120
+ }
121
+
122
+ - (void)listGeofences:(RCTPromiseResolveBlock)resolve
123
+ reject:(RCTPromiseRejectBlock)reject {
124
+ [_impl listGeofencesWithResolver:resolve rejecter:reject];
59
125
  }
60
126
 
61
127
  - (std::shared_ptr<facebook::react::TurboModule>)getTurboModule: