Your app can subscribe to Bluetooth Low Energy (BLE) beacon messages using the same mechanism that is used to subscribe to messages published by other nearby devices.
By default, beacon subscriptions work only when your app is in the foreground. When your app goes to the background, the subscriptions automatically stop scanning for beacons. See Background Scanning for details about how to enable background scanning.
To subscribe to beacons, set the deviceTypesToDiscover
parameter to
kGNSDeviceBLEBeacon
in the subscription parameters. The following snippet
demonstrates how to do this:
Objective-C
id<GNSSubscription> beaconSubscription = [messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
}];
Swift
let beaconSubscription = messageManager.subscriptionWithMessageFoundHandler(
myMessageFoundHandler, messageLostHandler: myMessageLostHandler,
paramsBlock: { (params: GNSSubscriptionParams!) in
params.deviceTypesToDiscover = .BLEBeacon
})
The above subscription only discovers beacons owned by your project, and receives all messages from those beacons. If you want to receive messages from beacons registered with a different namespace, you can pass a namespace in the subscription parameters. Similarly, if you want a specific type of message, you can also pass a message type for filtering. The following snippet shows how to do this:
Objective-C
id<GNSSubscription> beaconSubscription = [messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.messageNamespace = @"com.mycompany.mybeaconservice";
params.type = @"mybeacontype";
}];
Swift
let beaconSubscription = messageManager.subscriptionWithMessageFoundHandler(
myMessageFoundHandler, messageLostHandler: myMessageLostHandler,
paramsBlock: { (params: GNSSubscriptionParams!) in
params.deviceTypesToDiscover = .BLEBeacon
params.messageNamespace = "com.mycompany.mybeaconservice"
params.type = "mybeacontype"
})
By default, a beacon subscription scans for both type of beacons, Eddystone and
iBeacon. When iBeacon scanning is enabled, users will be prompted to give
permission for the app to use their location data. Your app's Info.plist
must
include the NSLocationWhenInUseUsageDescription
key with short explanation of
why location is being used. See
Apple's documentation
for details.
If you want to scan only for Eddystone beacons, you can disable iBeacon
scanning in the GNSBeaconStrategy
, and iOS will not ask the user for
permission to use location. The following snippet shows how to disable iBeacon
scanning for the original subscription above:
Objective-C
id<GNSSubscription> beaconSubscription = [messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.beaconStrategy =
[GNSBeaconStrategy strategyWithParamsBlock:^(GNSBeaconStrategyParams *params) {
params.includeIBeacons = NO;
};
}];
Swift
let beaconSubscription = messageManager.subscriptionWithMessageFoundHandler(
myMessageFoundHandler, messageLostHandler: myMessageLostHandler,
paramsBlock: { (params: GNSSubscriptionParams!) in
params.deviceTypesToDiscover = .BLEBeacon
params.beaconStrategy =
GNSBeaconStrategy(paramsBlock: { (params: GNSBeaconStrategyParams!) in
params.includeIBeacons = false
})
})
By default, low power scanning is enabled, which can sometimes result in large latencies when finding Eddystone beacons. When low power mode is disabled, iBeacon scanning is used to help find Eddystone beacons, which can reduce these latencies. However, this results in greater battery usage, and iOS will ask the user for permission to use location.
The following snippet shows how to disable low power mode when scanning for Eddystone beacons.
Objective-C
id<GNSSubscription> beaconSubscription = [messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.beaconStrategy =
[GNSBeaconStrategy strategyWithParamsBlock:^(GNSBeaconStrategyParams *params) {
params.includeIBeacons = NO;
params.lowPowerPreferred = NO;
};
}];
Swift
let beaconSubscription = messageManager.subscriptionWithMessageFoundHandler(
myMessageFoundHandler, messageLostHandler: myMessageLostHandler,
paramsBlock: { (params: GNSSubscriptionParams!) in
params.deviceTypesToDiscover = .BLEBeacon
params.beaconStrategy =
GNSBeaconStrategy(paramsBlock: { (params: GNSBeaconStrategyParams!) in
params.includeIBeacons = false
params.lowPowerPreferred = false
})
})
When scanning for iBeacons, the iOS location permission dialog is preceded by
the Nearby permission dialog. If you want to override this dialog (for
instance, to provide a "preflight" dialog that explains why location permission
is needed), set the permissionRequestHandler
to a custom block in the
subscription parameters. The following snippet shows how to do this:
Objective-C
id<GNSSubscription> beaconSubscription = [messageManager
subscriptionWithMessageFoundHandler:myMessageFoundHandler
messageLostHandler:myMessageLostHandler
paramsBlock:^(GNSSubscriptionParams *params) {
params.deviceTypesToDiscover = kGNSDeviceBLEBeacon;
params.permissionRequestHandler = ^(GNSPermissionHandler permissionHandler) {
// Show your custom dialog here, and don't forget to call permissionHandler after it is dismissed
permissionHandler(userGavePermission);
};
}];
Swift
let beaconSubscription = messageManager.subscriptionWithMessageFoundHandler(
myMessageFoundHandler, messageLostHandler: myMessageLostHandler,
paramsBlock: { (params: GNSSubscriptionParams!) in
params.deviceTypesToDiscover = .BLEBeacon
params.permissionRequestHandler = { (permissionHandler: GNSPermissionHandler!) in
// Show your custom dialog here, and don't forget to call permissionHandler after it is dismissed
permissionHandler(userGavePermission);
}
})
Background Scanning
Since beacon scanning uses BLE, it can work in the background. Here are some things you should be aware of when deciding to use background mode:
- There is an added battery cost for background BLE. The cost is low, but you should measure it before deciding to use background mode.
- iOS will ask the user for permission to use location in the background if iBeacon scanning is enabled or low power mode is disabled.
To enable beacon scanning in the background, follow these additional steps:
Enable background mode for your subscription by passing in a properly configured
GNSBeaconStrategy
object. The following snippet shows how to do this:Objective-C
id<GNSSubscription> beaconSubscription = [messageManager subscriptionWithMessageFoundHandler:myMessageFoundHandler messageLostHandler:myMessageLostHandler paramsBlock:^(GNSSubscriptionParams *params) { params.deviceTypesToDiscover = kGNSDeviceBLEBeacon; params.beaconStrategy = [GNSBeaconStrategy strategyWithParamsBlock:^(GNSBeaconStrategyParams *params) { params.allowInBackground = YES; }]; }];
Swift
let beaconSubscription = messageManager.subscriptionWithMessageFoundHandler( myMessageFoundHandler, messageLostHandler: myMessageLostHandler, paramsBlock: { (params: GNSSubscriptionParams!) in params.deviceTypesToDiscover = .BLEBeacon params.beaconStrategy = GNSBeaconStrategy(paramsBlock: { (params: GNSBeaconStrategyParams!) in params.allowInBackground = true }) })
Add the required entries to your app's
Info.plist
:UIBackgroundModes
entries:bluetooth-central
for BLE scanning in the background.location
for iBeacon scanning in the background using high-power mode. You can omit this if you're doing low power scanning for Eddystone beacons only.
NSLocationAlwaysUsageDescription
string describing why you will be tracking the user's location in the background. For example, "Your location is needed to scan for beacons in the background." See Apple's documentation for details. You can omit this if you're doing low power scanning for Eddystone beacons only.
Can the user enable or disable background scanning in your app? If so, you should save the background mode value to
NSUserDefaults
because iOS can kill your app at any time while it's in the background. Your app should do the following:- Save the background mode value to
NSUserDefaults
whenever the user changes it. - On startup, read it from
NSUserDefaults
and restore the beacon subscription if background mode is enabled.
- Save the background mode value to
Background Notifications
If you want your app to notify the user when beacons are discovered while in the background, you can use local notifications. For details, see background notifications.