Run iPhone as an iBeacon in the background

2019-01-12 14:23发布

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;
    }
}
}

5条回答
孤傲高冷的网名
2楼-- · 2019-01-12 14:40

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.

查看更多
仙女界的扛把子
3楼-- · 2019-01-12 14:40

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.

查看更多
萌系小妹纸
4楼-- · 2019-01-12 14: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

查看更多
走好不送
5楼-- · 2019-01-12 15:00

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.

查看更多
该账号已被封号
6楼-- · 2019-01-12 15:01

This can be done. I don't want angry Apple engineers at my doorstep, so I won't publish the algorithm.

In summary, the overflow area that encodes the service UUIDs is just a bunch of bytes that goes over the air. You can sniff it yourself. In particular, it is a short hash that is subsequently represented by a one hot encoding. Multiple UUIDs are communicated by having multiple bits set. You will have collisions in this way. For example UUID 1001 will have the same encoding as 3333. You can check this yourself: have an iPhone broadcast UUID 1001 on the background and have the other one scan for 3333. It will think it receives 3333 indeed.

查看更多
登录 后发表回答