新機能 iBeacon とは?

 指定した領域を観測するために iOSでは以前から Core Location フレームワークを使用していました。その Core Location フレームワークに、今回新たに iBeacon という機能が追加されました。

 以前までは、デバイスを観測するために、地理的地域の観測を行っていました。この方法は GPS 等の機能を使用してデバイスを検出する、という方法をとっていたために近距離の観測には向かず、また屋内では GPS が取得できないことが多いなどの問題点がありました。

 iBeacon は、Bluetooth LEを使用することで、「ビーコン」と呼ばれる発信器の、近接や距離を検知する事が出来るようになります。このビーコンには、「Bluetooth LEタグ」という名称で市販されている物や、iOS 端末を使用する事が出来ます。これにより、GPS では実現できなかった屋内や、近距離での端末の検出を行うことができるようになります。

 それでは早速 iBeacon を利用してみましょう。

監視する対象となるビーコン領域を作成

 観測を行うには、まず対象のビーコン領域を作成する必要があります。CLBeaconRegion クラスを使用して領域を定義します。
 これを行うには、次のように記述します。

CLBeaconRegion *region = [[CLBeaconRegion alloc]
                initWithProximityUUID:UUID
                           identifier:identifier]; 

 インスタンスを作成する際、以下のパラメータを用いてビーコン領域を指定します。

  • proximity UUID
  • major value
  • minor value

 proximity UUID はビーコンそのものや、所属するグループを識別するための値で、一意のIDを設定します。なので、UUIDは重複しないようにする必要があります。この UUID の生成方法はいくつかありますが、ターミナルで uuidgen と打ち込むことで生成されるものを使用すれば十分でしょう

% uuidgen

   major value は16ビットの符号無し整数値で、UUIDが同じビーコンを区別するためのものになります。
 minor value は major value と同じく16ビットの符号無し整数値で、UUID も major value も同じビーコンを区別するために使用されます。
 CLBeaconRegion クラスのインスタンスを作成するコンストラクタは次のようになります。

  • initWithProximityUUID:identifier:

 基本的にこのメソッドを使用してインスタンスを作成するのが良いでしょう。
 UUID は先ほど説明した物を使用してください。
 identifierというのは、コード内の特定のビーコンへアクセスするための文字列となるので、適切な値を設定しておくことをお勧めします。

 しかし、たくさんのUUID をアプリの中に持ちたくはないけど、ビーコンはたくさん検出させたいという時もあるかと思います。そんなときには、UUID を一つにし major や minor で階層的にわける事が出来ます。

ビーコン領域への出入りを監視する

監視を行うためには次のように記述します。

//CLLocationManagerをインスタンス化しデリゲートをセットする。
_manager = [[CLLocationManager alloc] init];
_manager.delegate = self;

//ビーコン領域を生成する
_region = [[CLBeaconRegion alloc] initWithProximityUUID:UUID
                                    identifier:identifier]

//監視をスタート
[_manager startMonitoringForRegion:_region];
  • 前提として、デリゲートメソッドを介して渡される結果を取得することができるように CLLocationManagerDelegate を実装しておく必要があります。
  • 監視を行うには、CLLocationManager をインスタンス化する必要があります。
  • CLLocationManager のインスタンスを使用し startMonitoringForRegion: メソッドを呼び出します。このメソッドにビーコン領域のインスタンスを引数として渡すことで、インスタンスの情報をもとにデバイスの出入りを監視することができるようになります。

 デバイスが監視領域に出入りするたびに、CLLocationManager は、デリゲートのメソッドを呼び出します。

メソッド 説明
locationManager:didEnterRegion: ビーコンデバイスの領域内に入ったときに呼び出されるメソッド
locationManager:didExitRegion: ビーコンデバイスの領域内から出たときに呼び出されるメソッド

 監視対象となるビーコンの領域に出入りした際、そのビーコン領域の情報の開示や、適切なタイミングでアラートを表示するなどの動作を行うためにこのメソッドを実装する必要があります。

 監視中に何かのエラーが発生した際、 CLLocationManager はエラー用のデリゲートメソッドである locationManager:monitoringDidFailForRegion:withError: を呼び出します。エラーへの対処を行うためにこのメソッドを実装することをおすすめします。

