expo-beacon 0.5.3 → 0.5.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.
@@ -251,10 +251,12 @@ public class ExpoBeaconModule: Module {
251
251
 
252
252
  var eddystones = self.loadPairedEddystonesRaw()
253
253
  eddystones.removeAll { ($0["identifier"] as? String) == identifier }
254
+ // Normalize hex to lowercase — parseEddystoneFrame produces lowercase,
255
+ // so stored values must match for monitoring comparisons.
254
256
  eddystones.append([
255
257
  "identifier": identifier,
256
- "namespace": namespace,
257
- "instance": instance
258
+ "namespace": namespace.lowercased(),
259
+ "instance": instance.lowercased()
258
260
  ])
259
261
  self.defaults.set(eddystones, forKey: PAIRED_EDDYSTONES_KEY)
260
262
  self.cachedPairedEddystones = nil
@@ -307,11 +309,17 @@ public class ExpoBeaconModule: Module {
307
309
  self.defaults.removeObject(forKey: EXIT_DISTANCE_KEY)
308
310
  }
309
311
  self.defaults.set(true, forKey: IS_MONITORING_KEY)
310
- self.requestLocationPermission(requireAlways: true) { granted in
312
+ self.requestLocationPermission { granted in
311
313
  guard granted else {
312
- promise.reject("PERMISSION_DENIED", "Always location permission required for background monitoring")
314
+ promise.reject("PERMISSION_DENIED", "Location permission required for monitoring")
313
315
  return
314
316
  }
317
+ // Request Always authorization non-blockingly for background support.
318
+ // On iOS 13+ requestAlwaysAuthorization() from WhenInUse may be a
319
+ // no-op if the user already made their choice — don't block on it.
320
+ if self.locationManager.authorizationStatus != .authorizedAlways {
321
+ self.locationManager.requestAlwaysAuthorization()
322
+ }
315
323
  self.requestNotificationPermission()
316
324
  self.startRegionMonitoring()
317
325
  promise.resolve(nil)
@@ -662,27 +670,14 @@ public class ExpoBeaconModule: Module {
662
670
  guard let identifier = paired["identifier"] as? String,
663
671
  let pns = paired["namespace"] as? String,
664
672
  let pinst = paired["instance"] as? String,
665
- pns == ns && pinst == inst else { continue }
673
+ pns.lowercased() == ns && pinst.lowercased() == inst else { continue }
666
674
 
667
675
  eddystoneLatestSeen[identifier] = Date()
668
676
  eddystoneMissCounters[identifier] = 0
669
677
 
670
- // Throttle distance events
671
- let now = Date()
672
- if let lastEmit = eddystoneLastDistanceEmit[identifier],
673
- now.timeIntervalSince(lastEmit) < DISTANCE_EVENT_THROTTLE_INTERVAL {
674
- break
675
- }
676
- eddystoneLastDistanceEmit[identifier] = now
677
-
678
- sendEvent("onEddystoneDistance", [
679
- "identifier": identifier,
680
- "namespace": ns,
681
- "instance": inst,
682
- "distance": distance
683
- ])
684
-
685
- // Distance-driven enter/exit with hysteresis
678
+ // Distance-driven enter/exit with hysteresis — evaluated on every
679
+ // BLE callback (not throttled) so the hysteresis counters advance
680
+ // reliably regardless of advertisement rate.
686
681
  let maxDist = self.defaults.object(forKey: MAX_DISTANCE_KEY) as? Double
687
682
  let exitDist = self.defaults.object(forKey: EXIT_DISTANCE_KEY) as? Double
688
683
  let action = evaluateDistanceHysteresis(
@@ -716,6 +711,22 @@ public class ExpoBeaconModule: Module {
716
711
  case .none:
717
712
  break
718
713
  }
714
+
715
+ // Throttle distance events — enter/exit above is evaluated on every
716
+ // callback, but distance events are rate-limited to avoid flooding JS.
717
+ let now = Date()
718
+ if let lastEmit = eddystoneLastDistanceEmit[identifier],
719
+ now.timeIntervalSince(lastEmit) < DISTANCE_EVENT_THROTTLE_INTERVAL {
720
+ break
721
+ }
722
+ eddystoneLastDistanceEmit[identifier] = now
723
+
724
+ sendEvent("onEddystoneDistance", [
725
+ "identifier": identifier,
726
+ "namespace": ns,
727
+ "instance": inst,
728
+ "distance": distance
729
+ ])
719
730
  break
720
731
  }
721
732
  }
@@ -1213,9 +1224,12 @@ private class BluetoothDelegate: NSObject, CBCentralManagerDelegate {
1213
1224
  case .poweredOn:
1214
1225
  module?.ensureBleScanRunning()
1215
1226
  case .unauthorized:
1227
+ print("[ExpoBeacon] Bluetooth authorization denied — Eddystone scanning/monitoring unavailable. " +
1228
+ "Ensure NSBluetoothAlwaysUsageDescription is set in Info.plist.")
1216
1229
  module?.eddystoneScanPromise?.reject("BLUETOOTH_UNAUTHORIZED", "Bluetooth permission denied")
1217
1230
  module?.eddystoneScanPromise = nil
1218
1231
  case .poweredOff:
1232
+ print("[ExpoBeacon] Bluetooth is powered off — Eddystone scanning/monitoring unavailable.")
1219
1233
  module?.eddystoneScanPromise?.reject("BLUETOOTH_OFF", "Bluetooth is powered off")
1220
1234
  module?.eddystoneScanPromise = nil
1221
1235
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-beacon",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
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",