iBeaconを嗜む


#1.はじめに
注文していたBeacon機器が届いたので、これをネタに記事を書きたいと思います。

* 本稿範囲：iBeaconを使用してBeacon機器の一覧を表示するアプリを作る。
* 対象読者：Xcodeの基本を理解しているiBeacon初心者
* 開発環境：OS X Mavericks v10.9.4
* IDE:Xcode Ver 5.1.1
* 検証端末：iPhone 5S (iOS:7.1.2)
* Beacon機器：[MyBeacon Pro (MB004)](http://www.aplix.co.jp/?page_id=9463)

#2.iBeaconとは
iOS7から導入された「iBeacon」は、Beacon機器とiOSデバイスとの通信を実現する技術です。
これはBLE(Bluetooth Low Energy)を使用してiOSデバイスがBeacon機器の領域（リージョン）へ進入したこと
または退出したことを検知したり、Beacon機器との近接情報を取得することができます。

##2.1.識別情報
iBeaconアドバタイズメントは、BLEを介して以下の情報を提供します。

| Field | Size | 説明 |
|:-----------|:------------|:------------|
| UUID | 16Byte | アプリケーション開発者が定める固有のID。|
| Major | 2Byte | 細分化されたID。一例として、UUIDを大きい領域のIDとして使用し、Majorはもう一段階細分化した領域のIDとして使用する。|
| Minor  | 2Byte | 更に細分化されたID。|

##2.2.精度
Beacon機器が発するシグナルの受信強度が強い程、位置情報の精度は向上します。
（例えばGPSを使用した位置情報の取得についても衛星との間に遮るものがない場合と、遮るものがある場合とでは前者の方が受信強度が強く、精度が高いのと同じように。）
当然、Beacon機器とiOSデバイスの距離が遠かったり、また、間に遮蔽物が存在することでシグナルが減衰される、等の理由で受信強度が弱い場合、位置情報の精度は低下します。
CoreLocationは、この精度に関する情報をレポートしてくれます。
(CLBeaconクラスのaccuracyプロパティはメートル単位で計測してくれますが、この数値が大きい程、精度は低い。）

iBeaconでは2.4GHzの周波数帯を使用するBLEを使用します。
これは壁や扉、人体といった遮蔽物によりシグナルが減衰する為、距離計測の精度はBeacon機器を設置する環境によって大きく変化します。
Beacon機器を設置する場所の物理環境は、ほぼ確実に精度に影響を与えるという事を頭の片隅においておきましょう。

##2.3.認可
iBeaconはCoreLocationの一部なので、アプリの中で現在の位置情報を使用する事について予め許可を取っておく必要があります。
何故、そのアプリでは現在の位置情報を使用するのかについて利用者に説明し許可を得られやすいようにしておくと良いでしょう。

##2.5.領域モニタリング
ジオフェンスの領域モニタリングと同様、iOSデバイスがBeaconによって定義される領域への出るか、入るかした時には
アプリケーションは通知を受ける事ができます。
アプリがBeacon領域のモニタリングを開始するには、UUIDを指定する必要があります。
（1つのUUIDで同時に複数の領域をモニタリング可能です。）


##2.6.近接状態
デバイスとビーコン機器との近接状態は以下の４段階で表現されます。

| 近接状態 | 説明 | 
|:-----------|:------------|
| Immediate | 直接触れるぐらい近い。 |
| Near     | 1m〜3m程度の距離。ただし、遮蔽物等の影響でシグナルの減衰がある場合、この限りではない。 |
| Far       | ビーコン機器を発見できる程度の距離。精度に関する信頼性は上の二つよりも低く、必ずしも「ビーコン機器までの距離が近くない。」とは言い切れない。 |
| Unknown         | 計測不能。距離の測定が開始されたばかりである可能性もある。|

##2.7.calibration
calibrationとは、Beacon機器の出力値の最適値を探して、その出力値に調整するプロセスです。
Beacon機器を設置する物理環境は、ほぼ確実にシグナル強度に影響を与える為、予め各々のBeacon機器毎に最適な出力値となるように調整する必要があります。

このプロセスの詳細な手順は、[「Getting Started with iBeacon」](https://developer.apple.com/ibeacon/Getting-Started-with-iBeacon.pdf)に詳しく書かれています。

##2.8.一方通行通信
Beacon機器が発するシグナルをiOSデバイスが受信する、という一方通行の通信です。
ですので、Beacon機器を介してiOSデバイスが持つ情報を抜き取られる、といったリスクはありません。

#3.何ができるの？
領域への進入を検知し情報を通知する、という使い方が相性が良さそうです。
（位置を特定しナビゲーションに使用する、というの使い方は逆に相性が悪そうです。）

例えば、小売店の商品棚にBeacon機器を設置しておき、その領域への進入を検知すると、その商品棚に置いてある商品に関する情報をプッシュ通知する、という使い方ができるでしょう。
また、美術館で展示物の近くにBeacon機器を設置し、領域への進入を検知したら、その展示物に関する背景の情報をプッシュ通知する、といった使い方もできそうです。

#4.Beacon機器の購入と初期化
iOSデバイスをBeacon機器として使用する事も可能ですが、やはり専用のBeacon機器で検証してみたい。。。
ということで、[株式会社アプリックス](http://www.aplix.co.jp)さんのMy Beacon Pro MB004を購入。
スターターパックで10台、10,000円でした。（お問合せフォームから注文したい旨を伝えるとよいです。）
銀行振込で支払い数日で届きました。

My Beacon Proのクイックスタートガイドに則りアカウント登録後、専用のiPhoneアプリでBeacon機器の初期化を行います。
（UUID、Major、Minor etc..の設定を行ないます。）

#5.Beaconデバイス検出アプリを作る
さて、いよいよアプリの開発に取りかかります。
検出した全てのBeacon機器から取得した識別情報、近接状態をTableViewに表示するだけのシンプルなアプリです。

![Screenshot.png](https://qiita-image-store.s3.amazonaws.com/0/43853/51776e06-1d73-2a43-fa8c-e2a5ab1d0fe2.png)

プロジェクト名は、BeaconListとし、Single View Applicationテンプレートでプロジェクトを新規に作ります。
Main.storyboardを開いてViewにTableViewを追加します。

![スクリーンショット 2014-07-18 0.22.23.png](https://qiita-image-store.s3.amazonaws.com/0/43853/c02d8c7e-a20c-f0d9-16a3-e34dd11bf1e9.png)

dataSourceとdelegateの設定も忘れずに。

![スクリーンショット 2014-07-18 0.21.09.png](https://qiita-image-store.s3.amazonaws.com/0/43853/c941df5d-d301-237f-12a1-32ada1337d4e.png)


それでは本アプリ唯一のViewControllerを実装していきます。

```ViewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

@interface ViewController : UIViewController <CLLocationManagerDelegate, UITableViewDataSource, UITableViewDelegate>
@end
```

特筆すべき事は特にありませんがCoreLocationを使用しますのでimportをお忘れなく！
以下、ViewControllerの本体のソース全体です。

```ViewController.m
#import "ViewController.h"

@interface ViewController ()
@property (strong, nonatomic) IBOutlet UITableView *tableView;
@end

@implementation ViewController
{
    CLLocationManager *_locationManager;
    NSUUID *_uuid;
    CLBeaconRegion *_beaconRegion;
    NSMutableArray *_beaconArray;
}

#pragma mark - ViewController Life Cycle
- (void)viewDidLoad
{
    [super viewDidLoad];
    _uuid = [[NSUUID alloc]initWithUUIDString:@"00000000-0C4F-1001-B000-001C4D0184A3"];
    _beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:_uuid identifier:[_uuid UUIDString]];
    _locationManager = [[CLLocationManager alloc]init];
    _locationManager.delegate = self;
    
    _beaconArray = [[NSMutableArray alloc] init];
}

- (void)viewDidAppear:(BOOL)animated
{
    [_locationManager startRangingBeaconsInRegion:_beaconRegion];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [_locationManager stopRangingBeaconsInRegion:_beaconRegion];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

#pragma mark - Location Manager
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
    [_beaconArray removeAllObjects];
    [_beaconArray addObjectsFromArray:beacons];
    [self.tableView reloadData];
}

#pragma mark - UITableViewDataSource
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _beaconArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if(cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    CLBeacon *beacon = [_beaconArray objectAtIndex:indexPath.row];
    NSString *prox;
    switch(beacon.proximity) {
        case CLProximityImmediate:
            prox = @"Immediate";
            break;
        case CLProximityNear:
            prox = @"Near";
            break;
        case CLProximityFar:
            prox = @"Far";
            break;
        case CLProximityUnknown:
        default:
            prox = @"Unknown";
            break;
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Major:%@, Minor:%@, Proxy:%@", beacon.major, beacon.minor, prox];
    cell.detailTextLabel.text = [beacon.proximityUUID UUIDString];

    return cell;
}

@end
```

では、各部で何をしているのか、切り取って振り返ってみます。

```
- (void)viewDidLoad
{
    [super viewDidLoad];
    _uuid = [[NSUUID alloc]initWithUUIDString:@"00000000-0C4F-1001-B000-001C4D0184A3"];
    _beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:_uuid identifier:[_uuid UUIDString]];
    _locationManager = [[CLLocationManager alloc]init];
    _locationManager.delegate = self;
    
    _beaconArray = [[NSMutableArray alloc] init];
}
```

Beacon機器の初期化で設定したUUIDを元にCLBeaconRegionのオブジェクトを作成します。
（UUIDの値は適宜、書き換えて下さい。）

そして、CLLocationManagerも初期化して本クラスへデリゲートします。
_beaconArrayには、Beacon機器の一覧を格納しますが、ここではNSMutableArrayで初期化します。

```
- (void)viewDidAppear:(BOOL)animated
{
    [_locationManager startRangingBeaconsInRegion:_beaconRegion];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [_locationManager stopRangingBeaconsInRegion:_beaconRegion];
}
```

viewDidAppearで、Beacon機器の通知を開始し、viewDidDisappearで、Beacon機器の通知を終了します。
Beaconの領域内にいる間、ロケーションマネージャーは`locationManager:didRangeBeacons:inRegion:`メソッドを呼び出します。

```
#pragma mark - Location Manager
- (void)locationManager:(CLLocationManager *)manager didRangeBeacons:(NSArray *)beacons inRegion:(CLBeaconRegion *)region
{
    [_beaconArray removeAllObjects];
    [_beaconArray addObjectsFromArray:beacons];
    [self.tableView reloadData];
}
```
Beacon領域内にいる間、このメソッドが呼び出されます。
_beaconArrayを一旦まっさらにし、通知されたbeacon達を新たに配列へ詰めてからTableViewのreloadを指示します。

```
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *identifier = @"Cell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
    if(cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:identifier];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }

    CLBeacon *beacon = [_beaconArray objectAtIndex:indexPath.row];
    NSString *prox;
    switch(beacon.proximity) {
        case CLProximityImmediate:
            prox = @"Immediate";
            break;
        case CLProximityNear:
            prox = @"Near";
            break;
        case CLProximityFar:
            prox = @"Far";
            break;
        case CLProximityUnknown:
        default:
            prox = @"Unknown";
            break;
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Major:%@, Minor:%@, Proxy:%@", beacon.major, beacon.minor, prox];
    cell.detailTextLabel.text = [beacon.proximityUUID UUIDString];

    return cell;
}
```
_beaconArray配列からCLBeaconオブジェクトをひとつ取り出します。
そして、proximityを取得しCellに出力する文字列を決定します。
Cellに発見したBeaconのUUID、Major値、Minor値、近接状態を出力します。

#5.参考文献
* [iBeacon for Developers](https://developer.apple.com/ibeacon/)