@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.
- package/BeekonRn.podspec +1 -1
- package/README.md +91 -39
- package/android/build.gradle +9 -4
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +306 -60
- package/ios/BeekonRn.mm +90 -24
- package/ios/BeekonRn.swift +360 -60
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/BeekonKit +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Info.plist +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios.abi.json +7521 -1312
- 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 +191 -40
- 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/Info.plist +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/arm64-apple-ios-simulator.abi.json +7521 -1312
- 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 +191 -40
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +7521 -1312
- 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 +191 -40
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +2 -80
- package/lib/module/NativeBeekonRn.js +22 -7
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +198 -46
- package/lib/module/beekon.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/mappers.js +122 -28
- package/lib/module/internal/mappers.js.map +1 -1
- package/lib/module/types/config.js +2 -0
- package/lib/module/types/enums.js +2 -0
- package/lib/module/types/enums.js.map +1 -0
- package/lib/module/types/error.js +10 -4
- package/lib/module/types/error.js.map +1 -1
- package/lib/module/types/geofence.js +2 -0
- package/lib/module/types/geofence.js.map +1 -0
- package/lib/module/types/location.js +2 -0
- package/lib/module/types/sync.js +2 -0
- package/lib/module/types/sync.js.map +1 -0
- package/lib/typescript/src/NativeBeekonRn.d.ts +102 -20
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +81 -33
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +5 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/mappers.d.ts +12 -6
- package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
- package/lib/typescript/src/types/config.d.ts +50 -20
- package/lib/typescript/src/types/config.d.ts.map +1 -1
- package/lib/typescript/src/types/enums.d.ts +48 -0
- package/lib/typescript/src/types/enums.d.ts.map +1 -0
- package/lib/typescript/src/types/error.d.ts +11 -5
- package/lib/typescript/src/types/error.d.ts.map +1 -1
- package/lib/typescript/src/types/geofence.d.ts +36 -0
- package/lib/typescript/src/types/geofence.d.ts.map +1 -0
- package/lib/typescript/src/types/location.d.ts +22 -8
- package/lib/typescript/src/types/location.d.ts.map +1 -1
- package/lib/typescript/src/types/state.d.ts +13 -4
- package/lib/typescript/src/types/state.d.ts.map +1 -1
- package/lib/typescript/src/types/sync.d.ts +27 -0
- package/lib/typescript/src/types/sync.d.ts.map +1 -0
- package/package.json +4 -5
- package/scripts/fetch-beekonkit.sh +5 -5
- package/src/NativeBeekonRn.ts +110 -20
- package/src/beekon.ts +219 -49
- package/src/index.tsx +21 -2
- package/src/internal/mappers.ts +187 -30
- package/src/types/config.ts +52 -20
- package/src/types/enums.ts +64 -0
- package/src/types/error.ts +11 -8
- package/src/types/geofence.ts +37 -0
- package/src/types/location.ts +28 -8
- package/src/types/state.ts +13 -3
- package/src/types/sync.ts +23 -0
- package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeDirectory +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeRequirements +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeResources +0 -296
- package/ios/Frameworks/BeekonKit.xcframework/_CodeSignature/CodeSignature +0 -0
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64/BeekonKit.framework/_CodeSignature/CodeResources +0 -146
package/ios/BeekonRn.swift
CHANGED
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import BeekonKit
|
|
3
3
|
|
|
4
|
-
/// Swift impl of the Beekon RN TurboModule. Bridges the actor-based native
|
|
5
|
-
///
|
|
4
|
+
/// Swift impl of the Beekon RN TurboModule. Bridges the actor-based native SDK
|
|
5
|
+
/// (`BeekonKit.Beekon.shared`) to the ObjC TurboModule conformance in
|
|
6
6
|
/// `BeekonRn.mm`. All public-facing methods take ObjC-compatible callbacks
|
|
7
|
-
/// (resolver/rejecter) and dictionaries so they can be invoked from `.mm`.
|
|
7
|
+
/// (resolver/rejecter) and dictionaries/arrays so they can be invoked from `.mm`.
|
|
8
8
|
///
|
|
9
|
-
/// Event delivery: `BeekonRn.mm` constructs this with
|
|
10
|
-
/// the codegen
|
|
11
|
-
///
|
|
12
|
-
/// init.
|
|
13
|
-
//
|
|
14
|
-
//
|
|
15
|
-
//
|
|
16
|
-
// and `@objc` rules out `actor`,
|
|
9
|
+
/// Event delivery: `BeekonRn.mm` constructs this with four closures that call
|
|
10
|
+
/// the codegen `emitOnState:` / `emitOnLocation:` / `emitOnGeofenceEvent:` /
|
|
11
|
+
/// `emitOnSyncStatus:` ObjC methods; the closures are invoked from per-stream
|
|
12
|
+
/// `Task`s started during init.
|
|
13
|
+
//
|
|
14
|
+
// `@unchecked Sendable` is correct here: the four emit closures are immutable
|
|
15
|
+
// `let`s, and the stream `Task`s are mutated only from init/invalidate which run
|
|
16
|
+
// sequentially. NSObject can't be auto-Sendable and `@objc` rules out `actor`,
|
|
17
|
+
// so this is the path. Non-Sendable wire inputs (NSDictionary/NSArray) are
|
|
18
|
+
// converted to Sendable Beekon types *before* crossing into any `Task`.
|
|
17
19
|
@objc public final class BeekonRnImpl: NSObject, @unchecked Sendable {
|
|
18
20
|
|
|
19
21
|
private let onStateCb: (NSDictionary) -> Void
|
|
20
22
|
private let onLocationCb: (NSDictionary) -> Void
|
|
23
|
+
private let onGeofenceEventCb: (NSDictionary) -> Void
|
|
24
|
+
private let onSyncStatusCb: (NSDictionary) -> Void
|
|
21
25
|
|
|
22
26
|
private var stateTask: Task<Void, Never>?
|
|
23
27
|
private var locationsTask: Task<Void, Never>?
|
|
28
|
+
private var geofenceEventsTask: Task<Void, Never>?
|
|
29
|
+
private var syncStatusTask: Task<Void, Never>?
|
|
24
30
|
|
|
25
31
|
@objc public init(
|
|
26
32
|
onState: @escaping (NSDictionary) -> Void,
|
|
27
|
-
onLocation: @escaping (NSDictionary) -> Void
|
|
33
|
+
onLocation: @escaping (NSDictionary) -> Void,
|
|
34
|
+
onGeofenceEvent: @escaping (NSDictionary) -> Void,
|
|
35
|
+
onSyncStatus: @escaping (NSDictionary) -> Void
|
|
28
36
|
) {
|
|
29
37
|
self.onStateCb = onState
|
|
30
38
|
self.onLocationCb = onLocation
|
|
39
|
+
self.onGeofenceEventCb = onGeofenceEvent
|
|
40
|
+
self.onSyncStatusCb = onSyncStatus
|
|
31
41
|
super.init()
|
|
32
42
|
self.stateTask = Task { [weak self] in
|
|
33
43
|
guard let self = self else { return }
|
|
@@ -41,13 +51,38 @@ import BeekonKit
|
|
|
41
51
|
self.onLocationCb(self.locationToWire(loc))
|
|
42
52
|
}
|
|
43
53
|
}
|
|
54
|
+
self.geofenceEventsTask = Task { [weak self] in
|
|
55
|
+
guard let self = self else { return }
|
|
56
|
+
for await event in await Beekon.shared.geofenceEvents {
|
|
57
|
+
self.onGeofenceEventCb(self.geofenceEventToWire(event))
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
self.syncStatusTask = Task { [weak self] in
|
|
61
|
+
guard let self = self else { return }
|
|
62
|
+
for await status in await Beekon.shared.syncStatus {
|
|
63
|
+
self.onSyncStatusCb(self.syncStatusToWire(status))
|
|
64
|
+
}
|
|
65
|
+
}
|
|
44
66
|
}
|
|
45
67
|
|
|
46
68
|
@objc public func invalidate() {
|
|
47
69
|
stateTask?.cancel()
|
|
48
70
|
locationsTask?.cancel()
|
|
71
|
+
geofenceEventsTask?.cancel()
|
|
72
|
+
syncStatusTask?.cancel()
|
|
49
73
|
stateTask = nil
|
|
50
74
|
locationsTask = nil
|
|
75
|
+
geofenceEventsTask = nil
|
|
76
|
+
syncStatusTask = nil
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/// Register Beekon's background-refresh task and install cold-launch hooks.
|
|
80
|
+
/// Must be called once, synchronously, during app launch (before
|
|
81
|
+
/// `didFinishLaunchingWithOptions` returns) — invoke from the host AppDelegate.
|
|
82
|
+
@objc public static func registerBackgroundTasks() {
|
|
83
|
+
Beekon.registerBackgroundTasks()
|
|
84
|
+
// Touch the shared actor so its cold-launch resume hooks install.
|
|
85
|
+
_ = Beekon.shared
|
|
51
86
|
}
|
|
52
87
|
|
|
53
88
|
// MARK: - Lifecycle
|
|
@@ -55,17 +90,11 @@ import BeekonKit
|
|
|
55
90
|
@objc public func configure(
|
|
56
91
|
_ config: NSDictionary,
|
|
57
92
|
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
58
|
-
rejecter
|
|
93
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
59
94
|
) {
|
|
60
95
|
// Convert the non-Sendable NSDictionary to a Sendable BeekonConfig before
|
|
61
96
|
// crossing into the Task closure.
|
|
62
|
-
let cfg
|
|
63
|
-
do {
|
|
64
|
-
cfg = try wireToConfig(config)
|
|
65
|
-
} catch {
|
|
66
|
-
reject(errorCode(error), error.localizedDescription, error)
|
|
67
|
-
return
|
|
68
|
-
}
|
|
97
|
+
let cfg = wireToConfig(config)
|
|
69
98
|
Task {
|
|
70
99
|
await Beekon.shared.configure(cfg)
|
|
71
100
|
resolve(nil)
|
|
@@ -74,88 +103,266 @@ import BeekonKit
|
|
|
74
103
|
|
|
75
104
|
@objc public func startWithResolver(
|
|
76
105
|
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
106
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
107
|
+
) {
|
|
108
|
+
// start() never throws — outcome surfaces on the `state` stream.
|
|
109
|
+
Task {
|
|
110
|
+
await Beekon.shared.start()
|
|
111
|
+
resolve(nil)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
@objc public func stopWithResolver(
|
|
116
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
117
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
118
|
+
) {
|
|
119
|
+
Task {
|
|
120
|
+
await Beekon.shared.stop()
|
|
121
|
+
resolve(nil)
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
@objc public func resumeIfNeededWithResolver(
|
|
126
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
127
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
128
|
+
) {
|
|
129
|
+
Task {
|
|
130
|
+
await Beekon.shared.resumeIfNeeded()
|
|
131
|
+
resolve(nil)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// MARK: - History
|
|
136
|
+
|
|
137
|
+
@objc public func getLocationsFromMs(
|
|
138
|
+
_ fromMs: Double,
|
|
139
|
+
toMs: Double,
|
|
140
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
77
141
|
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
78
142
|
) {
|
|
79
143
|
Task { [weak self] in
|
|
80
144
|
guard let self = self else { return }
|
|
81
145
|
do {
|
|
82
|
-
|
|
83
|
-
|
|
146
|
+
let from = Date(timeIntervalSince1970: fromMs / 1000.0)
|
|
147
|
+
let to = Date(timeIntervalSince1970: toMs / 1000.0)
|
|
148
|
+
let locations = try await Beekon.shared.getLocations(from: from, to: to)
|
|
149
|
+
resolve(locations.map { self.locationToWire($0) })
|
|
84
150
|
} catch {
|
|
85
151
|
reject(self.errorCode(error), error.localizedDescription, error)
|
|
86
152
|
}
|
|
87
153
|
}
|
|
88
154
|
}
|
|
89
155
|
|
|
90
|
-
@objc public func
|
|
156
|
+
@objc public func deleteLocationsBeforeMs(
|
|
157
|
+
_ beforeMs: Double,
|
|
158
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
159
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
160
|
+
) {
|
|
161
|
+
Task { [weak self] in
|
|
162
|
+
guard let self = self else { return }
|
|
163
|
+
do {
|
|
164
|
+
// Negative is the wire sentinel for "delete all" (no cutoff).
|
|
165
|
+
let before: Date? =
|
|
166
|
+
beforeMs < 0 ? nil : Date(timeIntervalSince1970: beforeMs / 1000.0)
|
|
167
|
+
let count = try await Beekon.shared.deleteLocations(before: before)
|
|
168
|
+
resolve(count)
|
|
169
|
+
} catch {
|
|
170
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
@objc public func pendingUploadCountWithResolver(
|
|
91
176
|
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
92
177
|
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
178
|
+
) {
|
|
179
|
+
Task { [weak self] in
|
|
180
|
+
guard let self = self else { return }
|
|
181
|
+
do {
|
|
182
|
+
let count = try await Beekon.shared.pendingUploadCount()
|
|
183
|
+
resolve(count)
|
|
184
|
+
} catch {
|
|
185
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// MARK: - Sync
|
|
191
|
+
|
|
192
|
+
@objc public func syncWithResolver(
|
|
193
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
194
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
93
195
|
) {
|
|
94
196
|
Task {
|
|
95
|
-
await Beekon.shared.
|
|
197
|
+
await Beekon.shared.sync()
|
|
96
198
|
resolve(nil)
|
|
97
199
|
}
|
|
98
200
|
}
|
|
99
201
|
|
|
100
|
-
@objc public func
|
|
101
|
-
_
|
|
102
|
-
|
|
202
|
+
@objc public func setExtras(
|
|
203
|
+
_ entries: NSArray,
|
|
204
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
205
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
206
|
+
) {
|
|
207
|
+
let extras = entriesToDict(entries)
|
|
208
|
+
Task {
|
|
209
|
+
await Beekon.shared.setExtras(extras)
|
|
210
|
+
resolve(nil)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// MARK: - Geofences
|
|
215
|
+
|
|
216
|
+
@objc public func addGeofences(
|
|
217
|
+
_ geofences: NSArray,
|
|
103
218
|
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
104
219
|
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
105
220
|
) {
|
|
221
|
+
let list = wireToGeofences(geofences)
|
|
106
222
|
Task { [weak self] in
|
|
107
223
|
guard let self = self else { return }
|
|
108
224
|
do {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// Function-form overload of `locations` — the property is the live
|
|
112
|
-
// stream, the function is the historical fetch. Swift resolves on
|
|
113
|
-
// signature.
|
|
114
|
-
let locations = try await Beekon.shared.locations(from: from, to: to)
|
|
115
|
-
let arr = locations.map { self.locationToWire($0) }
|
|
116
|
-
resolve(arr)
|
|
225
|
+
try await Beekon.shared.addGeofences(list)
|
|
226
|
+
resolve(nil)
|
|
117
227
|
} catch {
|
|
118
228
|
reject(self.errorCode(error), error.localizedDescription, error)
|
|
119
229
|
}
|
|
120
230
|
}
|
|
121
231
|
}
|
|
122
232
|
|
|
123
|
-
|
|
233
|
+
@objc public func removeGeofences(
|
|
234
|
+
_ ids: NSArray,
|
|
235
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
236
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
237
|
+
) {
|
|
238
|
+
let list = ids.compactMap { $0 as? String }
|
|
239
|
+
Task {
|
|
240
|
+
await Beekon.shared.removeGeofences(ids: list)
|
|
241
|
+
resolve(nil)
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
@objc public func listGeofencesWithResolver(
|
|
246
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
247
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
248
|
+
) {
|
|
249
|
+
Task { [weak self] in
|
|
250
|
+
guard let self = self else { return }
|
|
251
|
+
let geofences = await Beekon.shared.listGeofences()
|
|
252
|
+
resolve(geofences.map { self.geofenceToWire($0) })
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// MARK: - Mappers: wire (NSDictionary/NSArray) → Beekon
|
|
124
257
|
|
|
125
|
-
private func wireToConfig(_ d: NSDictionary)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
258
|
+
private func wireToConfig(_ d: NSDictionary) -> BeekonConfig {
|
|
259
|
+
let minTime =
|
|
260
|
+
(d["minTimeBetweenLocationsSeconds"] as? NSNumber)?.doubleValue ?? 30
|
|
261
|
+
let minDist =
|
|
262
|
+
(d["minDistanceBetweenLocationsMeters"] as? NSNumber)?.doubleValue ?? 100
|
|
263
|
+
let stationaryRadius =
|
|
264
|
+
(d["stationaryRadiusMeters"] as? NSNumber)?.doubleValue ?? 5
|
|
265
|
+
let detectActivity = (d["detectActivity"] as? NSNumber)?.boolValue ?? false
|
|
266
|
+
|
|
267
|
+
var sync: SyncConfig?
|
|
268
|
+
if let s = d["sync"] as? NSDictionary,
|
|
269
|
+
let urlStr = s["url"] as? String,
|
|
270
|
+
let url = URL(string: urlStr) {
|
|
271
|
+
sync = SyncConfig(
|
|
272
|
+
url: url,
|
|
273
|
+
headers: entriesToDict((s["headers"] as? NSArray) ?? []),
|
|
274
|
+
intervalSeconds: (s["intervalSeconds"] as? NSNumber)?.doubleValue ?? 300,
|
|
275
|
+
batchSize: (s["batchSize"] as? NSNumber)?.intValue ?? 100
|
|
136
276
|
)
|
|
137
277
|
}
|
|
278
|
+
|
|
279
|
+
// `notification` is Android-only — iOS ignores it.
|
|
138
280
|
return BeekonConfig(
|
|
139
|
-
|
|
140
|
-
|
|
281
|
+
minTimeBetweenLocationsSeconds: minTime,
|
|
282
|
+
minDistanceBetweenLocationsMeters: minDist,
|
|
283
|
+
accuracyMode: accuracyModeFromWire(d["accuracyMode"] as? String),
|
|
284
|
+
whenStationary: stationaryModeFromWire(d["whenStationary"] as? String),
|
|
285
|
+
stationaryRadiusMeters: stationaryRadius,
|
|
286
|
+
detectActivity: detectActivity,
|
|
287
|
+
sync: sync
|
|
141
288
|
)
|
|
142
|
-
// androidNotification ignored on iOS.
|
|
143
289
|
}
|
|
144
290
|
|
|
291
|
+
private func wireToGeofences(_ arr: NSArray) -> [BeekonGeofence] {
|
|
292
|
+
var out: [BeekonGeofence] = []
|
|
293
|
+
for case let m as NSDictionary in arr {
|
|
294
|
+
guard
|
|
295
|
+
let id = m["id"] as? String,
|
|
296
|
+
let lat = (m["lat"] as? NSNumber)?.doubleValue,
|
|
297
|
+
let lng = (m["lng"] as? NSNumber)?.doubleValue,
|
|
298
|
+
let radius = (m["radiusMeters"] as? NSNumber)?.doubleValue
|
|
299
|
+
else { continue }
|
|
300
|
+
out.append(
|
|
301
|
+
BeekonGeofence(
|
|
302
|
+
id: id,
|
|
303
|
+
latitude: lat,
|
|
304
|
+
longitude: lng,
|
|
305
|
+
radiusMeters: radius,
|
|
306
|
+
notifyOnEntry: (m["notifyOnEntry"] as? NSNumber)?.boolValue ?? true,
|
|
307
|
+
notifyOnExit: (m["notifyOnExit"] as? NSNumber)?.boolValue ?? true
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
return out
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
private func entriesToDict(_ arr: NSArray) -> [String: String] {
|
|
315
|
+
var out: [String: String] = [:]
|
|
316
|
+
for case let e as NSDictionary in arr {
|
|
317
|
+
if let k = e["key"] as? String, let v = e["value"] as? String {
|
|
318
|
+
out[k] = v
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return out
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// MARK: - Mappers: Beekon → wire (NSDictionary)
|
|
325
|
+
|
|
145
326
|
private func locationToWire(_ loc: Location) -> NSDictionary {
|
|
146
|
-
// NSDictionary literals can't carry `nil
|
|
147
|
-
//
|
|
327
|
+
// NSDictionary literals can't carry `nil`, so optionals collapse to
|
|
328
|
+
// `NSNull` — the JS Codegen layer translates that back to `null`.
|
|
148
329
|
let d = NSMutableDictionary()
|
|
149
|
-
d["
|
|
150
|
-
d["
|
|
330
|
+
d["id"] = loc.id
|
|
331
|
+
d["lat"] = loc.latitude
|
|
332
|
+
d["lng"] = loc.longitude
|
|
151
333
|
d["timestampMs"] = loc.timestamp.timeIntervalSince1970 * 1000.0
|
|
152
334
|
d["accuracy"] = loc.accuracy.map { $0 as Any } ?? NSNull()
|
|
153
335
|
d["speed"] = loc.speed.map { $0 as Any } ?? NSNull()
|
|
154
336
|
d["bearing"] = loc.bearing.map { $0 as Any } ?? NSNull()
|
|
155
337
|
d["altitude"] = loc.altitude.map { $0 as Any } ?? NSNull()
|
|
338
|
+
d["quality"] = qualityToWire(loc.quality)
|
|
339
|
+
d["trigger"] = triggerToWire(loc.trigger)
|
|
340
|
+
d["motion"] = motionToWire(loc.motion)
|
|
341
|
+
d["activity"] = loc.activity.map { activityToWire($0) as Any } ?? NSNull()
|
|
342
|
+
d["isMock"] = loc.isMock
|
|
156
343
|
return d
|
|
157
344
|
}
|
|
158
345
|
|
|
346
|
+
private func geofenceToWire(_ g: BeekonGeofence) -> NSDictionary {
|
|
347
|
+
return [
|
|
348
|
+
"id": g.id,
|
|
349
|
+
"lat": g.latitude,
|
|
350
|
+
"lng": g.longitude,
|
|
351
|
+
"radiusMeters": g.radiusMeters,
|
|
352
|
+
"notifyOnEntry": g.notifyOnEntry,
|
|
353
|
+
"notifyOnExit": g.notifyOnExit,
|
|
354
|
+
]
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private func geofenceEventToWire(_ e: GeofenceEvent) -> NSDictionary {
|
|
358
|
+
return [
|
|
359
|
+
"id": e.id,
|
|
360
|
+
"geofenceId": e.geofenceId,
|
|
361
|
+
"type": transitionToWire(e.type),
|
|
362
|
+
"timestampMs": e.timestamp.timeIntervalSince1970 * 1000.0,
|
|
363
|
+
]
|
|
364
|
+
}
|
|
365
|
+
|
|
159
366
|
private func stateToWire(_ s: BeekonState) -> NSDictionary {
|
|
160
367
|
switch s {
|
|
161
368
|
case .idle:
|
|
@@ -164,6 +371,40 @@ import BeekonKit
|
|
|
164
371
|
return ["type": "tracking"]
|
|
165
372
|
case .stopped(let reason):
|
|
166
373
|
return ["type": "stopped", "stopReason": stopReasonToWire(reason)]
|
|
374
|
+
// BeekonKit enums are non-frozen (library-evolution binary); handle unknowns.
|
|
375
|
+
@unknown default:
|
|
376
|
+
return ["type": "idle"]
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
private func syncStatusToWire(_ s: SyncStatus) -> NSDictionary {
|
|
381
|
+
switch s {
|
|
382
|
+
case .idle:
|
|
383
|
+
return ["type": "idle"]
|
|
384
|
+
case .pending:
|
|
385
|
+
return ["type": "pending"]
|
|
386
|
+
case .failed(let reason):
|
|
387
|
+
return ["type": "failed", "failure": syncFailureToWire(reason)]
|
|
388
|
+
@unknown default:
|
|
389
|
+
return ["type": "idle"]
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// MARK: - Enum mappers
|
|
394
|
+
|
|
395
|
+
private func accuracyModeFromWire(_ s: String?) -> AccuracyMode {
|
|
396
|
+
switch s {
|
|
397
|
+
case "high": return .high
|
|
398
|
+
case "low": return .low
|
|
399
|
+
default: return .balanced
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private func stationaryModeFromWire(_ s: String?) -> StationaryMode {
|
|
404
|
+
switch s {
|
|
405
|
+
case "keepTracking": return .keepTracking
|
|
406
|
+
case "pauseWithCheckIns": return .pauseWithCheckIns
|
|
407
|
+
default: return .pause
|
|
167
408
|
}
|
|
168
409
|
}
|
|
169
410
|
|
|
@@ -172,18 +413,77 @@ import BeekonKit
|
|
|
172
413
|
case .user: return "user"
|
|
173
414
|
case .permissionDenied: return "permissionDenied"
|
|
174
415
|
case .locationServicesDisabled: return "locationServicesDisabled"
|
|
416
|
+
case .locationUnavailable: return "locationUnavailable"
|
|
417
|
+
case .system: return "system"
|
|
418
|
+
@unknown default: return "system"
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
private func syncFailureToWire(_ f: SyncFailure) -> String {
|
|
423
|
+
switch f {
|
|
424
|
+
case .auth: return "auth"
|
|
425
|
+
case .rejected: return "rejected"
|
|
426
|
+
@unknown default: return "rejected"
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private func qualityToWire(_ q: LocationQuality) -> String {
|
|
431
|
+
switch q {
|
|
432
|
+
case .ok: return "ok"
|
|
433
|
+
case .lowAccuracy: return "lowAccuracy"
|
|
434
|
+
case .implausibleSpeed: return "implausibleSpeed"
|
|
435
|
+
@unknown default: return "ok"
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
private func triggerToWire(_ t: LocationTrigger) -> String {
|
|
440
|
+
switch t {
|
|
441
|
+
case .interval: return "interval"
|
|
442
|
+
case .motion: return "motion"
|
|
443
|
+
case .checkIn: return "checkIn"
|
|
444
|
+
case .geofence: return "geofence"
|
|
445
|
+
case .manual: return "manual"
|
|
446
|
+
@unknown default: return "interval"
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
private func motionToWire(_ m: MotionState) -> String {
|
|
451
|
+
switch m {
|
|
452
|
+
case .moving: return "moving"
|
|
453
|
+
case .stationary: return "stationary"
|
|
454
|
+
case .unknown: return "unknown"
|
|
455
|
+
@unknown default: return "unknown"
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
private func activityToWire(_ a: ActivityType) -> String {
|
|
460
|
+
switch a {
|
|
461
|
+
case .stationary: return "stationary"
|
|
462
|
+
case .walking: return "walking"
|
|
463
|
+
case .running: return "running"
|
|
464
|
+
case .cycling: return "cycling"
|
|
465
|
+
case .automotive: return "automotive"
|
|
466
|
+
case .unknown: return "unknown"
|
|
467
|
+
@unknown default: return "unknown"
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
private func transitionToWire(_ t: Transition) -> String {
|
|
472
|
+
switch t {
|
|
473
|
+
case .enter: return "enter"
|
|
474
|
+
case .exit: return "exit"
|
|
475
|
+
@unknown default: return "enter"
|
|
175
476
|
}
|
|
176
477
|
}
|
|
177
478
|
|
|
178
|
-
/// Maps native `BeekonError` cases to stable string codes shared with
|
|
179
|
-
///
|
|
180
|
-
/// branches.
|
|
479
|
+
/// Maps native `BeekonError` cases to stable string codes shared with Android,
|
|
480
|
+
/// so JS-side error handling can switch on them without per-platform branches.
|
|
181
481
|
private func errorCode(_ e: Error) -> String {
|
|
182
482
|
if let be = e as? BeekonError {
|
|
183
483
|
switch be {
|
|
184
|
-
case .
|
|
185
|
-
case .
|
|
186
|
-
|
|
484
|
+
case .storage: return "STORAGE_FAILURE"
|
|
485
|
+
case .invalidGeofence: return "INVALID_GEOFENCE"
|
|
486
|
+
@unknown default: return "INTERNAL_ERROR"
|
|
187
487
|
}
|
|
188
488
|
}
|
|
189
489
|
return "INTERNAL_ERROR"
|
|
Binary file
|