読者です 読者をやめる 読者になる 読者になる

WonderPlanet DEVELOPER BLOG

ワンダープラネットの開発者ブログです。モバイルゲーム開発情報を発信。

iBeaconを試してみた

iOS Objective-C

今回のエンジニアブログ担当の山下です。
iOS界隈で最近iBeaconというテーマが熱くなってるように感じます。
今回はかんたんなプログラムを作りながらiBeaconを体験してみました。

iBeaconとは

iBeaconはBluetoothを利用した技術であり、ビーコンと呼ばれる常に電波を出し続けるデバイスを置くことで
iPhoneが特定の領域への侵入を判定できるようになります。
これにより特定のエリアに入るとアプリが通知を出したり、
ビーコンからの大雑把な距離を測るといったことが実現可能です。
またBluetooth Low Energy技術を利用しているため、ビーコンはコイン電池で
一年以上利用できるほど消費電力も非常に抑えられています。
仕組みとしては単純ですが、商用や観光案内などNFCに代わり様々な応用が考えられている技術です。

必要な環境

iBeaconはiOS 7以上のデバイスで利用できます。また前述のとおりBluetoothを使った位置情報推定の技術であるため、iOSデバイスでBluetoothをONにし、iBeacon対応アプリの位置情報取得を許可する必要があります。

実装 〜受信サイド〜

まずXcodeでSingle View Applicationの新しいプロジェクトを作成します。そしてStoryboardを開いてUILabelを置き、View ControllerとIBOutlet接続します。

次にView Controllerを実装していきます。View Controllerの名前等は適宜読み替えてください。

#import "ReceiverViewController.h"  
@import CoreLocation;  
  
@interface ReceiverViewController () <CLLocationManagerDelegate>  
@property (weak, nonatomic) IBOutlet UILabel *proximityLabel;  
@property(strong, nonatomic) CLLocationManager *locationManager;  
@property(strong, nonatomic) CLBeaconRegion *beaconRegion;  
@property(strong, nonatomic) NSDictionary *proximityDict;  
@end  
  
@implementation ReceiverViewController  
  
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
  
    // 検出するビーコン領域の定義  
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"*** UUID ***"];  
    self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid  
                                                           identifier:@"wonderplanet_ibeacondemo"];  
  
    // CLLocationManagerの作成  
    self.locationManager = [[CLLocationManager alloc] init];  
    self.locationManager.delegate = self;  
    [self.locationManager startMonitoringForRegion:self.beaconRegion];  
  
    self.proximityDict = @{@(CLProximityImmediate): @"Immediate",  
                           @(CLProximityFar): @"Far",  
                           @(CLProximityNear): @"Near",  
                           @(CLProximityUnknown): @"Unknown"  
                           };  
}  
…  

まず対象となるビーコンの領域をCLBeaconRegionクラスで表します。
領域はUUID、major、minorといった情報でグループ化します。今回はUUIDのみを指定しました。
ビーコンがある場合はビーコンの情報を適切に設定してください。
ビーコンがない場合、UUIDはターミナルでuuidgenコマンドを実行することにより生成できます。
そしてCLLocationManagerのインスタンスを生成し、ビーコン領域を設定して監視を開始します。

// 監視開始  
- (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region  
{  
    // 開始時に既に領域内に入っているかどうかチェック  
    [self.locationManager requestStateForRegion:(CLBeaconRegion *)region];  
}  
  
// 現在領域に入っているかどうかの判定  
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region  
{  
    if(state == CLRegionStateInside) {  
        [self.locationManager startRangingBeaconsInRegion:self.beaconRegion];  
    }  
}  
  
// 領域に入ったとき  
- (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region  
{  
    [self.locationManager startRangingBeaconsInRegion:(CLBeaconRegion *)region];  
}  
  
// 領域から出たとき  
- (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region  
{  
    [self.locationManager stopMonitoringForRegion:(CLBeaconRegion *)region];  
}  
  
// ビーコン情報取得  
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region  
{  
    for(CLBeacon *beacon in beacons) {  
        self.proximityLabel.text = self.proximityDict[@(beacon.proximity)];  
    }  
}  
  
- (void)locationManager:(CLLocationManager *)manager rangingBeaconsDidFailForRegion:(CLBeaconRegion *)region withError:(NSError *)error  
{  
    NSLog(@"Ranging error: %@", error);  
}  
  
- (void)locationManager:(CLLocationManager *)manager monitoringDidFailForRegion:(CLRegion *)region withError:(NSError *)error  
{  
    NSLog(@"Monitoring error: %@", error);  
}  

次にCLLocationManagerDelegateの各メソッドを実装していきます。
領域への出入りは-locationManager:didEnterRegion:と-locationManager:didExitRegion:でそれぞれハンドリング可能です。
領域に入ったときにはCLLocationManagerの-startRangingBeaconsInRegion:を呼ぶことで一定間隔ごとにビーコンの情報を- locationManager:didRangeBeacons:inRegion:で受け取れるようになります。
また検出開始時に既に領域内に入っていると-locationManager:didEnterRegion:でのハンドリングは出来ないため、検出開始時のステータスを判定する必要があります。

以上で受信側の実装は完了です。

実装 〜ビーコンサイド〜

本来ビーコンが用意されていればアプリは受信側のみ実装すればいいのですが、
ビーコン持ってないけどとりあえず試してみたい!というときは他のiOS端末をビーコンにしてしまうことも可能です。
ビーコンはCoreBluetoothフレームワークを使って以下のように実装出来ます。
UUIDは先ほど作った受信側と同じものを設定してください。

#import "SenderViewController.h"  
@import CoreLocation;  
@import CoreBluetooth;  
  
@interface SenderViewController ()<CBPeripheralManagerDelegate>  
@property(strong, nonatomic) CLBeaconRegion *beaconRegion;  
@property(strong, nonatomic) CBPeripheralManager *peripheralManager;  
@property(strong, nonatomic) NSDictionary *peripheralData;  
@end  
  
@implementation SenderViewController  
  
- (void)viewDidLoad  
{  
    [super viewDidLoad];  
    // Do any additional setup after loading the view.  
  
    NSUUID *uuid = [[NSUUID alloc] initWithUUIDString:@"*** UUID ***"];  
    self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:uuid  
                                                           identifier:@"wonderplanet_ibeacondemo"];  
  
    self.peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self  
                                                                     queue:dispatch_get_main_queue()];  
    self.peripheralData = [self.beaconRegion peripheralDataWithMeasuredPower:nil];  
}  
  
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral  
{  
    if (peripheral.state == CBPeripheralManagerStatePoweredOn) {  
        [self.peripheralManager startAdvertising:self.peripheralData];  
    } else if (peripheral.state == CBPeripheralManagerStatePoweredOff) {  
        [self.peripheralManager stopAdvertising];  
    }  
}  
  
@end  

これで実装完了です。2つのiOS端末を用意してそれぞれビーコン側と受信側とし、
2つのデバイスを近づけたり遠ざけたりしてみてください。
受信側で検出に成功したらFar(遠い)、Near(近い)、Immediate(密着)の3段階で大雑把な距離が画面上に表示されるはずです。

ibeacon_screenshot

いかがでしたでしょうか。私は思いの外簡単に実装できたという印象でした。
今回作成したプロジェクトはこちらからダウンロードできます。
iBeaconを応用したおもしろいアプリのアイデアを是非考えてみてください。