71

Is it possible to run an iOS 7 device as a Bluetooth LE peripheral (iBeacon) and have it advertise in the background? I have been able to get it to advertise in the foreground with the code below and can see it from another iOS device but as soon as I go back to the home screen it stops advertising. I did add the bluetooth-peripheral background mode in the plist but that didn't seem to help although I do get the prompt saying the device wants to use bluetooth in the background. Am I doing something wrong or is this just not possible in iOS 7?

peripManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
  if (peripheral.state != CBPeripheralManagerStatePoweredOn) {
      return;
  }

  NSString *identifier = @"MyBeacon";
  //Construct the region
  CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:identifier];

  //Passing nil will use the device default power
  NSDictionary *payload = [beaconRegion peripheralDataWithMeasuredPower:nil];

  //Start advertising
  [peripManager startAdvertising:payload];
}

Here is the code that is on the receiving/listening end:

- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons
           inRegion:(CLBeaconRegion *)region
{
//Check if we have moved closer or farther away from the iBeacon…
if (beacons.count > 0) {
    CLBeacon *beacon = [beacons objectAtIndex:0];

    switch (beacon.proximity) {
        case CLProximityImmediate:
            [self log:[NSString stringWithFormat:@"You're Sitting on it! %li", (long)beacon.rssi]];
            break;
        case CLProximityNear:
            [self log:[NSString stringWithFormat:@"Getting Warmer! %li", (long)beacon.rssi]];
            break;
        default:
            [self log:[NSString stringWithFormat:@"It's around here somewhere! %li", (long)beacon.rssi]];
            break;
    }
}
}
jpcoder
  • 1,125
  • 2
  • 9
  • 15
  • Do you mean that the region does not trigger when another device gets within range of the beacon? Or that you can't get additional details from the beacon after the region has triggered? – Wain Sep 22 '13 at 19:12
  • @Wain See the code below. This is what I am using on the receiving/listening end. This callback is triggered when the advertising code above runs in the foreground, but not when the app is in the advertising app is in the background. – jpcoder Sep 23 '13 at 01:45
  • @Wain I added the receiving code to the original post above. – jpcoder Sep 23 '13 at 01:48
  • 7
    According to the Apple Developer Forums iBeacons will only be broadcasted by to the app in the foreground. It probably has to do with the power saving characteristics of Core Bluetooth. Apple needs to modify all aspects of the Bluetooth advertisement to broadcast the beacons and this isn't available when the app is in the background according to the docs. https://developer.apple.com/library/ios/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothBackgroundProcessingForIOSApps/PerformingTasksWhileYourAppIsInTheBackground.html – PaulWoodIII Sep 23 '13 at 03:31
  • @ChinaPaul that doc says that everything should work with the right background keys, just slower and with some limitations. From "The bluetooth-peripheral Background Execution Mode". It doesn't give an idea of what slower is. The dev forum definitely indicates issues though... – Wain Sep 23 '13 at 07:06
  • 1
    @Wain Apple developers say point blank this isn't supported on the forums, but thats using the APIs I'm still hoping we find a way to reverse engineer it to use in the background as a custom peripheral. I still doubt this can be done though because of the advertising packet overflow area being smaller on background apps peripheral advertisements – PaulWoodIII Sep 23 '13 at 07:14
  • @ChinaPaul That whole talk of overflow area in the docs implies that something should work in the background. The question then is what does work. – jpcoder Sep 23 '13 at 12:10
  • @ChinaPaul, thanks for the info. Do you have a link to the thread where it's made explicit. I'd like to raise a bug report and reference that. Thanks – Wain Sep 23 '13 at 12:20
  • 1
    @jpcoder you are right about the overflow area if you are looking for it you should be able to find it, it but heres what worries me "The CBAdvertisementDataLocalNameKey advertisement key is ignored, and the local name of peripheral is not advertised." and later on about the CBAdvertisementDataOverflowServiceUUIDsKey: "Due to the nature of the data stored in this area, UUIDs listed here are “best effort” and may not always be accurate. For details about the overflow area of advertisement data, see the startAdvertising: method in CBPeripheralManager Class Reference." – PaulWoodIII Sep 23 '13 at 15:45
  • @Wain hope this helps: https://devforums.apple.com/message/832089#832089 – PaulWoodIII Sep 23 '13 at 15:46
  • @ChinaPaul thanks. Last question, is this iBeacon specific? So a basic CBPeripheralManager continues to advertise in the background? – Wain Sep 23 '13 at 17:36
  • 1
    @Wain Yes a basic CBPeripheralManager will continue to advertise, I've got another question here: http://stackoverflow.com/questions/18906988/what-is-the-ibeacon-bluetooth-profile (on hold so if you can edit in some clarity I'd be appreciative) that ask what the iBeacon service is. In theory if we knew how they made an iBeacon inside the Corebluetooth framework we may be able to use a CBPeripheralManager to advertise it in the background. – PaulWoodIII Sep 24 '13 at 01:29
  • Ok, so you can get a CBPeripheralManager to advertise something other than a CLBeaconRegion in the background using BLE? Do you know if there is an example of that somewhere? – jpcoder Sep 24 '13 at 03:34
  • @ChinaPaul: You can [easily use the CoreBluetooth framework to advertise as an iBeacon](http://stackoverflow.com/a/20932265/35690), however it still looks like it doesn't respect the backgrounding mode correctly when you do. If you are able to find a solution, please let us know. – Senseful Jan 05 '14 at 10:18
  • @jpcoder I developed a library which could be helpful in your case (advertising in background): https://github.com/omergul123/Discovery – Ömer Faruk Gül Feb 13 '15 at 16:33

4 Answers4

52

Standard CoreBluetooth advertisements can broadcast while the app is in the background, but not if they were started with CLBeaconRegion dictionary. The workaround is to ditch CoreLocation framework altogether and create your own proximity "framework" using only CoreBlueTooth.

You still need to use the appropriate background specifiers in the Info.plist file (e.g. bluetooth-peripheral and bluetooth-central).

The code looks something like this:

1) create a standard peripheral advertisement using CBPeripheralManager

