iOS/iPhone/iPad/watchOS/tvOS/MacOSX/Android プログラミング, Objective-C, Cocoa, Swiftなど
Core Bluetoothと使ったすれちがい通信だと、アプリケーションがバッググラウンドで動作している場合、通信の頻度が低くなるので、iBeaconを試してみる事にした。
自分で調べた訳ではないので間違っていたら申し訳ないが、iBeaconはBluetooth LEのAdvertisingパケットにiBeacon用の情報を埋め込んだもののようだ。つまり、サービスのスキャンで情報を受け取れるという事か?
まずは、バックグラウンドでもすれちがい通信できるようにInfo.plistを設定する。
iBeacon関連のフレームワークを使うとコードは簡素になる。Periperal側は以下のとおり。
@implementation BeaconPeripheralResponseParser
...
- (void)parse
{
self.state = kBeaconPeripheralStateAdvertising;
/* CBPeripheralManagerを生成 */
self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self
queue:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)];
if (! self.peripheralManager) {
/* CBPeripheralManagerの初期化失敗 */
self.state = kBeaconPeripheralStateError;
self.error = [self _errorWithCode:kBeaconPeripheralResponseParserGenericError
localizedDescription:@"CBPeripheralManagerの初期化に失敗しました。"];
return;
}
/* ビーコン領域を生成 */
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:BEACON_SERVICE_UUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid
major:12345
minor:67890
identifier:@"demo.Wibree.BeaconCentralResponseParser"];
if (! self.beaconRegion) {
/* ビーコン領域の初期化失敗 */
self.state = kBeaconPeripheralStateError;
self.error = [self _errorWithCode:kBeaconPeripheralResponseParserGenericError
localizedDescription:@"ビーコン領域の初期化に失敗しました。"];
self.peripheralManager = nil;
return;
}
/* 告知開始 */
NSDictionary *dictionary = [self.beaconRegion peripheralDataWithMeasuredPower:nil];
[self.peripheralManager startAdvertising:dictionary];
}
...
@end
Central側はこうなる。
@implementation BeaconCentralResponseParser
...
- (void)parse
{
self.state = kBeaconCentralStateScanning;
/* CLLocationManagerを生成 */
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
if (! self.locationManager) {
/* CLLocationManagerの初期化失敗 */
self.state = kBeaconCentralStateError;
self.error = [self _errorWithCode:kBeaconCentralResponseParserGenericError
localizedDescription:@"CLLocationManagerの初期化に失敗しました。"];
return;
}
/* ビーコン領域を生成 */
NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:BEACON_SERVICE_UUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid identifier:@"demo.Wibree.BeaconCentralResponseParser"];
if (! self.beaconRegion) {
/* ビーコン領域の初期化失敗 */
self.state = kBeaconCentralStateError;
self.error = [self _errorWithCode:kBeaconCentralResponseParserGenericError
localizedDescription:@"ビーコン領域の初期化に失敗しました。"];
self.locationManager = nil;
return;
}
/* ビーコン領域の出入りを監視 */
[self.locationManager startMonitoringForRegion:self.beaconRegion];
/* 距離を監視 */
[self.locationManager startRangingBeaconsInRegion:self.beaconRegion];
}
...
@end
ビーコンが見つかるとCLLocationManagerDelegateのデリゲート・メソッドが呼ばれるので、それを実装する事になる。
@implementation BeaconCentralResponseParser
...
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region
{
if ([self.delegate respondsToSelector:@selector(beaconCentralResponseParser:didEnterRegion:)]) {
[self.delegate beaconCentralResponseParser:self didEnterRegion:region];
}
}
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region
{
if ([self.delegate respondsToSelector:@selector(beaconCentralResponseParser:didExitRegion:)]) {
[self.delegate beaconCentralResponseParser:self didExitRegion:region];
}
}
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
if ([self.delegate respondsToSelector:@selector(beaconCentralResponseParser:didRangeBeacons:inRegion:)]) {
[self.delegate beaconCentralResponseParser:self didRangeBeacons:beacons inRegion:region];
}
}
...
@end
iBeaconでの通信が成功したというのは間違いだった。申し訳ない。
CLLocationManagerDelegateの-locationManager:didRangeBeacons:inRegion:は検出できなくても周期的に呼ばれるようで、それで検出できたと考えたが、見つけたビーコン配列は空だった。そればかりが、動いていたCore Bluetoothの通信もできなくなっている!
結論をいうとInfo.plistのBackgraound Modesの「Location updates」と「Uses Bluetooth LE accessories」、「Acts as a Bluetooth LE accessory」を一緒に設定するとBluetooth LE通信ができなくなるようだ。
この設定をしなくすると動作するようになった。Core BluetoothとiBeaconの両方を使ってiOS機器同士で通信する場合は、注意が必要なようだ。