ビーコン領域内でのデバイスとの距離を監視する

 iBeacon にはもう一つ、監視方法があります。この方法を使用することで、ビーコンとデバイスとの距離を知ることができます。
 これを行うには次のように記述します。

//先ほどのコードと同じなので省略

//監視をスタート
[_manager startRangingBeaconsInRegion:_region];

 基本的な流れは先ほど説明した方法と同様です。

 CLLocationManager のインスタンスを使用し、startRangingBeaconsInRegion: メソッドを呼び出します。これにより デリゲートメソッド locationManager:didRangeBeacons:inRegion: が一定間隔で呼ばれます。このデリゲートメソッドには、検出した結果が返ってくるので、ユーザへ検出したビーコンとの相対距離を開示するなどの処理を行う為に、実装しておきましょう。

 また、何らかの不具合によりエラーが発生した場合には CLLocationManager がエラー用のデリゲートメソッドである locationManager:rangingBeaconsDidFailForRegion:withError: を呼び出すので、このメソッドを実装して、エラーに対応するようにしましょう。

 デリゲートメソッドに渡される didRangeBeacons は、検出したビーコンの情報である CLBeacon クラスが格納された配列です。
 CLBeacon クラスは、ビーコンの検出が成功したときに得られる結果が入ったクラスになっています。そのため、直接このクラスをインスタンス化して使用することはありません。このクラスには次の6つのプロパティが設定してあります。

プロパティ 説明
proximityUUID 識別子
major
minor
proximity ビーコンとの距離
accuracy 近接値の精度
rssi 受信強度

 距離を取得することのできるプロパティの proximity ですが、距離とは言っても正確に数値としてわかるわけではなく、大体の近さを示す4種類の値で取得します。

enum 距離
CLProximityUnknown わからない、見つからない
CLProximityImmediate すごく近い
CLProximityNear Immediate よりは遠いが、far よりは近い
CLProximityFar 遠い

 これらのステータスをもとに、ビーコンまでの大体の距離をユーザへ通知することができます。

iOSデバイスをビーコンとして使用する

 iBeacon では iOS 端末自体をビーコンデバイスとして扱うこともできます。それには、監視用のアプリが検出できるように端末の情報を告知する必要があります。
 それを実現するためには次のように記述します。

//CBPeripheralManagerをインスタンス化
self.manager = [[CBPeripheralManager alloc]
                        initWithDelegate:self 
                                   queue:dispatch_get_main_queue()];

//告知するビーコンの情報を設定
CLBeaconRegion *region = [[CLBeaconRegion alloc]
                    initWithProximityUUID:UUID
                               identifier:identifier];

//設定した値を使用して告知に使用するdictionaryを取得する
NSDictionary *dictionary = [region peripheralDataWithMeasuredPower:nil];

//dictionaryを使用して告知開始
[self.manager startAdvertising:dictionary];
  • Core Bluetooth フレームワークの中で定義されている CBPeripheralManager をインスタンス化します。この時に、結果をハンドリングするために、CBPeripharalManagerDelegateを実装しておき、それを CBPeripheralManager に delegate として設定します。
  • CLBeaconRegion のインスタンスを作成します。ここで使用するメソッドは監視を行うために使用していたメソッドと同じものです。
  • 作成した CLBeaconRegion クラスのインスタンスを使用し、peripheralDataWithMeasuredPower メソッドを呼び出します。これにより、告知するための情報が入っている NSDictionary オブジェクトを取得することができます。このオブジェクトは特に触る必要は無いので、そのまま保持しておきます。
  • CBPeripheralManager クラスのインスタンスを使用し startAdvertising: メソッドに先ほど作成した Dictionary を引数として渡して告知を開始します。

 告知を開始すると、デリゲートメソッドの peripheralManagerDidUpdateState: が呼ばれるようになります。このメソッドはマネージャの状態が変更されたタイミングで呼び出されます。よって、告知が本当に開始されているのかどうかを確認するためにこのメソッドを実装しておくと良いでしょう。

 この時にメソッドに引数として渡される CBPeripheralManager クラスのプロパティである state を参照することで現在のマネージャの状態を知ることができます。
 