NSDictionary *advertisingData = @{CBAdvertisementDataLocalNameKey:@"my-peripheral",
                                  CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:identifier]]};

// Start advertising over BLE
[peripheralManager startAdvertising:advertisingData];

2) use use CBCentralManager to scan for that service using the UUID you specified.

NSDictionary *scanOptions = @{CBCentralManagerScanOptionAllowDuplicatesKey:@(YES)};
NSArray *services = @[[CBUUID UUIDWithString:identifier]];

[centralManager scanForPeripheralsWithServices:services options:scanOptions];

3) in the CBCentralManagerDelegate method didDiscoverPeripheral, read the RSSI value of the advertisement.

- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral
     advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{

    NSLog(@"RSSI: %d", [RSSI intValue]);
}

4) Translate the RSSI values into a distance.

- (INDetectorRange)convertRSSItoINProximity:(NSInteger)proximity
{
    if (proximity < -70)
        return INDetectorRangeFar;
    if (proximity < -55)
        return INDetectorRangeNear;
    if (proximity < 0)
        return INDetectorRangeImmediate;

    return INDetectorRangeUnknown;
}

I found that I needed to "ease" or "average" the RSSI values to get anything workable. This is no different than when you are working with any sensor data (e.g. accelerometer data).

I have this concept fully working hope to publish it somewhere at some point.

Also, use the docs (Core Bluetooth Programming Guide) if you get stuck.

Update: A full code sample is up on Github. I worked on this as part of a work related project.

Update #2: Apple release major improvements to iBeacon background behavior for iOS7.1

