iBeacon(2) - Bluetoothと位置情報設定を監視しよう

さて、では実装に入りましょう。以下のコードはこちらgithub: ESBeaconにアップしてありますので参照しながら読んでいただけるとよいかと思います。

iBeacon Singletonオブジェクトの用意

まずは、バックグランドでの動作も想定して、Viewから独立したiBeacon管理用オブジェクトをSingletonとして用意します。

こちらの記事のテンプレートを活用して、

Objective-C – Singleton(シングルトン)クラスの実装 -

ESBeaconというクラスをSingletonとして実装します。ESBeaconではBluetoothや位置情報のアップデートも受け取りますので、CBPeripheralManagerDelegateおよびCLLocationManagerDelegateのdelegate宣言もしておきます。

ESBeacon.h

@interface ESBeacon : NSObject <CBPeripheralManagerDelegate, CLLocationManagerDelegate>
+ (ESBeacon *)sharedManager;
...
@end

ESBeacon.m

@implementation ESBeacon

+ (ESBeacon *)sharedManager
{
    static ESBeacon *sharedSingleton;
    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{
        sharedSingleton = [[ESBeacon alloc] initSharedInstance];
    });

    return sharedSingleton;
}

- (id)initSharedInstance {
    self = [super init];
    if (self) {
        // Initialization of ESBeacon singleton.
        ...
    }
    return self;
}

- (id)init {
    [self doesNotRecognizeSelector:_cmd];
    return nil;
}

これにより、[ESBeacon sharedManager]を呼ぶことでESBeaconのsingletonオブジェクトを取得することができます。githubのコードでは、ESBeaconViewController.m内で、

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.beacon = [ESBeacon sharedManager];
    ...
}

このように呼び出しています。

Bluetoothのデバイス状況監視 - CBPeripheralManager

次にBluetoothのデバイス状況の監視設定をします。Bluetooth関連機能を使う場合は、@import CoreBluetoothとして機能をインポートします。

iBeaconの機能のみにアクセスする場合、CoreBluetoothをまったく使用せず、CoreLocationのみで実装することも可能ですが、Bluetoothがオフになっている場合、iBeaconへのアクセスをしてエラーが帰ってくるまで状況が把握できないので、Bluetoothのデバイス状況も監視し、必要な場合はユーザーへ設定の確認をうながすなどしたほうがよいでしょう。

Bluetoothの設定情報はCBPeripheralManagerのdelegateである、CBPeripheralManagerDelegateで通知されますので、ESBeaconでCBPeripheralManagerをallocしてdelegateをselfにセットします。

ESBeacon.m

@interface ESBeacon ()
@property (nonatomic) CBPeripheralManager *peripheralManager;
@end

@implementation ESBeacon
- (id)initSharedInstance {
    self = [super init];
    if (self) {
        // Initialization of ESBeacon singleton.
        _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];
    }
}

- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral
{
    NSLog(@"peripheralManagerDidUpdateState: %@", [self peripheralStateString:peripheral.state]);
}
@end

[self peripheralStateString]ではCBPeripheralManagerStateに対応した文字列を返しています。これでBluetoothのデバイス状況監視が完了です。

位置情報設定の監視 - CLLocationManager

位置情報設定の監視は、CLLocationManagerCLLocationManagerDelegateから通知されます。設定が変更されると、didUpdateAuthorizationStatusが呼ばれます。CBPeripheralManagerと同様、allocしてdelegateをselfにセットします。

ESBeacon.m

@interface ESBeacon ()
@property (nonatomic) CBPeripheralManager *peripheralManager;
@property (nonatomic) CLLocationManager *locationManager;
@end

@implementation ESBeacon
- (id)initSharedInstance {
    self = [super init];
    if (self) {
        // Initialization of ESBeacon singleton.
        _peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

        _locationManager = [[CLLocationManager alloc] init];
        _locationManager.delegate = self;
    }
}
@end

delegateのmethodを用意します。

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    NSLog(@"locationManager didChangeAuthorizationStatus %@", [self locationAuthorizationStatusString:status]);
}

これで位置情報設定情報のアップデートが受信できるようになりました。

githubのコードでは、アップデートがあった時にUIViewControllerに通知をして現在のデバイスの状態を画面に表示するようにしています。

アプリインストール直後で、まだ位置情報サービスのリクエスト一度もしていない状態だと、[CLLocationManager authorizationStatus]はkCLAuthorizationStatusNotDeterminedを返します。その通りなのですが、ちょっと不思議な感じがしますね。

次回はリージョン監視とレンジングを行います。