expo-beacon 0.8.6 → 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")
@@ -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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-beacon",
3
- "version": "0.8.6",
3
+ "version": "0.8.7",
4
4
  "description": "Expo module for scanning, pairing, and monitoring iBeacons on Android and iOS",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",