expo-beacon 0.5.2 → 0.5.4

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.
@@ -242,20 +242,21 @@ public class ExpoBeaconModule: Module {
242
242
  // MARK: - Eddystone Pair
243
243
 
244
244
  Function("pairEddystone") { (identifier: String, namespace: String, instance: String) -> Void in
245
- let hexPattern = /^[0-9a-fA-F]+$/
246
- guard namespace.count == 20, namespace.wholeMatch(of: hexPattern) != nil else {
245
+ guard namespace.count == 20, namespace.range(of: "^[0-9a-fA-F]+$", options: .regularExpression) != nil else {
247
246
  throw Exception(name: "INVALID_NAMESPACE", description: "Namespace must be 20 hex characters, got: \(namespace)")
248
247
  }
249
- guard instance.count == 12, instance.wholeMatch(of: hexPattern) != nil else {
248
+ guard instance.count == 12, instance.range(of: "^[0-9a-fA-F]+$", options: .regularExpression) != nil else {
250
249
  throw Exception(name: "INVALID_INSTANCE", description: "Instance must be 12 hex characters, got: \(instance)")
251
250
  }
252
251
 
253
252
  var eddystones = self.loadPairedEddystonesRaw()
254
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.
255
256
  eddystones.append([
256
257
  "identifier": identifier,
257
- "namespace": namespace,
258
- "instance": instance
258
+ "namespace": namespace.lowercased(),
259
+ "instance": instance.lowercased()
259
260
  ])
260
261
  self.defaults.set(eddystones, forKey: PAIRED_EDDYSTONES_KEY)
261
262
  self.cachedPairedEddystones = nil
@@ -663,27 +664,14 @@ public class ExpoBeaconModule: Module {
663
664
  guard let identifier = paired["identifier"] as? String,
664
665
  let pns = paired["namespace"] as? String,
665
666
  let pinst = paired["instance"] as? String,
666
- pns == ns && pinst == inst else { continue }
667
+ pns.lowercased() == ns && pinst.lowercased() == inst else { continue }
667
668
 
668
669
  eddystoneLatestSeen[identifier] = Date()
669
670
  eddystoneMissCounters[identifier] = 0
670
671
 
671
- // Throttle distance events
672
- let now = Date()
673
- if let lastEmit = eddystoneLastDistanceEmit[identifier],
674
- now.timeIntervalSince(lastEmit) < DISTANCE_EVENT_THROTTLE_INTERVAL {
675
- break
676
- }
677
- eddystoneLastDistanceEmit[identifier] = now
678
-
679
- sendEvent("onEddystoneDistance", [
680
- "identifier": identifier,
681
- "namespace": ns,
682
- "instance": inst,
683
- "distance": distance
684
- ])
685
-
686
- // Distance-driven enter/exit with hysteresis
672
+ // Distance-driven enter/exit with hysteresis — evaluated on every
673
+ // BLE callback (not throttled) so the hysteresis counters advance
674
+ // reliably regardless of advertisement rate.
687
675
  let maxDist = self.defaults.object(forKey: MAX_DISTANCE_KEY) as? Double
688
676
  let exitDist = self.defaults.object(forKey: EXIT_DISTANCE_KEY) as? Double
689
677
  let action = evaluateDistanceHysteresis(
@@ -717,6 +705,22 @@ public class ExpoBeaconModule: Module {
717
705
  case .none:
718
706
  break
719
707
  }
708
+
709
+ // Throttle distance events — enter/exit above is evaluated on every
710
+ // callback, but distance events are rate-limited to avoid flooding JS.
711
+ let now = Date()
712
+ if let lastEmit = eddystoneLastDistanceEmit[identifier],
713
+ now.timeIntervalSince(lastEmit) < DISTANCE_EVENT_THROTTLE_INTERVAL {
714
+ break
715
+ }
716
+ eddystoneLastDistanceEmit[identifier] = now
717
+
718
+ sendEvent("onEddystoneDistance", [
719
+ "identifier": identifier,
720
+ "namespace": ns,
721
+ "instance": inst,
722
+ "distance": distance
723
+ ])
720
724
  break
721
725
  }
722
726
  }
@@ -1214,9 +1218,12 @@ private class BluetoothDelegate: NSObject, CBCentralManagerDelegate {
1214
1218
  case .poweredOn:
1215
1219
  module?.ensureBleScanRunning()
1216
1220
  case .unauthorized:
1221
+ print("[ExpoBeacon] Bluetooth authorization denied — Eddystone scanning/monitoring unavailable. " +
1222
+ "Ensure NSBluetoothAlwaysUsageDescription is set in Info.plist.")
1217
1223
  module?.eddystoneScanPromise?.reject("BLUETOOTH_UNAUTHORIZED", "Bluetooth permission denied")
1218
1224
  module?.eddystoneScanPromise = nil
1219
1225
  case .poweredOff:
1226
+ print("[ExpoBeacon] Bluetooth is powered off — Eddystone scanning/monitoring unavailable.")
1220
1227
  module?.eddystoneScanPromise?.reject("BLUETOOTH_OFF", "Bluetooth is powered off")
1221
1228
  module?.eddystoneScanPromise = nil
1222
1229
  default:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-beacon",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
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",