@wayq/beekon-rn 0.0.3 → 0.0.6
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 +5 -3
- package/CHANGELOG.md +103 -0
- package/README.md +326 -52
- package/android/build.gradle +9 -4
- package/android/src/main/java/in/wayq/beekonrn/BeekonRnModule.kt +411 -59
- package/ios/BeekonRn.mm +103 -24
- package/ios/BeekonRn.swift +465 -61
- package/ios/Frameworks/BeekonKit.xcframework/Info.plist +5 -5
- 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 +11424 -1279
- 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 +262 -36
- 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 +11424 -1279
- 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 +262 -36
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/Modules/BeekonKit.swiftmodule/x86_64-apple-ios-simulator.abi.json +11424 -1279
- 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 +262 -36
- package/ios/Frameworks/BeekonKit.xcframework/ios-arm64_x86_64-simulator/BeekonKit.framework/_CodeSignature/CodeResources +2 -80
- package/lib/module/NativeBeekonRn.js +35 -7
- package/lib/module/NativeBeekonRn.js.map +1 -1
- package/lib/module/beekon.js +246 -45
- package/lib/module/beekon.js.map +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/internal/mappers.js +166 -25
- package/lib/module/internal/mappers.js.map +1 -1
- package/lib/module/types/auth.js +4 -0
- package/lib/module/types/auth.js.map +1 -0
- 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 +20 -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 +150 -20
- package/lib/typescript/src/NativeBeekonRn.d.ts.map +1 -1
- package/lib/typescript/src/beekon.d.ts +110 -33
- package/lib/typescript/src/beekon.d.ts.map +1 -1
- package/lib/typescript/src/index.d.ts +6 -2
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/internal/mappers.d.ts +16 -6
- package/lib/typescript/src/internal/mappers.d.ts.map +1 -1
- package/lib/typescript/src/types/auth.d.ts +99 -0
- package/lib/typescript/src/types/auth.d.ts.map +1 -0
- package/lib/typescript/src/types/config.d.ts +66 -20
- package/lib/typescript/src/types/config.d.ts.map +1 -1
- package/lib/typescript/src/types/enums.d.ts +62 -0
- package/lib/typescript/src/types/enums.d.ts.map +1 -0
- package/lib/typescript/src/types/error.d.ts +21 -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 +8 -5
- package/scripts/fetch-beekonkit.sh +5 -5
- package/src/NativeBeekonRn.ts +165 -20
- package/src/beekon.ts +278 -48
- package/src/index.tsx +24 -2
- package/src/internal/mappers.ts +242 -27
- package/src/types/auth.ts +101 -0
- package/src/types/config.ts +68 -20
- package/src/types/enums.ts +80 -0
- package/src/types/error.ts +23 -5
- 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,47 @@
|
|
|
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
|
|
25
|
+
private let onAuthTokensCb: (NSDictionary) -> Void
|
|
21
26
|
|
|
22
27
|
private var stateTask: Task<Void, Never>?
|
|
23
28
|
private var locationsTask: Task<Void, Never>?
|
|
29
|
+
private var geofenceEventsTask: Task<Void, Never>?
|
|
30
|
+
private var syncStatusTask: Task<Void, Never>?
|
|
31
|
+
private var authTokensTask: Task<Void, Never>?
|
|
24
32
|
|
|
25
33
|
@objc public init(
|
|
26
34
|
onState: @escaping (NSDictionary) -> Void,
|
|
27
|
-
onLocation: @escaping (NSDictionary) -> Void
|
|
35
|
+
onLocation: @escaping (NSDictionary) -> Void,
|
|
36
|
+
onGeofenceEvent: @escaping (NSDictionary) -> Void,
|
|
37
|
+
onSyncStatus: @escaping (NSDictionary) -> Void,
|
|
38
|
+
onAuthTokens: @escaping (NSDictionary) -> Void
|
|
28
39
|
) {
|
|
29
40
|
self.onStateCb = onState
|
|
30
41
|
self.onLocationCb = onLocation
|
|
42
|
+
self.onGeofenceEventCb = onGeofenceEvent
|
|
43
|
+
self.onSyncStatusCb = onSyncStatus
|
|
44
|
+
self.onAuthTokensCb = onAuthTokens
|
|
31
45
|
super.init()
|
|
32
46
|
self.stateTask = Task { [weak self] in
|
|
33
47
|
guard let self = self else { return }
|
|
@@ -41,13 +55,46 @@ import BeekonKit
|
|
|
41
55
|
self.onLocationCb(self.locationToWire(loc))
|
|
42
56
|
}
|
|
43
57
|
}
|
|
58
|
+
self.geofenceEventsTask = Task { [weak self] in
|
|
59
|
+
guard let self = self else { return }
|
|
60
|
+
for await event in await Beekon.shared.geofenceEvents {
|
|
61
|
+
self.onGeofenceEventCb(self.geofenceEventToWire(event))
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
self.syncStatusTask = Task { [weak self] in
|
|
65
|
+
guard let self = self else { return }
|
|
66
|
+
for await status in await Beekon.shared.syncStatus {
|
|
67
|
+
self.onSyncStatusCb(self.syncStatusToWire(status))
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
self.authTokensTask = Task { [weak self] in
|
|
71
|
+
guard let self = self else { return }
|
|
72
|
+
for await tokens in await Beekon.shared.authChanges {
|
|
73
|
+
self.onAuthTokensCb(self.authTokensToWire(tokens))
|
|
74
|
+
}
|
|
75
|
+
}
|
|
44
76
|
}
|
|
45
77
|
|
|
46
78
|
@objc public func invalidate() {
|
|
47
79
|
stateTask?.cancel()
|
|
48
80
|
locationsTask?.cancel()
|
|
81
|
+
geofenceEventsTask?.cancel()
|
|
82
|
+
syncStatusTask?.cancel()
|
|
83
|
+
authTokensTask?.cancel()
|
|
49
84
|
stateTask = nil
|
|
50
85
|
locationsTask = nil
|
|
86
|
+
geofenceEventsTask = nil
|
|
87
|
+
syncStatusTask = nil
|
|
88
|
+
authTokensTask = nil
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/// Register Beekon's background-refresh task and install cold-launch hooks.
|
|
92
|
+
/// Must be called once, synchronously, during app launch (before
|
|
93
|
+
/// `didFinishLaunchingWithOptions` returns) — invoke from the host AppDelegate.
|
|
94
|
+
@objc public static func registerBackgroundTasks() {
|
|
95
|
+
Beekon.registerBackgroundTasks()
|
|
96
|
+
// Touch the shared actor so its cold-launch resume hooks install.
|
|
97
|
+
_ = Beekon.shared
|
|
51
98
|
}
|
|
52
99
|
|
|
53
100
|
// MARK: - Lifecycle
|
|
@@ -55,17 +102,11 @@ import BeekonKit
|
|
|
55
102
|
@objc public func configure(
|
|
56
103
|
_ config: NSDictionary,
|
|
57
104
|
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
58
|
-
rejecter
|
|
105
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
59
106
|
) {
|
|
60
107
|
// Convert the non-Sendable NSDictionary to a Sendable BeekonConfig before
|
|
61
108
|
// 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
|
-
}
|
|
109
|
+
let cfg = wireToConfig(config)
|
|
69
110
|
Task {
|
|
70
111
|
await Beekon.shared.configure(cfg)
|
|
71
112
|
resolve(nil)
|
|
@@ -74,88 +115,325 @@ import BeekonKit
|
|
|
74
115
|
|
|
75
116
|
@objc public func startWithResolver(
|
|
76
117
|
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
118
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
119
|
+
) {
|
|
120
|
+
// start() never throws — outcome surfaces on the `state` stream.
|
|
121
|
+
Task {
|
|
122
|
+
await Beekon.shared.start()
|
|
123
|
+
resolve(nil)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
@objc public func stopWithResolver(
|
|
128
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
129
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
130
|
+
) {
|
|
131
|
+
Task {
|
|
132
|
+
await Beekon.shared.stop()
|
|
133
|
+
resolve(nil)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
@objc public func resumeIfNeededWithResolver(
|
|
138
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
139
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
140
|
+
) {
|
|
141
|
+
Task {
|
|
142
|
+
await Beekon.shared.resumeIfNeeded()
|
|
143
|
+
resolve(nil)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@objc public func getCurrentLocationTimeoutMs(
|
|
148
|
+
_ timeoutMs: Double,
|
|
149
|
+
accuracy: String,
|
|
150
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
77
151
|
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
78
152
|
) {
|
|
153
|
+
// Empty accuracy is the wire encoding of "use the configured mode".
|
|
154
|
+
let mode: AccuracyMode? = accuracy.isEmpty ? nil : accuracyModeFromWire(accuracy)
|
|
79
155
|
Task { [weak self] in
|
|
80
156
|
guard let self = self else { return }
|
|
81
157
|
do {
|
|
82
|
-
try await Beekon.shared.
|
|
83
|
-
|
|
158
|
+
let location = try await Beekon.shared.getCurrentLocation(
|
|
159
|
+
timeout: timeoutMs / 1000.0,
|
|
160
|
+
accuracy: mode
|
|
161
|
+
)
|
|
162
|
+
// 0- or 1-element array — empty means timeout / no fix.
|
|
163
|
+
resolve(location.map { [self.locationToWire($0)] } ?? [])
|
|
84
164
|
} catch {
|
|
85
165
|
reject(self.errorCode(error), error.localizedDescription, error)
|
|
86
166
|
}
|
|
87
167
|
}
|
|
88
168
|
}
|
|
89
169
|
|
|
90
|
-
|
|
170
|
+
// MARK: - History
|
|
171
|
+
|
|
172
|
+
@objc public func getLocationsFromMs(
|
|
173
|
+
_ fromMs: Double,
|
|
174
|
+
toMs: Double,
|
|
175
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
176
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
177
|
+
) {
|
|
178
|
+
Task { [weak self] in
|
|
179
|
+
guard let self = self else { return }
|
|
180
|
+
do {
|
|
181
|
+
let from = Date(timeIntervalSince1970: fromMs / 1000.0)
|
|
182
|
+
let to = Date(timeIntervalSince1970: toMs / 1000.0)
|
|
183
|
+
let locations = try await Beekon.shared.getLocations(from: from, to: to)
|
|
184
|
+
resolve(locations.map { self.locationToWire($0) })
|
|
185
|
+
} catch {
|
|
186
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
@objc public func deleteLocationsBeforeMs(
|
|
192
|
+
_ beforeMs: Double,
|
|
193
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
194
|
+
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
195
|
+
) {
|
|
196
|
+
Task { [weak self] in
|
|
197
|
+
guard let self = self else { return }
|
|
198
|
+
do {
|
|
199
|
+
// Negative is the wire sentinel for "delete all" (no cutoff).
|
|
200
|
+
let before: Date? =
|
|
201
|
+
beforeMs < 0 ? nil : Date(timeIntervalSince1970: beforeMs / 1000.0)
|
|
202
|
+
let count = try await Beekon.shared.deleteLocations(before: before)
|
|
203
|
+
resolve(count)
|
|
204
|
+
} catch {
|
|
205
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
@objc public func pendingUploadCountWithResolver(
|
|
91
211
|
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
92
212
|
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
213
|
+
) {
|
|
214
|
+
Task { [weak self] in
|
|
215
|
+
guard let self = self else { return }
|
|
216
|
+
do {
|
|
217
|
+
let count = try await Beekon.shared.pendingUploadCount()
|
|
218
|
+
resolve(count)
|
|
219
|
+
} catch {
|
|
220
|
+
reject(self.errorCode(error), error.localizedDescription, error)
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// MARK: - Sync
|
|
226
|
+
|
|
227
|
+
@objc public func syncWithResolver(
|
|
228
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
229
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
93
230
|
) {
|
|
94
231
|
Task {
|
|
95
|
-
await Beekon.shared.
|
|
232
|
+
await Beekon.shared.sync()
|
|
96
233
|
resolve(nil)
|
|
97
234
|
}
|
|
98
235
|
}
|
|
99
236
|
|
|
100
|
-
@objc public func
|
|
101
|
-
_
|
|
102
|
-
|
|
237
|
+
@objc public func setExtras(
|
|
238
|
+
_ entries: NSArray,
|
|
239
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
240
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
241
|
+
) {
|
|
242
|
+
let extras = entriesToDict(entries)
|
|
243
|
+
Task {
|
|
244
|
+
await Beekon.shared.setExtras(extras)
|
|
245
|
+
resolve(nil)
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// MARK: - Geofences
|
|
250
|
+
|
|
251
|
+
@objc public func addGeofences(
|
|
252
|
+
_ geofences: NSArray,
|
|
103
253
|
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
104
254
|
rejecter reject: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
105
255
|
) {
|
|
256
|
+
let list = wireToGeofences(geofences)
|
|
106
257
|
Task { [weak self] in
|
|
107
258
|
guard let self = self else { return }
|
|
108
259
|
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)
|
|
260
|
+
try await Beekon.shared.addGeofences(list)
|
|
261
|
+
resolve(nil)
|
|
117
262
|
} catch {
|
|
118
263
|
reject(self.errorCode(error), error.localizedDescription, error)
|
|
119
264
|
}
|
|
120
265
|
}
|
|
121
266
|
}
|
|
122
267
|
|
|
123
|
-
|
|
268
|
+
@objc public func removeGeofences(
|
|
269
|
+
_ ids: NSArray,
|
|
270
|
+
resolver resolve: @escaping @Sendable (Any?) -> Void,
|
|
271
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
272
|
+
) {
|
|
273
|
+
let list = ids.compactMap { $0 as? String }
|
|
274
|
+
Task {
|
|
275
|
+
await Beekon.shared.removeGeofences(ids: list)
|
|
276
|
+
resolve(nil)
|
|
277
|
+
}
|
|
278
|
+
}
|
|
124
279
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
let
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
domain: "BeekonRn",
|
|
134
|
-
code: -1,
|
|
135
|
-
userInfo: [NSLocalizedDescriptionKey: "intervalSeconds/distanceMeters missing"]
|
|
136
|
-
)
|
|
280
|
+
@objc public func listGeofencesWithResolver(
|
|
281
|
+
_ resolve: @escaping @Sendable (Any?) -> Void,
|
|
282
|
+
rejecter _: @escaping @Sendable (String?, String?, Error?) -> Void
|
|
283
|
+
) {
|
|
284
|
+
Task { [weak self] in
|
|
285
|
+
guard let self = self else { return }
|
|
286
|
+
let geofences = await Beekon.shared.listGeofences()
|
|
287
|
+
resolve(geofences.map { self.geofenceToWire($0) })
|
|
137
288
|
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// MARK: - Mappers: wire (NSDictionary/NSArray) → Beekon
|
|
292
|
+
|
|
293
|
+
private func wireToConfig(_ d: NSDictionary) -> BeekonConfig {
|
|
294
|
+
let minTime =
|
|
295
|
+
(d["minTimeBetweenLocationsSeconds"] as? NSNumber)?.doubleValue ?? 30
|
|
296
|
+
let minDist =
|
|
297
|
+
(d["minDistanceBetweenLocationsMeters"] as? NSNumber)?.doubleValue ?? 100
|
|
298
|
+
let stationaryRadius =
|
|
299
|
+
(d["stationaryRadiusMeters"] as? NSNumber)?.doubleValue ?? 5
|
|
300
|
+
let detectActivity = (d["detectActivity"] as? NSNumber)?.boolValue ?? false
|
|
301
|
+
|
|
302
|
+
var sync: SyncConfig?
|
|
303
|
+
if let s = d["sync"] as? NSDictionary,
|
|
304
|
+
let urlStr = s["url"] as? String {
|
|
305
|
+
if let url = URL(string: urlStr) {
|
|
306
|
+
let auth = (s["auth"] as? NSDictionary).map { wireToAuthConfig($0) }
|
|
307
|
+
sync = SyncConfig(
|
|
308
|
+
url: url,
|
|
309
|
+
headers: entriesToDict((s["headers"] as? NSArray) ?? []),
|
|
310
|
+
intervalSeconds: (s["intervalSeconds"] as? NSNumber)?.doubleValue ?? 300,
|
|
311
|
+
batchSize: (s["batchSize"] as? NSNumber)?.intValue ?? 100,
|
|
312
|
+
auth: auth
|
|
313
|
+
)
|
|
314
|
+
} else {
|
|
315
|
+
// Don't silently drop the whole sync config on an unparseable URL —
|
|
316
|
+
// surface it so a malformed endpoint is diagnosable instead of a no-op.
|
|
317
|
+
NSLog("[BeekonRn] sync config ignored: url is not a valid URL: %@", urlStr)
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// `notification` is Android-only — iOS ignores it.
|
|
138
322
|
return BeekonConfig(
|
|
139
|
-
|
|
140
|
-
|
|
323
|
+
minTimeBetweenLocationsSeconds: minTime,
|
|
324
|
+
minDistanceBetweenLocationsMeters: minDist,
|
|
325
|
+
accuracyMode: accuracyModeFromWire(d["accuracyMode"] as? String),
|
|
326
|
+
whenStationary: stationaryModeFromWire(d["whenStationary"] as? String),
|
|
327
|
+
stationaryRadiusMeters: stationaryRadius,
|
|
328
|
+
detectActivity: detectActivity,
|
|
329
|
+
sync: sync
|
|
141
330
|
)
|
|
142
|
-
// androidNotification ignored on iOS.
|
|
143
331
|
}
|
|
144
332
|
|
|
333
|
+
private func wireToGeofences(_ arr: NSArray) -> [BeekonGeofence] {
|
|
334
|
+
var out: [BeekonGeofence] = []
|
|
335
|
+
for case let m as NSDictionary in arr {
|
|
336
|
+
guard
|
|
337
|
+
let id = m["id"] as? String,
|
|
338
|
+
let lat = (m["lat"] as? NSNumber)?.doubleValue,
|
|
339
|
+
let lng = (m["lng"] as? NSNumber)?.doubleValue,
|
|
340
|
+
let radius = (m["radiusMeters"] as? NSNumber)?.doubleValue
|
|
341
|
+
else { continue }
|
|
342
|
+
out.append(
|
|
343
|
+
BeekonGeofence(
|
|
344
|
+
id: id,
|
|
345
|
+
latitude: lat,
|
|
346
|
+
longitude: lng,
|
|
347
|
+
radiusMeters: radius,
|
|
348
|
+
notifyOnEntry: (m["notifyOnEntry"] as? NSNumber)?.boolValue ?? true,
|
|
349
|
+
notifyOnExit: (m["notifyOnExit"] as? NSNumber)?.boolValue ?? true
|
|
350
|
+
)
|
|
351
|
+
)
|
|
352
|
+
}
|
|
353
|
+
return out
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
private func entriesToDict(_ arr: NSArray) -> [String: String] {
|
|
357
|
+
var out: [String: String] = [:]
|
|
358
|
+
for case let e as NSDictionary in arr {
|
|
359
|
+
if let k = e["key"] as? String, let v = e["value"] as? String {
|
|
360
|
+
out[k] = v
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
return out
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
private func wireToAuthConfig(_ d: NSDictionary) -> AuthConfig {
|
|
367
|
+
// Wire carries epoch millis; the native recipe wants epoch seconds.
|
|
368
|
+
let expiresAt = (d["expiresAtMs"] as? NSNumber).map { $0.doubleValue / 1000.0 }
|
|
369
|
+
let responseMapping = (d["responseMapping"] as? NSDictionary)
|
|
370
|
+
.map { wireToResponseMapping($0) } ?? AuthResponseMapping()
|
|
371
|
+
return AuthConfig(
|
|
372
|
+
accessToken: d["accessToken"] as? String,
|
|
373
|
+
refreshToken: d["refreshToken"] as? String,
|
|
374
|
+
expiresAt: expiresAt,
|
|
375
|
+
strategy: authStrategyFromWire(d["strategy"] as? String),
|
|
376
|
+
refreshUrl: (d["refreshUrl"] as? String).flatMap { URL(string: $0) },
|
|
377
|
+
refreshPayload: entriesToDict((d["refreshPayload"] as? NSArray) ?? []),
|
|
378
|
+
refreshHeaders: entriesToDict((d["refreshHeaders"] as? NSArray) ?? []),
|
|
379
|
+
refreshBodyFormat: authBodyFormatFromWire(d["refreshBodyFormat"] as? String),
|
|
380
|
+
responseMapping: responseMapping,
|
|
381
|
+
skewMarginSeconds: (d["skewMarginSeconds"] as? NSNumber)?.doubleValue ?? 60,
|
|
382
|
+
seedEpoch: (d["seedEpoch"] as? NSNumber)?.intValue
|
|
383
|
+
)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private func wireToResponseMapping(_ d: NSDictionary) -> AuthResponseMapping {
|
|
387
|
+
return AuthResponseMapping(
|
|
388
|
+
accessToken: d["accessToken"] as? String,
|
|
389
|
+
refreshToken: d["refreshToken"] as? String,
|
|
390
|
+
expiresIn: d["expiresIn"] as? String,
|
|
391
|
+
expiresAt: d["expiresAt"] as? String
|
|
392
|
+
)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// MARK: - Mappers: Beekon → wire (NSDictionary)
|
|
396
|
+
|
|
145
397
|
private func locationToWire(_ loc: Location) -> NSDictionary {
|
|
146
|
-
// NSDictionary literals can't carry `nil
|
|
147
|
-
//
|
|
398
|
+
// NSDictionary literals can't carry `nil`, so optionals collapse to
|
|
399
|
+
// `NSNull` — the JS Codegen layer translates that back to `null`.
|
|
148
400
|
let d = NSMutableDictionary()
|
|
149
|
-
d["
|
|
150
|
-
d["
|
|
401
|
+
d["id"] = loc.id
|
|
402
|
+
d["lat"] = loc.latitude
|
|
403
|
+
d["lng"] = loc.longitude
|
|
151
404
|
d["timestampMs"] = loc.timestamp.timeIntervalSince1970 * 1000.0
|
|
152
405
|
d["accuracy"] = loc.accuracy.map { $0 as Any } ?? NSNull()
|
|
153
406
|
d["speed"] = loc.speed.map { $0 as Any } ?? NSNull()
|
|
154
407
|
d["bearing"] = loc.bearing.map { $0 as Any } ?? NSNull()
|
|
155
408
|
d["altitude"] = loc.altitude.map { $0 as Any } ?? NSNull()
|
|
409
|
+
d["quality"] = qualityToWire(loc.quality)
|
|
410
|
+
d["trigger"] = triggerToWire(loc.trigger)
|
|
411
|
+
d["motion"] = motionToWire(loc.motion)
|
|
412
|
+
d["activity"] = loc.activity.map { activityToWire($0) as Any } ?? NSNull()
|
|
413
|
+
d["isMock"] = loc.isMock
|
|
156
414
|
return d
|
|
157
415
|
}
|
|
158
416
|
|
|
417
|
+
private func geofenceToWire(_ g: BeekonGeofence) -> NSDictionary {
|
|
418
|
+
return [
|
|
419
|
+
"id": g.id,
|
|
420
|
+
"lat": g.latitude,
|
|
421
|
+
"lng": g.longitude,
|
|
422
|
+
"radiusMeters": g.radiusMeters,
|
|
423
|
+
"notifyOnEntry": g.notifyOnEntry,
|
|
424
|
+
"notifyOnExit": g.notifyOnExit,
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
private func geofenceEventToWire(_ e: GeofenceEvent) -> NSDictionary {
|
|
429
|
+
return [
|
|
430
|
+
"id": e.id,
|
|
431
|
+
"geofenceId": e.geofenceId,
|
|
432
|
+
"type": transitionToWire(e.type),
|
|
433
|
+
"timestampMs": e.timestamp.timeIntervalSince1970 * 1000.0,
|
|
434
|
+
]
|
|
435
|
+
}
|
|
436
|
+
|
|
159
437
|
private func stateToWire(_ s: BeekonState) -> NSDictionary {
|
|
160
438
|
switch s {
|
|
161
439
|
case .idle:
|
|
@@ -164,6 +442,66 @@ import BeekonKit
|
|
|
164
442
|
return ["type": "tracking"]
|
|
165
443
|
case .stopped(let reason):
|
|
166
444
|
return ["type": "stopped", "stopReason": stopReasonToWire(reason)]
|
|
445
|
+
// BeekonKit enums are non-frozen (library-evolution binary); handle unknowns.
|
|
446
|
+
@unknown default:
|
|
447
|
+
return ["type": "idle"]
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
private func syncStatusToWire(_ s: SyncStatus) -> NSDictionary {
|
|
452
|
+
switch s {
|
|
453
|
+
case .idle:
|
|
454
|
+
return ["type": "idle"]
|
|
455
|
+
case .pending:
|
|
456
|
+
return ["type": "pending"]
|
|
457
|
+
case .failed(let reason):
|
|
458
|
+
return ["type": "failed", "failure": syncFailureToWire(reason)]
|
|
459
|
+
@unknown default:
|
|
460
|
+
return ["type": "idle"]
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private func authTokensToWire(_ t: AuthTokens) -> NSDictionary {
|
|
465
|
+
// NSDictionary literals can't carry `nil`; optionals collapse to `NSNull`,
|
|
466
|
+
// which the Codegen layer translates back to `null` on the JS side.
|
|
467
|
+
let d = NSMutableDictionary()
|
|
468
|
+
d["accessToken"] = t.accessToken
|
|
469
|
+
d["refreshToken"] = t.refreshToken.map { $0 as Any } ?? NSNull()
|
|
470
|
+
// Native epoch seconds → wire epoch millis.
|
|
471
|
+
d["expiresAtMs"] = t.expiresAt.map { ($0 * 1000.0) as Any } ?? NSNull()
|
|
472
|
+
d["epoch"] = t.epoch
|
|
473
|
+
return d
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// MARK: - Enum mappers
|
|
477
|
+
|
|
478
|
+
private func accuracyModeFromWire(_ s: String?) -> AccuracyMode {
|
|
479
|
+
switch s {
|
|
480
|
+
case "high": return .high
|
|
481
|
+
case "low": return .low
|
|
482
|
+
default: return .balanced
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
private func stationaryModeFromWire(_ s: String?) -> StationaryMode {
|
|
487
|
+
switch s {
|
|
488
|
+
case "keepTracking": return .keepTracking
|
|
489
|
+
case "pauseWithCheckIns": return .pauseWithCheckIns
|
|
490
|
+
default: return .pause
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
private func authStrategyFromWire(_ s: String?) -> AuthStrategy {
|
|
495
|
+
switch s {
|
|
496
|
+
case "raw": return .raw
|
|
497
|
+
default: return .bearer
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private func authBodyFormatFromWire(_ s: String?) -> AuthBodyFormat {
|
|
502
|
+
switch s {
|
|
503
|
+
case "json": return .json
|
|
504
|
+
default: return .form
|
|
167
505
|
}
|
|
168
506
|
}
|
|
169
507
|
|
|
@@ -172,18 +510,84 @@ import BeekonKit
|
|
|
172
510
|
case .user: return "user"
|
|
173
511
|
case .permissionDenied: return "permissionDenied"
|
|
174
512
|
case .locationServicesDisabled: return "locationServicesDisabled"
|
|
513
|
+
case .locationUnavailable: return "locationUnavailable"
|
|
514
|
+
case .system: return "system"
|
|
515
|
+
@unknown default: return "system"
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
private func syncFailureToWire(_ f: SyncFailure) -> String {
|
|
520
|
+
switch f {
|
|
521
|
+
case .auth: return "auth"
|
|
522
|
+
case .rejected: return "rejected"
|
|
523
|
+
@unknown default: return "rejected"
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
private func qualityToWire(_ q: LocationQuality) -> String {
|
|
528
|
+
switch q {
|
|
529
|
+
case .ok: return "ok"
|
|
530
|
+
case .lowAccuracy: return "lowAccuracy"
|
|
531
|
+
case .implausibleSpeed: return "implausibleSpeed"
|
|
532
|
+
@unknown default: return "ok"
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
private func triggerToWire(_ t: LocationTrigger) -> String {
|
|
537
|
+
switch t {
|
|
538
|
+
case .interval: return "interval"
|
|
539
|
+
case .motion: return "motion"
|
|
540
|
+
case .checkIn: return "checkIn"
|
|
541
|
+
case .geofence: return "geofence"
|
|
542
|
+
case .manual: return "manual"
|
|
543
|
+
@unknown default: return "interval"
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
private func motionToWire(_ m: MotionState) -> String {
|
|
548
|
+
switch m {
|
|
549
|
+
case .moving: return "moving"
|
|
550
|
+
case .stationary: return "stationary"
|
|
551
|
+
case .unknown: return "unknown"
|
|
552
|
+
@unknown default: return "unknown"
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
private func activityToWire(_ a: ActivityType) -> String {
|
|
557
|
+
switch a {
|
|
558
|
+
case .stationary: return "stationary"
|
|
559
|
+
case .walking: return "walking"
|
|
560
|
+
case .running: return "running"
|
|
561
|
+
case .cycling: return "cycling"
|
|
562
|
+
case .automotive: return "automotive"
|
|
563
|
+
case .unknown: return "unknown"
|
|
564
|
+
@unknown default: return "unknown"
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
private func transitionToWire(_ t: Transition) -> String {
|
|
569
|
+
switch t {
|
|
570
|
+
case .enter: return "enter"
|
|
571
|
+
case .exit: return "exit"
|
|
572
|
+
@unknown default: return "enter"
|
|
175
573
|
}
|
|
176
574
|
}
|
|
177
575
|
|
|
178
|
-
/// Maps native `BeekonError` cases to stable string codes shared with
|
|
179
|
-
///
|
|
180
|
-
/// branches.
|
|
576
|
+
/// Maps native `BeekonError` cases to stable string codes shared with Android,
|
|
577
|
+
/// so JS-side error handling can switch on them without per-platform branches.
|
|
181
578
|
private func errorCode(_ e: Error) -> String {
|
|
182
579
|
if let be = e as? BeekonError {
|
|
183
580
|
switch be {
|
|
184
|
-
case .
|
|
185
|
-
case .
|
|
186
|
-
case .
|
|
581
|
+
case .storage: return "STORAGE_FAILURE"
|
|
582
|
+
case .invalidGeofence: return "INVALID_GEOFENCE"
|
|
583
|
+
case .locationUnavailable(let reason):
|
|
584
|
+
switch reason {
|
|
585
|
+
case .permissionDenied: return "PERMISSION_DENIED"
|
|
586
|
+
case .locationServicesDisabled: return "LOCATION_SERVICES_DISABLED"
|
|
587
|
+
case .unavailable: return "LOCATION_UNAVAILABLE"
|
|
588
|
+
@unknown default: return "LOCATION_UNAVAILABLE"
|
|
589
|
+
}
|
|
590
|
+
@unknown default: return "INTERNAL_ERROR"
|
|
187
591
|
}
|
|
188
592
|
}
|
|
189
593
|
return "INTERNAL_ERROR"
|
|
@@ -8,32 +8,32 @@
|
|
|
8
8
|
<key>BinaryPath</key>
|
|
9
9
|
<string>BeekonKit.framework/BeekonKit</string>
|
|
10
10
|
<key>LibraryIdentifier</key>
|
|
11
|
-
<string>ios-
|
|
11
|
+
<string>ios-arm64</string>
|
|
12
12
|
<key>LibraryPath</key>
|
|
13
13
|
<string>BeekonKit.framework</string>
|
|
14
14
|
<key>SupportedArchitectures</key>
|
|
15
15
|
<array>
|
|
16
16
|
<string>arm64</string>
|
|
17
|
-
<string>x86_64</string>
|
|
18
17
|
</array>
|
|
19
18
|
<key>SupportedPlatform</key>
|
|
20
19
|
<string>ios</string>
|
|
21
|
-
<key>SupportedPlatformVariant</key>
|
|
22
|
-
<string>simulator</string>
|
|
23
20
|
</dict>
|
|
24
21
|
<dict>
|
|
25
22
|
<key>BinaryPath</key>
|
|
26
23
|
<string>BeekonKit.framework/BeekonKit</string>
|
|
27
24
|
<key>LibraryIdentifier</key>
|
|
28
|
-
<string>ios-
|
|
25
|
+
<string>ios-arm64_x86_64-simulator</string>
|
|
29
26
|
<key>LibraryPath</key>
|
|
30
27
|
<string>BeekonKit.framework</string>
|
|
31
28
|
<key>SupportedArchitectures</key>
|
|
32
29
|
<array>
|
|
33
30
|
<string>arm64</string>
|
|
31
|
+
<string>x86_64</string>
|
|
34
32
|
</array>
|
|
35
33
|
<key>SupportedPlatform</key>
|
|
36
34
|
<string>ios</string>
|
|
35
|
+
<key>SupportedPlatformVariant</key>
|
|
36
|
+
<string>simulator</string>
|
|
37
37
|
</dict>
|
|
38
38
|
</array>
|
|
39
39
|
<key>CFBundlePackageType</key>
|
|
Binary file
|