expo-beacon 0.8.5 → 0.8.7
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.
|
@@ -380,4 +380,36 @@ extension ExpoBeaconModule {
|
|
|
380
380
|
scanPromise = nil
|
|
381
381
|
}
|
|
382
382
|
}
|
|
383
|
+
|
|
384
|
+
/// Response to `locationManager.requestState(for:)` called during `OnCreate`
|
|
385
|
+
/// after a background process restart. When iOS confirms `.inside`, seed the
|
|
386
|
+
/// enter counter to ENTER_HYSTERESIS_COUNT − 1 so the very first valid ranging
|
|
387
|
+
/// reading immediately fires `onBeaconEnter` without accumulating additional
|
|
388
|
+
/// cycles. When `.outside`, make sure the beacon is not stuck in `enteredRegions`
|
|
389
|
+
/// from a stale prior session.
|
|
390
|
+
func handleDidDetermineState(_ state: CLRegionState, for region: CLRegion) {
|
|
391
|
+
guard let beaconRegion = region as? CLBeaconRegion else { return }
|
|
392
|
+
let identifier = beaconRegion.identifier
|
|
393
|
+
switch state {
|
|
394
|
+
case .inside:
|
|
395
|
+
// Pre-seed enter counter so the next ranging cycle completes the enter.
|
|
396
|
+
// Only set it when we have NOT already entered (avoid re-emitting enter).
|
|
397
|
+
if !enteredRegions.contains(identifier) {
|
|
398
|
+
enterCounters[identifier] = max(enterCounters[identifier] ?? 0, ENTER_HYSTERESIS_COUNT - 1)
|
|
399
|
+
}
|
|
400
|
+
case .outside:
|
|
401
|
+
// If a stale session left the beacon in entered state, clean it up.
|
|
402
|
+
if enteredRegions.remove(identifier) != nil {
|
|
403
|
+
enterCounters.removeValue(forKey: identifier)
|
|
404
|
+
exitCounters.removeValue(forKey: identifier)
|
|
405
|
+
smoothedDistances.removeValue(forKey: identifier)
|
|
406
|
+
sendLoggedEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, region: beaconRegion, event: "exit"))
|
|
407
|
+
postBeaconNotification(identifier: identifier, eventType: "exit")
|
|
408
|
+
}
|
|
409
|
+
case .unknown:
|
|
410
|
+
break
|
|
411
|
+
@unknown default:
|
|
412
|
+
break
|
|
413
|
+
}
|
|
414
|
+
}
|
|
383
415
|
}
|
|
@@ -150,6 +150,27 @@ public class ExpoBeaconModule: Module {
|
|
|
150
150
|
// to this freshly-created module learn the session ended.
|
|
151
151
|
CarPlayMonitor.shared.reconcileOnProcessStart()
|
|
152
152
|
}
|
|
153
|
+
// Restart beacon ranging on process recreation (background relaunch via
|
|
154
|
+
// SLC, Visit, or region-entry wake). iOS persists CLBeaconRegion monitoring
|
|
155
|
+
// at the OS level so the app is woken on region boundary crossings, but
|
|
156
|
+
// ranging is per-process and stops when the process is terminated.
|
|
157
|
+
// Without restarting here, handleDidRange callbacks never fire until JS
|
|
158
|
+
// explicitly calls startMonitoring() — causing the observed 2-5 min
|
|
159
|
+
// detection delay when CarPlay is active and the app has been sleeping
|
|
160
|
+
// for days. ENTER_HYSTERESIS_COUNT=1, so the first valid ranging reading
|
|
161
|
+
// fires onBeaconEnter within ~1 s of ranging resuming.
|
|
162
|
+
if self.defaults.bool(forKey: IS_MONITORING_KEY) {
|
|
163
|
+
let authStatus = self.locationManager.authorizationStatus
|
|
164
|
+
if authStatus == .authorizedAlways || authStatus == .authorizedWhenInUse {
|
|
165
|
+
self.startRegionMonitoring()
|
|
166
|
+
// Ask iOS for immediate region-state delivery: if the device is
|
|
167
|
+
// already inside a monitored region, didDetermineState fires with
|
|
168
|
+
// .inside without waiting for the next organic boundary crossing.
|
|
169
|
+
for region in self.monitoredRegions {
|
|
170
|
+
self.locationManager.requestState(for: region)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
153
174
|
}
|
|
154
175
|
|
|
155
176
|
Events("onBeaconEnter", "onBeaconExit", "onBeaconDistance", "onBeaconTimeout", "onBeaconFound", "onEddystoneFound", "onEddystoneEnter", "onEddystoneExit", "onEddystoneDistance", "onEddystoneTimeout", "onBeaconError", "onCarPlayConnected", "onCarPlayDisconnected")
|
|
@@ -467,12 +488,12 @@ public class ExpoBeaconModule: Module {
|
|
|
467
488
|
self.startCarPlayMonitoringInternal()
|
|
468
489
|
promise.resolve(nil)
|
|
469
490
|
}
|
|
470
|
-
|
|
471
|
-
|
|
491
|
+
|
|
472
492
|
AsyncFunction("stopCarPlayMonitoring") { (promise: Promise) in
|
|
473
493
|
self.defaults.set(false, forKey: CARPLAY_MONITORING_ENABLED_KEY)
|
|
474
494
|
CarPlayMonitor.shared.stop()
|
|
475
495
|
self.stopCarPlayBackgroundWakes()
|
|
496
|
+
self.removeAppForegroundObserverForCarPlay()
|
|
476
497
|
promise.resolve(nil)
|
|
477
498
|
}
|
|
478
499
|
|
|
@@ -633,11 +654,11 @@ self.removeAppForegroundObserverForCarPlay()
|
|
|
633
654
|
// that route changes continue to be observed across module recreations
|
|
634
655
|
// (e.g. background-launch wake → module re-init → OnDestroy on suspend).
|
|
635
656
|
if !self.defaults.bool(forKey: CARPLAY_MONITORING_ENABLED_KEY) {
|
|
636
|
-
|
|
657
|
+
CarPlayMonitor.shared.stop()
|
|
658
|
+
}
|
|
637
659
|
// Foreground observer is bound to this module instance — always
|
|
638
660
|
// remove it on destroy to avoid leaking observers across recreations.
|
|
639
|
-
self.removeAppForegroundObserverForCarPlay()
|
|
640
|
-
}
|
|
661
|
+
self.removeAppForegroundObserverForCarPlay()
|
|
641
662
|
self.centralManager?.stopScan()
|
|
642
663
|
self.centralManager = nil
|
|
643
664
|
self.scanTimer?.cancel()
|
|
@@ -45,4 +45,12 @@ internal final class LocationDelegate: NSObject, CLLocationManagerDelegate {
|
|
|
45
45
|
func locationManager(_ manager: CLLocationManager, didVisit visit: CLVisit) {
|
|
46
46
|
module?.handleBackgroundWakeForCarPlay()
|
|
47
47
|
}
|
|
48
|
+
|
|
49
|
+
// Response to requestState(for:) — called by iOS after a background-process
|
|
50
|
+
// restart to confirm whether the device is currently inside a monitored
|
|
51
|
+
// region. Forwarded so the module can log and take action (e.g. emit a
|
|
52
|
+
// synthetic enter if state is .inside and ranging has not yet delivered data).
|
|
53
|
+
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
|
|
54
|
+
module?.handleDidDetermineState(state, for: region)
|
|
55
|
+
}
|
|
48
56
|
}
|