expo-beacon 0.5.4 → 0.5.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.
@@ -176,7 +176,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
176
176
  }
177
177
  }
178
178
 
179
- Function("pairBeacon") { identifier: String, uuid: String, major: Int, minor: Int ->
179
+ Function("pairBeacon") { identifier: String, uuid: String, major: Int, minor: Int, name: String? ->
180
180
  // Validate UUID format
181
181
  try {
182
182
  java.util.UUID.fromString(uuid)
@@ -198,6 +198,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
198
198
  put("uuid", uuid)
199
199
  put("major", major)
200
200
  put("minor", minor)
201
+ if (name != null) put("name", name)
201
202
  }
202
203
  beacons.put(newBeacon)
203
204
  prefs.edit().putString(PREFS_KEY, beacons.toString()).apply()
@@ -212,16 +213,18 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
212
213
  val beacons = loadPairedBeaconsJson()
213
214
  (0 until beacons.length()).map { i ->
214
215
  val b = beacons.getJSONObject(i)
215
- mapOf(
216
- "identifier" to b.getString("identifier"),
217
- "uuid" to b.getString("uuid"),
218
- "major" to b.getInt("major"),
219
- "minor" to b.getInt("minor")
220
- )
216
+ buildMap<String, Any?> {
217
+ put("identifier", b.getString("identifier"))
218
+ put("uuid", b.getString("uuid"))
219
+ put("major", b.getInt("major"))
220
+ put("minor", b.getInt("minor"))
221
+ val n = b.optString("name").takeIf { it.isNotEmpty() }
222
+ if (n != null) put("name", n)
223
+ }
221
224
  }
222
225
  }
223
226
 
224
- Function("pairEddystone") { identifier: String, namespace: String, instance: String ->
227
+ Function("pairEddystone") { identifier: String, namespace: String, instance: String, name: String? ->
225
228
  if (!namespace.matches(NAMESPACE_REGEX)) {
226
229
  throw expo.modules.kotlin.exception.CodedException("INVALID_NAMESPACE", "Namespace must be 20 hex characters, got: $namespace", null)
227
230
  }
@@ -236,6 +239,7 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
236
239
  put("identifier", identifier)
237
240
  put("namespace", namespace)
238
241
  put("instance", instance)
242
+ if (name != null) put("name", name)
239
243
  }
240
244
  eddystones.put(newEddystone)
241
245
  eddystonePrefs.edit().putString(EDDYSTONE_PREFS_KEY, eddystones.toString()).apply()
@@ -250,11 +254,13 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
250
254
  val eddystones = loadPairedEddystonesJson()
251
255
  (0 until eddystones.length()).map { i ->
252
256
  val e = eddystones.getJSONObject(i)
253
- mapOf(
254
- "identifier" to e.getString("identifier"),
255
- "namespace" to e.getString("namespace"),
256
- "instance" to e.getString("instance")
257
- )
257
+ buildMap<String, Any?> {
258
+ put("identifier", e.getString("identifier"))
259
+ put("namespace", e.getString("namespace"))
260
+ put("instance", e.getString("instance"))
261
+ val n = e.optString("name").takeIf { it.isNotEmpty() }
262
+ if (n != null) put("name", n)
263
+ }
258
264
  }
259
265
  }
260
266
 
@@ -446,14 +452,15 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
446
452
  if (isEddystoneBeacon(beacon)) {
447
453
  sendEvent("onEddystoneFound", eddystoneBeaconToMap(beacon))
448
454
  } else if (beacon.identifiers.size >= 3) {
449
- sendEvent("onBeaconFound", mapOf(
450
- "uuid" to beacon.id1.toString().uppercase(),
451
- "major" to beacon.id2.toInt(),
452
- "minor" to beacon.id3.toInt(),
453
- "rssi" to beacon.rssi,
454
- "distance" to beacon.distance,
455
- "txPower" to beacon.txPower
456
- ))
455
+ sendEvent("onBeaconFound", buildMap<String, Any?> {
456
+ put("uuid", beacon.id1.toString().uppercase())
457
+ put("major", beacon.id2.toInt())
458
+ put("minor", beacon.id3.toInt())
459
+ put("rssi", beacon.rssi)
460
+ put("distance", beacon.distance)
461
+ put("txPower", beacon.txPower)
462
+ beacon.bluetoothName?.let { put("name", it) }
463
+ })
457
464
  }
458
465
  }
459
466
  }
@@ -475,14 +482,15 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
475
482
 