ビーコンのカテゴライズ

 ビーコン定義時に指定するIDの使い方について考えてみましょう。

 例として、美術館で iBeacon を使用し、ユーザへ作品までの距離やフロア内の情報を表示できるようにするアプリを考えます。ここで使用する発信器に指定するIDの振り方としては、まず美術館内に配置するビーコン全てに同じUUIDを指定します。  次に、フロアでの識別を行うために、各フロアのビーコン毎に major value を設定します。最後にフロア内にあるそれぞれの作品を識別するための minor value を指定します。

 こうすることで、例えばユーザが3階のフロアにたどり着いたときに一番近いビーコンから3階の情報を取得します。それと同時に、その階にある複数の作品までの距離が大まかにわかるように表示する、と言った様なことができるのではないでしょうか。

iBeaconを使用する上で

 ここまで説明を行ってきた iBeacon ですが、実際に使用するにあたり知っておくと良い事をまとめておきます。これらの情報が、使用する際の問題点や解決方法を見いだす手助けになると幸いです。

注意点

 iOSの端末を、ビーコンデバイスとして使用する時には、位置情報送信をONにする必要があります。これは、アプリケーションごとに設定することができるようになっているため、見落としがちなので注意が必要です。

iPad mini 環境設定画面

 画像はiPad miniで設定したものになっています。画像にもあるように、iOSの設定から行います。

[設定] > [プライバシー] > [位置情報サービス] > 各アプリ毎の設定

 この設定が “OFF” になっていると、監視を始めた時に CLLocation Manager が locationManager:rangingBeaconsDidFailForRegion:withError: を呼び出してしまいます。
 基本的には、アプリをインストールして初めて監視を行う際に、次の画像のようなアラートが表示されます。この時に OK ボタンをタップすれば自動的に “ON”になります。

位置情報使用 許可アラート

 もし、原因の分からないエラーが発生した際にはこの設定を疑うのもひとつの手ではないかと思います。  

距離 proximity

 iBeacon の説明をしていたときにも触れましたが、距離を取得することのできる監視モードでの取得値についてまとめたいと思います。距離とはいえ数値が取得できる訳ではなく大まかな値として、enum に設定された定数が取得できるという説明をしたと思います。

 そのときに取得することのできる値が Unknown , Immediate , Near , Far の4つでした。Unknownは距離がわからなかったときの値ですが、残りの3つについては 実際にどのくらいの距離なのかわかりません。

 そこで、これについて簡単な実験を行ったところ、次のような結果になりました。

enumの値 実際の距離
CLProximityImmediate 50cm以内
CLProximityNear 50cm~6m
CLProximityFar 6m~20m

 この実験を行った環境は、10×20m くらいの大きさのオフィス内で、PCが多数存在していました。オフィス内にはFarになる場所が無かったため、壁などの障害物を挟んでの計測になっています。
 もちろんこの結果が全てという訳ではありません。実験する環境にも左右されると思いますのであくまでも目安の値になります。  

領域への出入り

 領域への出入りを監視するメソッドについてもテストを行いました。その結果を簡単にまとめます。

  • スタート時、すでに監視対象の領域内にいる場合には、デリゲートメソッドである didEnterRegion: メソッドが呼ばれることはありません。あくまでもこのデリゲートメソッドは領域の境界部分を超えた場合に呼び出されます。
  • didExitRegion: メソッドについては、領域の外へ出てから約 35 秒程のタイムラグの後に呼び出されてしまいます。もちろんここで出している 35 秒という値は、あくまでも実験結果の値ですので、環境によっては全く別な結果になる可能性もあります。おそらく、境界部分をすぐに行ったり来たりした時に、毎回 didExitRegion: と didEnterRegion: を交互に呼ぶ必要もないという事を考えての仕様なのではないかと思います。
  • proximity が Unknown になった場所で 40 秒程待った時に、didExitRegion: は呼ばれるのかということを行いました。その結果 didExitRegion: が呼ばれることはありませんでした。これも実測値でしか無いのですが、出入りを監視する範囲と、proximity が Far から Unknown になる範囲はずれているのでは無いでしょうか。

おわりに

 今回の特集はいかがだったでしょうか。
iBeacon はとても豊富な可能性を秘めていると思います。
この機能を使用することで、さらにiOSのアプリの幅が広がって行くと言っても良いのではないでしょうか。

 iOSだけではなく、周辺機器でブルートゥースに対応しているものを検出することもできるので、いろいろな使い道が浮かんできそうですね。
 そのうち、iBeaconを使用したサービスなんかもできるようになるのではないでしょうか。

それでは次回の特集もお楽しみに!