bentford
  • 33,038
  • 7
  • 61
  • 57
  • is this another implementation to solve the problem or is this an iBeacon? From what I see another phone will not register this as an iBeacon, and you will not get all the same awake from background, and enter exit region callbacks that you would get with a CoreLocation iBeacon. – PaulWoodIII Dec 09 '13 at 03:42
  • Right. This will not use the CoreLocation iBeacon framework at all. It uses CoreBluetooth to do exactly what iBeacons does, but it runs in the background and will "wake" your app to perform updates. – bentford Dec 16 '13 at 21:46
  • 1
    @bentford I see that this works in the background (when you say, go to the home screen or switch apps), but does the app still advertise/wake up in the event that it is terminated by iOS to save memory? I saw in the doc that we can "call the connectPeripheral:options: method of the CBCentralManager class, and because connection requests do not time out, the iOS device will reconnect when the peripheral is found". I'm still unclear if the peripheral continues to advertise in this way. Thanks! – PotatoFro Jan 10 '14 at 18:07
  • @PotatoFro From my knowledge, the peripheral stops broadcasting if your apps is killed, either by the user or by iOS. – bentford Jan 10 '14 at 23:20
  • @bentford Interesting! I wonder, can the peripheral broadcasting be reinitiated when the app is woken in the background to perform another task? (perhaps, a periodic server update or if using Significant Location Change) – PotatoFro Jan 13 '14 at 18:16
  • 2
    Unfortunately, the data payload (ie. iBeacon informations) are stripped when the app moves to background. Tested on iOS 7.0.5. – valvoline Feb 06 '14 at 09:14
  • @valvoline can you explain how the loss of iBeacon information causes an issue? – bluefloyd8 Mar 31 '14 at 15:22
  • 2
    This doesn't actually make the app work exactly like an iBeacon, right? As in, it doesn't support Major/Minor values, and it doesn't get detected by the traditional CoreLocation APIs? – Yazid Apr 03 '14 at 23:03
  • 2
    @Yazid Right. It doesn't use or work with the traditional CoreLocation APIs. But it does allow you to replicate the iBeacon behavior in your app while supporting background broadcast and detection. – bentford Apr 04 '14 at 18:03
  • Can you talk about how you see this scaling to a larger number of beacons? -- Is the approach you describe in your github's readme 'historical' section, your recommended approach? -- What is the limit of different UUIDs you can monitor in parallel? – Daij-Djan Jul 20 '14 at 13:06
  • Although not perfect, this solution was ideal for me and essentially mimicked a beacon anyway. It should be marked as the correct answer. – Jacob King Dec 17 '15 at 17:20
  • This solution changes the name of the device, as seen by the bluetooth of other devices. Wouldn't this cause the disconnection of other devices eg. earphones, Apple Watch etc? Is there any other field in which we can send about 4 bytes of data, that would not affect other bluetooth communication on the iOS device? – Abdurrahman Mubeen Ali Oct 16 '17 at 13:13
  • @bentford Can you check my question? I have a problem about your solution. https://stackoverflow.com/questions/61676910/background-scan-and-advertising-between-two-%c4%b1os-device – uyarc May 09 '20 at 08:46
4

The Can you smell the iBeacon? article discusses both the use of Estimotes and advertising from Macs and iOS devices. You need to check the capability “Acts as Bluetooth LE accessory” in the project target.

Cameron Lowell Palmer
  • 21,528
  • 7
  • 125
  • 126
  • Interestingly iOS won't allow iBeacon advertising in the background. It will allow you to use `CoreBluetooth` to act as a peripheral or central however. iBeacons, no dice (at least not for the time being, but here's to hoping). – Yazid Apr 19 '14 at 15:32
2

No, iOS devices only advertise iBeacon when the app that does the advertising runs in the foreground. so, if you switch to another app or if the device goes to sleep, the advertisement stops.

Of course, if you really want the advertisement to continue, disable the idle timer and do Guided Access so that the iOs device does not go to sleep and no one can switch to another app.

RawMean
  • 8,374
  • 6
  • 55
  • 82
  • 1
    This is a crazy solution, how about on iOS7.1? What do you mean by "the idle timer"? – bluefloyd8 Mar 31 '14 at 15:21
  • Yes, using an iOS device as iBeacon advertiser is not a solution that I would recommend. Obviously, the right solution is to get an actual dedicated iBeacon device (there are a few of them that you can buy, but they are way too expensive for what they are at this point). You can even use a Raspberry Pi and and a BTLE USB dongle to configure it as a iBeacon advertiser. By "idle time", I'm referring to the sleep timer which upon expiration causes the device to go to sleep. – RawMean Mar 31 '14 at 18:41
1

I am also hoping to be able to set up my (test) app to advertise an iBeacon from the background. The docs on the UIBackgroundModes info.plist key suggest that the bluetooth-peripheral key might work, but it seems that it doesn't. (I just tested it a few minutes ago.)

What I'm doing for now is setting the idle timer to disabled, as RawMean suggests, and then setting the screen brightness to 0. Finally, when my test app is acting as an iBeacon, I add a shake event handler that lights the screen up again for 30 seconds. Dimming the screen as low as it will go helps reduce battery drain somewhat.

Duncan C
  • 128,072
  • 22
  • 173
  • 272