476
483
  val results = synchronized(scanResults) {
477
484
  val mapped = scanResults.distinctBy { "${it.id1}:${it.id2}:${it.id3}" }.map { beacon ->
478
- mapOf(
479
- "uuid" to beacon.id1.toString().uppercase(),
480
- "major" to beacon.id2.toInt(),
481
- "minor" to beacon.id3.toInt(),
482
- "rssi" to beacon.rssi,
483
- "distance" to beacon.distance,
484
- "txPower" to beacon.txPower
485
- )
485
+ buildMap<String, Any?> {
486
+ put("uuid", beacon.id1.toString().uppercase())
487
+ put("major", beacon.id2.toInt())
488
+ put("minor", beacon.id3.toInt())
489
+ put("rssi", beacon.rssi)
490
+ put("distance", beacon.distance)
491
+ put("txPower", beacon.txPower)
492
+ beacon.bluetoothName?.let { put("name", it) }
493
+ }
486
494
  }
487
495
  scanResults.clear()
488
496
  mapped
@@ -516,24 +524,24 @@ class ExpoBeaconModule : Module(), BeaconConsumer {
516
524
  unbindIfIdle()
517
525
  }
518
526
 
519
- private fun eddystoneBeaconToMap(beacon: Beacon): Map<String, Any> {
527
+ private fun eddystoneBeaconToMap(beacon: Beacon): Map<String, Any?> {
520
528
  // AltBeacon provides distance via its built-in path-loss model.
521
529
  // iOS uses a custom calculateDistance() with NaN/Infinity clamping for Eddystone.
522
530
  // Both return -1.0 for invalid readings, but distance estimates may differ slightly.
523
- val map = mutableMapOf<String, Any>(
524
- "rssi" to beacon.rssi,
525
- "distance" to beacon.distance,
526
- "txPower" to beacon.txPower
527
- )
528
- if (beacon.identifiers.size >= 2) {
529
- map["frameType"] = "uid"
530
- map["namespace"] = beacon.id1.toString().removePrefix("0x")
531
- map["instance"] = beacon.id2.toString().removePrefix("0x")
532
- } else {
533
- map["frameType"] = "url"
534
- map["url"] = decodeEddystoneUrl(beacon.id1.toByteArray())
531
+ return buildMap {
532
+ put("rssi", beacon.rssi)
533
+ put("distance", beacon.distance)
534
+ put("txPower", beacon.txPower)
535
+ if (beacon.identifiers.size >= 2) {
536
+ put("frameType", "uid")
537
+ put("namespace", beacon.id1.toString().removePrefix("0x"))
538
+ put("instance", beacon.id2.toString().removePrefix("0x"))
539
+ } else {
540
+ put("frameType", "url")
541
+ put("url", decodeEddystoneUrl(beacon.id1.toByteArray()))
542
+ }
543
+ beacon.bluetoothName?.let { put("name", it) }
535
544
  }
536
- return map
537
545
  }
538
546
 
539
547
  // Decodes an Eddystone-URL payload from AltBeacon's id1 byte array.
@@ -6,6 +6,8 @@ export type BeaconScanResult = {
6
6
  rssi: number;
7
7
  distance: number;
8
8
  txPower: number;
9
+ /** BLE advertising device name. May be undefined on iOS (CoreLocation does not expose it for iBeacon). */
10
+ name?: string;
9
11
  };
10
12
  /**
11
13
  * A beacon that has been paired/registered for monitoring.
@@ -18,6 +20,8 @@ export type PairedBeacon = {
18
20
  uuid: string;
19
21
  major: number;
20
22
  minor: number;
23
+ /** BLE advertising device name, if provided at pairing time. */
24
+ name?: string;
21
25
  };
22
26
  /** Payload for enter/exit region events. */
23
27
  export type BeaconRegionEvent = {
@@ -118,6 +122,8 @@ export type EddystoneScanResult = {
118
122
  rssi: number;
119
123
  distance: number;
120
124
  txPower: number;
125
+ /** BLE advertising device name. */
126
+ name?: string;
121
127
  };
122
128
  /**
123
129
  * An Eddystone-UID beacon that has been paired/registered for monitoring.
@@ -131,6 +137,8 @@ export type PairedEddystone = {
131
137
  namespace: string;
132
138
  /** 6-byte instance ID as hex string (12 chars). */
133
139
  instance: string;
140
+ /** BLE advertising device name, if provided at pairing time. */
141
+ name?: string;
134
142
  };
135
143
  /** Payload for Eddystone enter/exit region events. */
136
144
  export type EddystoneRegionEvent = {
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoBeacon.types.d.ts","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ExpoBeacon.types.d.ts","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoBeacon.types.js","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"","sourcesContent":["/** Raw beacon discovered during a scan. */\r\nexport type BeaconScanResult = {\r\n uuid: string; // iBeacon proximity UUID (uppercase, formatted)\r\n major: number; // iBeacon major value (0–65535)\r\n minor: number; // iBeacon minor value (0–65535)\r\n rssi: number; // Signal strength in dBm (negative number)\r\n distance: number; // Estimated distance in meters\r\n txPower: number; // Calibrated TX power\r\n};\r\n\r\n/**\r\n * A beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedBeacon = {\r\n identifier: string; // User-defined label (e.g. \"lobby-door\")\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n};\r\n\r\n/** Payload for enter/exit region events. */\r\nexport type BeaconRegionEvent = {\r\n identifier: string; // Matches PairedBeacon.identifier\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n};\r\n\r\n/** Payload for periodic distance update events during monitoring. */\r\nexport type BeaconDistanceEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n distance: number;\r\n};\r\n\r\n/** Configuration for beacon enter/exit event notifications. */\r\nexport type BeaconNotificationConfig = {\r\n /** Whether to show enter/exit notifications. Default: true. */\r\n enabled?: boolean;\r\n /** Notification title on beacon enter. Default: \"Beacon Entered\". */\r\n enterTitle?: string;\r\n /** Notification title on beacon exit. Default: \"Beacon Exited\". */\r\n exitTitle?: string;\r\n /**\r\n * Notification body template. Supports {identifier} and {event} placeholders.\r\n * Default: \"{identifier} region {event}ed\".\r\n */\r\n body?: string;\r\n /** Play a sound with the notification (iOS only). Default: true. */\r\n sound?: boolean;\r\n /** Android drawable resource name for the notification icon (e.g. \"ic_notification\"). */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android foreground service notification (persistent status bar entry). */\r\nexport type ForegroundServiceConfig = {\r\n /** Title of the persistent notification. Default: \"Beacon Monitoring Active\". */\r\n title?: string;\r\n /** Body text of the persistent notification. Default: \"Monitoring for iBeacons in the background\". */\r\n text?: string;\r\n /** Android drawable resource name for the notification icon. */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android notification channel. */\r\nexport type NotificationChannelConfig = {\r\n /** Channel display name shown in system settings. Default: \"Beacon Monitoring\". */\r\n name?: string;\r\n /** Channel description shown in system settings. Default: \"Used for background iBeacon region monitoring\". */\r\n description?: string;\r\n /**\r\n * Channel importance level. Default: 'low'.\r\n * Note: Android may ignore decreases in importance after first channel creation until the app is reinstalled.\r\n */\r\n importance?: \"low\" | \"default\" | \"high\";\r\n};\r\n\r\n/** Combined notification configuration for all notification types. */\r\nexport type NotificationConfig = {\r\n /** Settings for beacon enter/exit event notifications. */\r\n beaconEvents?: BeaconNotificationConfig;\r\n /** Settings for the persistent foreground service notification (Android only). */\r\n foregroundService?: ForegroundServiceConfig;\r\n /** Settings for the Android notification channel (Android only). */\r\n channel?: NotificationChannelConfig;\r\n};\r\n\r\n/** Options accepted by startMonitoring(). */\r\nexport type MonitoringOptions = {\r\n /**\r\n * Maximum distance in metres for distance-based enter events.\r\n * Exit events are always emitted when the region is lost.\r\n */\r\n maxDistance?: number;\r\n /**\r\n * Distance in metres at which exit events fire (must be ≥ maxDistance).\r\n * Creates a hysteresis band between enter and exit thresholds to prevent\r\n * rapid toggling near the boundary.\r\n *\r\n * Default when omitted: `maxDistance + min(maxDistance × 0.5, 2.5)`.\r\n * Only used when `maxDistance` is set.\r\n */\r\n exitDistance?: number;\r\n /** Notification configuration overrides to apply for this monitoring session. */\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Eddystone frame type. */\r\nexport type EddystoneFrameType = \"uid\" | \"url\";\r\n\r\n/** Raw Eddystone beacon discovered during a scan. */\r\nexport type EddystoneScanResult = {\r\n frameType: EddystoneFrameType;\r\n /** 10-byte namespace ID as hex string (20 chars). Present for UID frames. */\r\n namespace?: string;\r\n /** 6-byte instance ID as hex string (12 chars). Present for UID frames. */\r\n instance?: string;\r\n /** Decoded URL. Present for URL frames. */\r\n url?: string;\r\n rssi: number;\r\n distance: number;\r\n txPower: number;\r\n};\r\n\r\n/**\r\n * An Eddystone-UID beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedEddystone = {\r\n identifier: string;\r\n /** 10-byte namespace ID as hex string (20 chars). */\r\n namespace: string;\r\n /** 6-byte instance ID as hex string (12 chars). */\r\n instance: string;\r\n};\r\n\r\n/** Payload for Eddystone enter/exit region events. */\r\nexport type EddystoneRegionEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n};\r\n\r\n/** Payload for periodic Eddystone distance update events during monitoring. */\r\nexport type EddystoneDistanceEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n distance: number;\r\n};\r\n\r\n/** Module event map. */\r\nexport type ExpoBeaconModuleEvents = {\r\n onBeaconEnter: (params: BeaconRegionEvent) => void;\r\n onBeaconExit: (params: BeaconRegionEvent) => void;\r\n onBeaconDistance: (params: BeaconDistanceEvent) => void;\r\n /** Fired continuously during a live scan as each iBeacon is detected. */\r\n onBeaconFound: (params: BeaconScanResult) => void;\r\n /** Fired continuously during a live scan as each Eddystone beacon is detected. */\r\n onEddystoneFound: (params: EddystoneScanResult) => void;\r\n onEddystoneEnter: (params: EddystoneRegionEvent) => void;\r\n onEddystoneExit: (params: EddystoneRegionEvent) => void;\r\n onEddystoneDistance: (params: EddystoneDistanceEvent) => void;\r\n};\r\n"]}
1
+ {"version":3,"file":"ExpoBeacon.types.js","sourceRoot":"","sources":["../src/ExpoBeacon.types.ts"],"names":[],"mappings":"","sourcesContent":["/** Raw beacon discovered during a scan. */\r\nexport type BeaconScanResult = {\r\n uuid: string; // iBeacon proximity UUID (uppercase, formatted)\r\n major: number; // iBeacon major value (0–65535)\r\n minor: number; // iBeacon minor value (0–65535)\r\n rssi: number; // Signal strength in dBm (negative number)\r\n distance: number; // Estimated distance in meters\r\n txPower: number; // Calibrated TX power\r\n /** BLE advertising device name. May be undefined on iOS (CoreLocation does not expose it for iBeacon). */\r\n name?: string;\r\n};\r\n\r\n/**\r\n * A beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedBeacon = {\r\n identifier: string; // User-defined label (e.g. \"lobby-door\")\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n /** BLE advertising device name, if provided at pairing time. */\r\n name?: string;\r\n};\r\n\r\n/** Payload for enter/exit region events. */\r\nexport type BeaconRegionEvent = {\r\n identifier: string; // Matches PairedBeacon.identifier\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n};\r\n\r\n/** Payload for periodic distance update events during monitoring. */\r\nexport type BeaconDistanceEvent = {\r\n identifier: string;\r\n uuid: string;\r\n major: number;\r\n minor: number;\r\n distance: number;\r\n};\r\n\r\n/** Configuration for beacon enter/exit event notifications. */\r\nexport type BeaconNotificationConfig = {\r\n /** Whether to show enter/exit notifications. Default: true. */\r\n enabled?: boolean;\r\n /** Notification title on beacon enter. Default: \"Beacon Entered\". */\r\n enterTitle?: string;\r\n /** Notification title on beacon exit. Default: \"Beacon Exited\". */\r\n exitTitle?: string;\r\n /**\r\n * Notification body template. Supports {identifier} and {event} placeholders.\r\n * Default: \"{identifier} region {event}ed\".\r\n */\r\n body?: string;\r\n /** Play a sound with the notification (iOS only). Default: true. */\r\n sound?: boolean;\r\n /** Android drawable resource name for the notification icon (e.g. \"ic_notification\"). */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android foreground service notification (persistent status bar entry). */\r\nexport type ForegroundServiceConfig = {\r\n /** Title of the persistent notification. Default: \"Beacon Monitoring Active\". */\r\n title?: string;\r\n /** Body text of the persistent notification. Default: \"Monitoring for iBeacons in the background\". */\r\n text?: string;\r\n /** Android drawable resource name for the notification icon. */\r\n icon?: string;\r\n};\r\n\r\n/** Configuration for the Android notification channel. */\r\nexport type NotificationChannelConfig = {\r\n /** Channel display name shown in system settings. Default: \"Beacon Monitoring\". */\r\n name?: string;\r\n /** Channel description shown in system settings. Default: \"Used for background iBeacon region monitoring\". */\r\n description?: string;\r\n /**\r\n * Channel importance level. Default: 'low'.\r\n * Note: Android may ignore decreases in importance after first channel creation until the app is reinstalled.\r\n */\r\n importance?: \"low\" | \"default\" | \"high\";\r\n};\r\n\r\n/** Combined notification configuration for all notification types. */\r\nexport type NotificationConfig = {\r\n /** Settings for beacon enter/exit event notifications. */\r\n beaconEvents?: BeaconNotificationConfig;\r\n /** Settings for the persistent foreground service notification (Android only). */\r\n foregroundService?: ForegroundServiceConfig;\r\n /** Settings for the Android notification channel (Android only). */\r\n channel?: NotificationChannelConfig;\r\n};\r\n\r\n/** Options accepted by startMonitoring(). */\r\nexport type MonitoringOptions = {\r\n /**\r\n * Maximum distance in metres for distance-based enter events.\r\n * Exit events are always emitted when the region is lost.\r\n */\r\n maxDistance?: number;\r\n /**\r\n * Distance in metres at which exit events fire (must be ≥ maxDistance).\r\n * Creates a hysteresis band between enter and exit thresholds to prevent\r\n * rapid toggling near the boundary.\r\n *\r\n * Default when omitted: `maxDistance + min(maxDistance × 0.5, 2.5)`.\r\n * Only used when `maxDistance` is set.\r\n */\r\n exitDistance?: number;\r\n /** Notification configuration overrides to apply for this monitoring session. */\r\n notifications?: NotificationConfig;\r\n};\r\n\r\n/** Eddystone frame type. */\r\nexport type EddystoneFrameType = \"uid\" | \"url\";\r\n\r\n/** Raw Eddystone beacon discovered during a scan. */\r\nexport type EddystoneScanResult = {\r\n frameType: EddystoneFrameType;\r\n /** 10-byte namespace ID as hex string (20 chars). Present for UID frames. */\r\n namespace?: string;\r\n /** 6-byte instance ID as hex string (12 chars). Present for UID frames. */\r\n instance?: string;\r\n /** Decoded URL. Present for URL frames. */\r\n url?: string;\r\n rssi: number;\r\n distance: number;\r\n txPower: number;\r\n /** BLE advertising device name. */\r\n name?: string;\r\n};\r\n\r\n/**\r\n * An Eddystone-UID beacon that has been paired/registered for monitoring.\r\n *\r\n * Note: Paired beacon data is stored unencrypted in UserDefaults (iOS) /\r\n * SharedPreferences (Android) and may be included in device backups.\r\n */\r\nexport type PairedEddystone = {\r\n identifier: string;\r\n /** 10-byte namespace ID as hex string (20 chars). */\r\n namespace: string;\r\n /** 6-byte instance ID as hex string (12 chars). */\r\n instance: string;\r\n /** BLE advertising device name, if provided at pairing time. */\r\n name?: string;\r\n};\r\n\r\n/** Payload for Eddystone enter/exit region events. */\r\nexport type EddystoneRegionEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n event: \"enter\" | \"exit\";\r\n /** Measured distance in metres at the time of the event (–1 if unavailable). */\r\n distance: number;\r\n};\r\n\r\n/** Payload for periodic Eddystone distance update events during monitoring. */\r\nexport type EddystoneDistanceEvent = {\r\n identifier: string;\r\n namespace: string;\r\n instance: string;\r\n distance: number;\r\n};\r\n\r\n/** Module event map. */\r\nexport type ExpoBeaconModuleEvents = {\r\n onBeaconEnter: (params: BeaconRegionEvent) => void;\r\n onBeaconExit: (params: BeaconRegionEvent) => void;\r\n onBeaconDistance: (params: BeaconDistanceEvent) => void;\r\n /** Fired continuously during a live scan as each iBeacon is detected. */\r\n onBeaconFound: (params: BeaconScanResult) => void;\r\n /** Fired continuously during a live scan as each Eddystone beacon is detected. */\r\n onEddystoneFound: (params: EddystoneScanResult) => void;\r\n onEddystoneEnter: (params: EddystoneRegionEvent) => void;\r\n onEddystoneExit: (params: EddystoneRegionEvent) => void;\r\n onEddystoneDistance: (params: EddystoneDistanceEvent) => void;\r\n};\r\n"]}
@@ -24,7 +24,7 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
24
24
  /**
25
25
  * Register a beacon for persistent region monitoring.
26
26
  */
27
- pairBeacon(identifier: string, uuid: string, major: number, minor: number): void;
27
+ pairBeacon(identifier: string, uuid: string, major: number, minor: number, name?: string): void;
28
28
  /**
29
29
  * Remove a previously paired beacon.
30
30
  */
@@ -36,7 +36,7 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
36
36
  /**
37
37
  * Register an Eddystone-UID beacon for persistent monitoring.
38
38
  */
39
- pairEddystone(identifier: string, namespace: string, instance: string): void;
39
+ pairEddystone(identifier: string, namespace: string, instance: string, name?: string): void;
40
40
  /**
41
41
  * Remove a previously paired Eddystone beacon.
42
42
  */
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,OAAO,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IACzE;;;;;;;;;;;OAWG;IACH,mBAAmB,CACjB,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAE9B;;;;;OAKG;IACH,sBAAsB,CACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAEjC;;OAEG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,IAAI;IAEP;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEtC;;OAEG;IACH,gBAAgB,IAAI,YAAY,EAAE;IAElC;;OAEG;IACH,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,IAAI;IAEP;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEzC;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAExC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAEvD;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpE;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAE3B,iEAAiE;IACjE,kBAAkB,IAAI,IAAI;IAE1B;;;OAGG;IACH,UAAU,IAAI,IAAI;IAElB,yEAAyE;IACzE,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;CAC5C;;AAED,wBAAmE"}
1
+ {"version":3,"file":"ExpoBeaconModule.d.ts","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,MAAM,CAAC;AAEzD,OAAO,EACL,sBAAsB,EACtB,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,OAAO,gBAAiB,SAAQ,YAAY,CAAC,sBAAsB,CAAC;IACzE;;;;;;;;;;;OAWG;IACH,mBAAmB,CACjB,KAAK,CAAC,EAAE,MAAM,EAAE,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAE9B;;;;;OAKG;IACH,sBAAsB,CACpB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,mBAAmB,EAAE,CAAC;IAEjC;;OAEG;IACH,UAAU,CACR,UAAU,EAAE,MAAM,EAClB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI;IAEP;;OAEG;IACH,YAAY,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEtC;;OAEG;IACH,gBAAgB,IAAI,YAAY,EAAE;IAElC;;OAEG;IACH,aAAa,CACX,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,IAAI,CAAC,EAAE,MAAM,GACZ,IAAI;IAEP;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAEzC;;OAEG;IACH,mBAAmB,IAAI,eAAe,EAAE;IAExC;;;OAGG;IACH,qBAAqB,CAAC,MAAM,EAAE,kBAAkB,GAAG,IAAI;IAEvD;;;;;;;OAOG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAEpE;;OAEG;IACH,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAE/B;;;OAGG;IACH,mBAAmB,IAAI,IAAI;IAE3B,iEAAiE;IACjE,kBAAkB,IAAI,IAAI;IAE1B;;;OAGG;IACH,UAAU,IAAI,IAAI;IAElB,yEAAyE;IACzE,uBAAuB,IAAI,OAAO,CAAC,OAAO,CAAC;CAC5C;;AAED,wBAAmE"}
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoBeaconModule.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAuHzD,eAAe,mBAAmB,CAAmB,YAAY,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\r\n\r\nimport {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n} from \"./ExpoBeacon.types\";\r\n\r\ndeclare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {\r\n /**\r\n * Start a one-shot iBeacon scan. Resolves with discovered beacons after scanDuration ms.\r\n *\r\n * Pass one or more UUIDs to scan for specific beacons (uses CoreLocation on iOS).\r\n * On iOS, at least one UUID is required — Apple strips iBeacon data from BLE\r\n * advertisements, making wildcard discovery impossible. When you pass an empty\r\n * array, the module automatically uses UUIDs from paired beacons.\r\n * On Android, pass an empty array to discover all nearby iBeacons.\r\n *\r\n * @param uuids Proximity UUIDs to filter by. Empty/omitted = use paired UUIDs (iOS) or wildcard (Android).\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForBeaconsAsync(\r\n uuids?: string[],\r\n scanDuration?: number,\r\n ): Promise<BeaconScanResult[]>;\r\n\r\n /**\r\n * Start a one-shot Eddystone beacon scan using BLE.\r\n * Discovers Eddystone-UID and Eddystone-URL frames.\r\n *\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForEddystonesAsync(\r\n scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]>;\r\n\r\n /**\r\n * Register a beacon for persistent region monitoring.\r\n */\r\n pairBeacon(\r\n identifier: string,\r\n uuid: string,\r\n major: number,\r\n minor: number,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired beacon.\r\n */\r\n unpairBeacon(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired beacons.\r\n */\r\n getPairedBeacons(): PairedBeacon[];\r\n\r\n /**\r\n * Register an Eddystone-UID beacon for persistent monitoring.\r\n */\r\n pairEddystone(\r\n identifier: string,\r\n namespace: string,\r\n instance: string,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired Eddystone beacon.\r\n */\r\n unpairEddystone(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired Eddystone beacons.\r\n */\r\n getPairedEddystones(): PairedEddystone[];\r\n\r\n /**\r\n * Set persistent notification configuration. Settings are saved and applied to all\r\n * subsequent monitoring sessions until explicitly changed.\r\n */\r\n setNotificationConfig(config: NotificationConfig): void;\r\n\r\n /**\r\n * Start background region monitoring for all paired beacons.\r\n * On Android starts a foreground service.\r\n * On iOS starts CLLocationManager region monitoring.\r\n *\r\n * Accepts a plain number (backward-compatible maxDistance shorthand) or a\r\n * MonitoringOptions object with maxDistance and/or notification overrides.\r\n */\r\n startMonitoring(options?: MonitoringOptions | number): Promise<void>;\r\n\r\n /**\r\n * Stop background region monitoring.\r\n */\r\n stopMonitoring(): Promise<void>;\r\n\r\n /**\r\n * Start a continuous BLE scan. Fires `onBeaconFound` events as beacons are detected.\r\n * Call stopContinuousScan() to end the scan.\r\n */\r\n startContinuousScan(): void;\r\n\r\n /** Stop the continuous scan started by startContinuousScan(). */\r\n stopContinuousScan(): void;\r\n\r\n /**\r\n * Cancel any in-progress one-shot scan (iBeacon or Eddystone).\r\n * The pending promise will be rejected with code \"SCAN_CANCELLED\".\r\n */\r\n cancelScan(): void;\r\n\r\n /** Request Bluetooth + Location permissions. Returns true if granted. */\r\n requestPermissionsAsync(): Promise<boolean>;\r\n}\r\n\r\nexport default requireNativeModule<ExpoBeaconModule>(\"ExpoBeacon\");\r\n"]}
1
+ {"version":3,"file":"ExpoBeaconModule.js","sourceRoot":"","sources":["../src/ExpoBeaconModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAyHzD,eAAe,mBAAmB,CAAmB,YAAY,CAAC,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from \"expo\";\r\n\r\nimport {\r\n ExpoBeaconModuleEvents,\r\n BeaconScanResult,\r\n EddystoneScanResult,\r\n PairedBeacon,\r\n PairedEddystone,\r\n NotificationConfig,\r\n MonitoringOptions,\r\n} from \"./ExpoBeacon.types\";\r\n\r\ndeclare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {\r\n /**\r\n * Start a one-shot iBeacon scan. Resolves with discovered beacons after scanDuration ms.\r\n *\r\n * Pass one or more UUIDs to scan for specific beacons (uses CoreLocation on iOS).\r\n * On iOS, at least one UUID is required — Apple strips iBeacon data from BLE\r\n * advertisements, making wildcard discovery impossible. When you pass an empty\r\n * array, the module automatically uses UUIDs from paired beacons.\r\n * On Android, pass an empty array to discover all nearby iBeacons.\r\n *\r\n * @param uuids Proximity UUIDs to filter by. Empty/omitted = use paired UUIDs (iOS) or wildcard (Android).\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForBeaconsAsync(\r\n uuids?: string[],\r\n scanDuration?: number,\r\n ): Promise<BeaconScanResult[]>;\r\n\r\n /**\r\n * Start a one-shot Eddystone beacon scan using BLE.\r\n * Discovers Eddystone-UID and Eddystone-URL frames.\r\n *\r\n * @param scanDuration Duration in ms (default 5000)\r\n */\r\n scanForEddystonesAsync(\r\n scanDuration?: number,\r\n ): Promise<EddystoneScanResult[]>;\r\n\r\n /**\r\n * Register a beacon for persistent region monitoring.\r\n */\r\n pairBeacon(\r\n identifier: string,\r\n uuid: string,\r\n major: number,\r\n minor: number,\r\n name?: string,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired beacon.\r\n */\r\n unpairBeacon(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired beacons.\r\n */\r\n getPairedBeacons(): PairedBeacon[];\r\n\r\n /**\r\n * Register an Eddystone-UID beacon for persistent monitoring.\r\n */\r\n pairEddystone(\r\n identifier: string,\r\n namespace: string,\r\n instance: string,\r\n name?: string,\r\n ): void;\r\n\r\n /**\r\n * Remove a previously paired Eddystone beacon.\r\n */\r\n unpairEddystone(identifier: string): void;\r\n\r\n /**\r\n * Return all currently paired Eddystone beacons.\r\n */\r\n getPairedEddystones(): PairedEddystone[];\r\n\r\n /**\r\n * Set persistent notification configuration. Settings are saved and applied to all\r\n * subsequent monitoring sessions until explicitly changed.\r\n */\r\n setNotificationConfig(config: NotificationConfig): void;\r\n\r\n /**\r\n * Start background region monitoring for all paired beacons.\r\n * On Android starts a foreground service.\r\n * On iOS starts CLLocationManager region monitoring.\r\n *\r\n * Accepts a plain number (backward-compatible maxDistance shorthand) or a\r\n * MonitoringOptions object with maxDistance and/or notification overrides.\r\n */\r\n startMonitoring(options?: MonitoringOptions | number): Promise<void>;\r\n\r\n /**\r\n * Stop background region monitoring.\r\n */\r\n stopMonitoring(): Promise<void>;\r\n\r\n /**\r\n * Start a continuous BLE scan. Fires `onBeaconFound` events as beacons are detected.\r\n * Call stopContinuousScan() to end the scan.\r\n */\r\n startContinuousScan(): void;\r\n\r\n /** Stop the continuous scan started by startContinuousScan(). */\r\n stopContinuousScan(): void;\r\n\r\n /**\r\n * Cancel any in-progress one-shot scan (iBeacon or Eddystone).\r\n * The pending promise will be rejected with code \"SCAN_CANCELLED\".\r\n */\r\n cancelScan(): void;\r\n\r\n /** Request Bluetooth + Location permissions. Returns true if granted. */\r\n requestPermissionsAsync(): Promise<boolean>;\r\n}\r\n\r\nexport default requireNativeModule<ExpoBeaconModule>(\"ExpoBeacon\");\r\n"]}
@@ -205,7 +205,7 @@ public class ExpoBeaconModule: Module {
205
205
 
206
206
  // MARK: - Pair
207
207
 
208
- Function("pairBeacon") { (identifier: String, uuid: String, major: Int, minor: Int) -> Void in
208
+ Function("pairBeacon") { (identifier: String, uuid: String, major: Int, minor: Int, name: String?) -> Void in
209
209
  guard UUID(uuidString: uuid) != nil else {
210
210
  throw Exception(name: "INVALID_UUID", description: "Invalid UUID format: \(uuid)")
211
211
  }
@@ -218,12 +218,14 @@ public class ExpoBeaconModule: Module {
218
218
 
219
219
  var beacons = self.loadPairedBeaconsRaw()
220
220
  beacons.removeAll { ($0["identifier"] as? String) == identifier }
221
- beacons.append([
221
+ var entry: [String: Any] = [
222
222
  "identifier": identifier,
223
223
  "uuid": uuid,
224
224
  "major": major,
225
225
  "minor": minor
226
- ])
226
+ ]
227
+ if let name = name { entry["name"] = name }
228
+ beacons.append(entry)
227
229
  self.defaults.set(beacons, forKey: PAIRED_BEACONS_KEY)
228
230
  self.cachedPairedBeacons = nil
229
231
  }
@@ -241,7 +243,7 @@ public class ExpoBeaconModule: Module {
241
243
 
242
244
  // MARK: - Eddystone Pair
243
245
 
244
- Function("pairEddystone") { (identifier: String, namespace: String, instance: String) -> Void in
246
+ Function("pairEddystone") { (identifier: String, namespace: String, instance: String, name: String?) -> Void in
245
247
  guard namespace.count == 20, namespace.range(of: "^[0-9a-fA-F]+$", options: .regularExpression) != nil else {
246
248
  throw Exception(name: "INVALID_NAMESPACE", description: "Namespace must be 20 hex characters, got: \(namespace)")
247
249
  }
@@ -253,11 +255,13 @@ public class ExpoBeaconModule: Module {
253
255
  eddystones.removeAll { ($0["identifier"] as? String) == identifier }
254
256
  // Normalize hex to lowercase — parseEddystoneFrame produces lowercase,
255
257
  // so stored values must match for monitoring comparisons.
256
- eddystones.append([
258
+ var entry: [String: Any] = [
257
259
  "identifier": identifier,
258
260
  "namespace": namespace.lowercased(),
259
261
  "instance": instance.lowercased()
260
- ])
262
+ ]
263
+ if let name = name { entry["name"] = name }
264
+ eddystones.append(entry)
261
265
  self.defaults.set(eddystones, forKey: PAIRED_EDDYSTONES_KEY)
262
266
  self.cachedPairedEddystones = nil
263
267
  }
@@ -309,11 +313,17 @@ public class ExpoBeaconModule: Module {
309
313
  self.defaults.removeObject(forKey: EXIT_DISTANCE_KEY)
310
314
  }
311
315
  self.defaults.set(true, forKey: IS_MONITORING_KEY)
312
- self.requestLocationPermission(requireAlways: true) { granted in
316
+ self.requestLocationPermission { granted in
313
317
  guard granted else {
314
- promise.reject("PERMISSION_DENIED", "Always location permission required for background monitoring")
318
+ promise.reject("PERMISSION_DENIED", "Location permission required for monitoring")
315
319
  return
316
320
  }
321
+ // Request Always authorization non-blockingly for background support.
322
+ // On iOS 13+ requestAlwaysAuthorization() from WhenInUse may be a
323
+ // no-op if the user already made their choice — don't block on it.
324
+ if self.locationManager.authorizationStatus != .authorizedAlways {
325
+ self.locationManager.requestAlwaysAuthorization()
326
+ }
317
327
  self.requestNotificationPermission()
318
328
  self.startRegionMonitoring()
319
329
  promise.resolve(nil)
@@ -645,12 +655,18 @@ public class ExpoBeaconModule: Module {
645
655
 
646
656
  guard let beacon = Self.parseEddystoneFrame(data: data, rssi: rssi.intValue) else { return }
647
657
 
658
+ // Augment with the BLE advertising device name if present
659
+ var beaconInfo = beacon
660
+ if let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
661
+ beaconInfo["name"] = localName
662
+ }
663
+
648
664
  if eddystoneScanPromise != nil {
649
- eddystoneScannedBeacons.append(beacon)
665
+ eddystoneScannedBeacons.append(beaconInfo)
650
666
  }
651
667
 
652
668
  if continuousScanActive {
653
- sendEvent("onEddystoneFound", beacon)
669
+ sendEvent("onEddystoneFound", beaconInfo)
654
670
  }
655
671
 
656
672
  // Eddystone monitoring: match UID frames against paired list
@@ -1048,28 +1064,30 @@ public class ExpoBeaconModule: Module {
1048
1064
  // Emit distance event every ranging cycle (~1 s)
1049
1065
  sendEvent("onBeaconDistance", makeBeaconEventParams(identifier: identifier, beacon: beacon))
1050
1066
 
1051
- // Distance-driven enter/exit synthesis with hysteresis
1052
- if let maxDist = self.defaults.object(forKey: MAX_DISTANCE_KEY) as? Double {
1053
- let exitDist = self.defaults.object(forKey: EXIT_DISTANCE_KEY) as? Double
1054
- let action = evaluateDistanceHysteresis(
1055
- identifier: identifier,
1056
- distance: beacon.accuracy,
1057
- maxDistance: maxDist,
1058
- exitDistance: exitDist,
1059
- entered: &enteredRegions,
1060
- enterCtrs: &enterCounters,
1061
- exitCtrs: &exitCounters
1062
- )
1063
- switch action {
1064
- case .enter:
1065
- sendEvent("onBeaconEnter", makeBeaconEventParams(identifier: identifier, beacon: beacon, event: "enter"))
1066
- postBeaconNotification(identifier: identifier, eventType: "enter")
1067
- case .exit:
1068
- sendEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, beacon: beacon, event: "exit"))
1069
- postBeaconNotification(identifier: identifier, eventType: "exit")
1070
- case .none:
1071
- break
1072
- }
1067
+ // Enter/exit synthesis with hysteresis — always applied.
1068
+ // When maxDistance is set, distance thresholds control transitions.
1069
+ // When maxDistance is nil, pure presence-based hysteresis is used
1070
+ // (HYSTERESIS_COUNT consecutive readings to confirm enter).
1071
+ let maxDist = self.defaults.object(forKey: MAX_DISTANCE_KEY) as? Double
1072
+ let exitDist = self.defaults.object(forKey: EXIT_DISTANCE_KEY) as? Double
1073
+ let action = evaluateDistanceHysteresis(
1074
+ identifier: identifier,
1075
+ distance: beacon.accuracy,
1076
+ maxDistance: maxDist,
1077
+ exitDistance: exitDist,
1078
+ entered: &enteredRegions,
1079
+ enterCtrs: &enterCounters,
1080
+ exitCtrs: &exitCounters
1081
+ )
1082
+ switch action {
1083
+ case .enter:
1084
+ sendEvent("onBeaconEnter", makeBeaconEventParams(identifier: identifier, beacon: beacon, event: "enter"))
1085
+ postBeaconNotification(identifier: identifier, eventType: "enter")
1086
+ case .exit:
1087
+ sendEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, beacon: beacon, event: "exit"))
1088
+ postBeaconNotification(identifier: identifier, eventType: "exit")
1089
+ case .none:
1090
+ break
1073
1091
  }
1074
1092
 
1075
1093
  // Note: onBeaconFound for continuous scan is emitted by the
@@ -1113,38 +1131,27 @@ public class ExpoBeaconModule: Module {
1113
1131
  }
1114
1132
 
1115
1133
  fileprivate func handleDidEnterRegion(_ region: CLRegion) {
1116
- guard let beaconRegion = region as? CLBeaconRegion else { return }
1117
- let identifier = beaconRegion.identifier
1118
-
1119
- // If maxDistance is set, distance ranging handles enter/exit — skip region-based emit
1120
- guard self.defaults.object(forKey: MAX_DISTANCE_KEY) == nil else { return }
1121
-
1122
- sendEvent("onBeaconEnter", makeBeaconEventParams(identifier: identifier, region: beaconRegion, event: "enter"))
1123
- postBeaconNotification(identifier: identifier, eventType: "enter")
1134
+ // Region callbacks are suppressed all enter/exit logic goes through
1135
+ // ranging-based hysteresis in handleDidRange for consistent behaviour
1136
+ // with HYSTERESIS_COUNT, regardless of whether maxDistance is set.
1124
1137
  }
1125
1138
 
1126
1139
  fileprivate func handleDidExitRegion(_ region: CLRegion) {
1127
1140
  guard let beaconRegion = region as? CLBeaconRegion else { return }
1128
1141
  let identifier = beaconRegion.identifier
1129
1142
 
1130
- // If maxDistance is set, distance ranging normally handles exit.
1131
- // However, if the beacon was in "entered" state when the OS fires
1132
- // didExitRegion, we must emit the exit event ourselves distance
1133
- // ranging will no longer receive readings for this beacon.
1134
- if self.defaults.object(forKey: MAX_DISTANCE_KEY) != nil {
1135
- let wasEntered = enteredRegions.remove(identifier) != nil
1136
- enterCounters.removeValue(forKey: identifier)
1137
- exitCounters.removeValue(forKey: identifier)
1138
- missCounters.removeValue(forKey: identifier)
1139
- if wasEntered {
1140
- sendEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, region: beaconRegion, event: "exit"))
1141
- postBeaconNotification(identifier: identifier, eventType: "exit")
1142
- }
1143
- return
1143
+ // Ranging-based hysteresis (handleDidRange miss counter) handles exit
1144
+ // in most cases. However, when the OS fires didExitRegion, ranging may
1145
+ // have already stopped delivering callbacks for this beacon. If the
1146
+ // beacon was in "entered" state, emit the exit event as a safety net.
1147
+ let wasEntered = enteredRegions.remove(identifier) != nil
1148
+ enterCounters.removeValue(forKey: identifier)
1149
+ exitCounters.removeValue(forKey: identifier)
1150
+ missCounters.removeValue(forKey: identifier)
1151
+ if wasEntered {
1152
+ sendEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, region: beaconRegion, event: "exit"))
1153
+ postBeaconNotification(identifier: identifier, eventType: "exit")
1144
1154
  }
1145
-
1146
- sendEvent("onBeaconExit", makeBeaconEventParams(identifier: identifier, region: beaconRegion, event: "exit"))
1147
- postBeaconNotification(identifier: identifier, eventType: "exit")
1148
1155
  }
1149
1156
 
1150
1157
  fileprivate func handleMonitoringDidFail(for region: CLRegion?, withError error: Error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-beacon",
3
- "version": "0.5.4",
3
+ "version": "0.5.6",
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",
@@ -6,6 +6,8 @@ export type BeaconScanResult = {
6
6
  rssi: number; // Signal strength in dBm (negative number)
7
7
  distance: number; // Estimated distance in meters
8
8
  txPower: number; // Calibrated TX power
9
+ /** BLE advertising device name. May be undefined on iOS (CoreLocation does not expose it for iBeacon). */
10
+ name?: string;
9
11
  };
10
12
 
11
13
  /**
@@ -19,6 +21,8 @@ export type PairedBeacon = {
19
21
  uuid: string;
20
22
  major: number;
21
23
  minor: number;
24
+ /** BLE advertising device name, if provided at pairing time. */
25
+ name?: string;
22
26
  };
23
27
 
24
28
  /** Payload for enter/exit region events. */
@@ -128,6 +132,8 @@ export type EddystoneScanResult = {
128
132
  rssi: number;
129
133
  distance: number;
130
134
  txPower: number;
135
+ /** BLE advertising device name. */
136
+ name?: string;
131
137
  };
132
138
 
133
139
  /**
@@ -142,6 +148,8 @@ export type PairedEddystone = {
142
148
  namespace: string;
143
149
  /** 6-byte instance ID as hex string (12 chars). */
144
150
  instance: string;
151
+ /** BLE advertising device name, if provided at pairing time. */
152
+ name?: string;
145
153
  };
146
154
 
147
155
  /** Payload for Eddystone enter/exit region events. */
@@ -46,6 +46,7 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
46
46
  uuid: string,
47
47
  major: number,
48
48
  minor: number,
49
+ name?: string,
49
50
  ): void;
50
51
 
51
52
  /**
@@ -65,6 +66,7 @@ declare class ExpoBeaconModule extends NativeModule<ExpoBeaconModuleEvents> {
65
66
  identifier: string,
66
67
  namespace: string,
67
68
  instance: string,
69
+ name?: string,
68
70
  ): void;
69
71
 
70
72
  /**