領域観測とiBeacon

Core Locationフレームワークには、特定の領域へのユーザの進入と退出を検出するために、地理的領域の観測(iOS 4.0以降およびOS X v10.8以降)とビーコンによる領域観測(iOS 7.0以降)という2とおりの方法が用意されています。地理的領域とは、地上にある既知の地点から特定の半径内にある円によって定義されるエリアのことです。これに対して、ビーコン領域とは、デバイスのBluetooth Low-Energyビーコンへの近さによって定義されるエリアのことです。このビーコン自身は、特定のBluetooth Low-Energyのペイロードをアドバタイズする単純なビーコンデバイスですが、Core Bluetoothフレームワークの支援を受けると、ご使用中のiOSデバイスをビーコンデバイスに変えることもできます。

アプリケーションは地理的観測を使用して、ユーザが地理的な境界線を横断したとき、またはビーコンの近傍に対して進入または退出したときに通知を受けることができます。ビーコンがiOSデバイスの範囲内にある場合は、アプリケーションはビーコンからの相対距離を監視することもできます。これらの機能を使用すると、多くの種類の革新的な位置情報活用アプリケーションを開発することができます。地理的領域とビーコン領域は別のものなので、使用することに決めた領域観測のタイプによって、アプリケーションの使い道が決まるものと考えられます。

iOSでは、アプリケーションに関連付けられた領域は、アプリケーションが実行していないときも含め、常に追跡されます。アプリケーションが実行していないときに、ある領域の境界線を横切ると、アプリケーションはそのイベントを処理するためにバックグラウンドで起動されます。同様に、イベントが発生したときにアプリケーションが一時停止中の場合、アプリケーションは動作させられて、イベントを処理するために短い時間(約10秒)が割り当てられます。必要であればアプリケーションは、UIApplicationクラスのbeginBackgroundTaskWithExpirationHandler:メソッドを使用して、より長いバックグラウンド実行時間を要求することができます。

OS Xでは、領域観測はアプリケーションが(フォアグラウンドまたはバックグラウンドで)実行中にユーザのシステムが起動している場合にのみ動作します。この結果、領域に関する通知を配信するためにシステムがアプリケーションを起動することはありません。

領域観測の利用可否の確認

特定の領域を観測しようとする前に、アプリケーションは、現在のデバイス上で領域監視がサポートされているかを確認するべきです。領域観測が利用できない理由はいくつか考えられます。

iOS 7.0以降では、領域の観測を行う前にCLLocationManagerisMonitoringAvailableForClass:クラスメソッドとauthorizationStatusクラスメソッドを常に呼び出す必要があります(OS X v10.8以降とiOSの以前のバージョンでは、この代わりにregionMonitoringAvailableクラスメソッドを使用します)。isMonitoringAvailableForClass:メソッドを使用すると、指定したクラスで領域の観測がハードウェアでサポートされているかどうかが分かります。メソッドがNOを返した場合、このデバイスでは領域観測を使用できません。YESが返された場合は、authorizationStatusメソッドを呼び出して、アプリケーションに現在位置情報サービスを使用する承認が与えられているかどうかを判別します。承認ステータスがkCLAuthorizationStatusAuthorizedの場合、アプリケーションは登録されている任意の領域に対する境界線の横断通知を受信できます。承認ステータスがこれ以外の値に設定されていた場合は、アプリケーションはこの通知を受信しません。

最後に、アプリケーションで位置情報の更新をバックグラウンドで処理する必要がある場合は、UIApplicationクラスのbackgroundRefreshStatusプロパティを必ずチェックしてください。 このプロパティの値を使用すると、バックグラウンドでの処理か可能であるかどうかを判断することができ、処理できない場合はユーザに警告できます。なお、「Background App Refresh」の設定を(すべてのアプリケーションに対してグルーバルに、または特定のアプリケーションについて)無効にしている場合、領域通知のためにシステムがアプリケーションを「起こす」ことはありません。

地理的領域の観測

地理的領域の観測では位置情報サービスを使用して、既知の地理的位置についてのあらゆる進入または退出を検出します(位置情報サービスについて詳しくは、「ユーザの位置の取得」を参照)。この機能を使用して、ユーザが特定の位置に近づくとアラートを生成するなど、適切な情報を提供できます。たとえば、所定のドライクリーニング店に近づいたとき、仕上がっている服を受け取るよう通知するアプリケーションが考えられます。

観測する地理的領域の定義

ある地理的領域の観測を始めるには、領域を定義してそれをシステムに登録しなければなりません。iOS 7.0以降では、CLCircularRegionクラスを使用して地理的領域を定義します(OS X v10.8以降と以前のバージョンのiOSでは、これの代わりにCLRegionクラスを使用します)。作成する各領域には、地理上の地域を定義するデータと、一意の識別文字列の両方が含まれている必要があります。識別文字列は、アプリケーションが後で領域を特定するための唯一確実な方法です。領域を登録するには、CLLocationManagerオブジェクトのstartMonitoringForRegion:メソッドを呼び出します。

リスト2-1に、円形のMap Kitオーバーレイに基づいて新しい地理的領域を作成するメソッドの例を示します。オーバーレイの中心点と半径がこの領域の境界線を形成しますが、半径が大きすぎて観測できない場合は、半径が自動的に縮小されます。作成した領域への強い参照を保存する必要ありませんが、領域の情報に後からアクセスする予定の場合は、領域の ID を保存しておく場合があります。

リスト2-1 Map Kitオーバーレイに基づいた地理的領域の作成と登録

- (void)registerRegionWithCircularOverlay:(MKCircle*)overlay andIdentifier:(NSString*)identifier {
 
   // オーバーレイの半径が大きすぎる場合、登録は自動的に失敗に終わるため、
   // 半径を最大値に固定
   CLLocationDistance radius = overlay.radius;
   if (radius > self.locManager.maximumRegionMonitoringDistance) {
      radius = self.locManager.maximumRegionMonitoringDistance;
   }
 
   // 観測する地理的領域を作成する。
   CLCircularRegion *geoRegion = [[CLCircularRegion alloc]
      initWithCenter:overlay.coordinate
              radius:radius
          identifier:identifier];
   [self.locManager startMonitoringForRegion:geoRegion];
}

地理的領域の観測は、認定済みのアプリケーションへの登録後すぐに開始されます。しかし、イベントをすぐに受信するものと想定しないでください。イベントが生成されるのは、境界を横断したときだけです。特に、登録時にユーザの位置がすでに領域の内側にある場合、位置情報マネージャはイベントを自動的には生成しません。アプリケーションが領域の境界線を横断するまで待たないと、イベントが生成されてデリゲートに送信されることはありません。ユーザが領域境界内にいるかどうか判断するためには、CLLocationManagerクラスのrequestStateForRegion:メソッドを使います。

観測する一連の領域を指定するときには常に慎重に検討してください。領域は共有のシステムリソースであり、システム全体で利用可能な領域の総数は限られています。このため、Core Locationは、1つのアプリケーションで同時に観測できる領域の数を20に制限しています。このような制限を回避するには、ユーザの近隣の領域のみ登録することを検討してください。ユーザの位置が変わるのにつれて、距離の遠くなった領域を削除し、ユーザの進路上で近づいている領域を追加することができます。領域を登録しようとしたときに空きがないと、位置情報マネージャはデリゲートのlocationManager:monitoringDidFailForRegion:withError:メソッドを呼び出して、エラーコードkCLErrorRegionMonitoringFailureを渡します。

地理的領域の境界線横断イベントの処理

デフォルトでは、ユーザの現在位置が境界領域を横断するたびに、システムはアプリケーションに対して適切な領域イベントを生成します。アプリケーションは、次のメソッドを実装して境界線の横断を処理することができます。

領域を定義して登録するときに、CLRegionクラスのnotifyOnEntryプロパティとnotifyOnExitプロパティを明示的に設定するとアプリケーションに通知される境界線横断のイベントをカスタマイズできます(プロパティのデフォルト値はどちらもYESになります)。たとえば、ユーザが領域の境界線から退出したときにのみ通知が必要な場合は、領域のnotifyOnEntryプロパティの値をNOに設定します。

システムは、境界線に加え、システムで定義された緩衝距離を越えるまで境界線の横断を報告しません。緩衝距離は、ユーザが境界線付近を移動しているときに、システムが大量の進入・退出イベントを立て続けに生成するのを防ぐための値です。

領域の境界線を横断したときの最も可能性の高い応答は、対象物に接近していることをユーザに知らせることです。アプリケーションがバックグラウンドで実行中の場合、ローカル通知を使用してユーザに知らせることができます。それ以外の場合は単にアラートを送信できます。

ビーコン領域の観測

ビーコン領域の観測では、iOSデバイスに内蔵されている無線を使用して、ユーザがiBeacon情報をアドバタイズしているBluetooth Low-Energyデバイスの近くにいることを検出します。地理的領域の観測と同様に、この機能を使用して、ユーザがビーコン領域に対して侵入または退出した場合に、アラートの生成やその他の適切な情報提供を行うことができます。ただし、ビーコン領域は、固定された地理的座標ではなく、次の値の組み合わせをアドバタイズするBluetooth Low-Energyビーコンとの近接度によって識別されます。

同じビーコン領域が複数のビーコンを表すこともできるので、ビーコン領域の観測機能を活用する、興味深い用途がいくつかあります。たとえばあるデパートの顧客サービス専用アプリケーションでは、同じ近接UUIDを使って、チェーン店すべてを観測できます。ユーザがある店舗に近づくと、アプリケーションはその店のビーコンを検出し、メジャー値とマイナー値に基づいて、検出した具体的な店舗や、その中の売り場などといった事項を判断します(なお、ビーコンは近接UUIDを常にアドバタイズしますが、メジャー値やマイナー値は必要に応じて行うだけです)。

観測するビーコン領域の定義

あるビーコン領域の観測を始めるには、領域を定義してそれをシステムに登録しなければなりません。ビーコン領域は、CLBeaconRegionクラスの適切な初期化メソッドで定義します。CLBeaconRegionオブジェクトを生成する際には、観測するビーコンの、proximityUUIDmajorminorの各プロパティを指定します(近接UUIDは必須、それ以外は必要な場合のみ)。さらに、コード中で参照できるよう、領域を一意に識別する文字列を指定しなければなりません。なお、領域の識別文字列は、ビーコンがアドバタイズする識別情報とは無関係です。

領域を登録するには、CLLocationManagerオブジェクトのstartMonitoringForRegion:メソッドを呼び出します。リスト2-2に、ビーコン領域を作成して登録するメソッドの例を示します。

リスト2-2 ビーコン領域の作成と登録

- (void)registerBeaconRegionWithUUID:(NSUUID *)proximityUUID andIdentifier:(NSString*)identifier {
 
   // 観測するビーコン領域を作成する。
   CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc]
      initWithProximityUUID:proximityUUID
                 identifier:identifier];
 
   // ビーコン領域を位置情報マネージャに登録する。
   [self.locManager startMonitoringForRegion:beaconRegion];
}

地理的領域の観測と同様に、ビーコン領域の観測は、認定済みのアプリケーションへの登録直後に開始されます。ユーザのデバイスが、登録されたビーコン領域(近接UUID、メジャー値、マイナー値)によって定義された識別情報をアドバタイズしているビーコンを検出すると、システムはアプリケーションに対して適切な領域イベントを生成します。

ビーコン領域の境界線横断イベントの処理

登録済みのビーコン領域にユーザが進入すると、位置情報マネージャはデリゲートオブジェクトのlocationManager:didEnterRegion:を呼び出します。同様に、登録済みのすべてのビーコン領域内からユーザがいなくなった場合にも、位置情報マネージャはデリゲートオブジェクトのlocationManager:didExitRegion:を呼び出します。なお、ユーザが領域境界を横切らなければ、上記のメソッドが呼び出されることはありません。特に、ユーザが既に領域内にいる場合、位置情報マネージャがlocationManager:didEnterRegion:を呼び出すことはありません。これらのデリゲートメソッドは、ユーザの接近を知らせる、またはある位置に固有のUIを表示するために実装することができます。

実際にアプリケーションに通知する境界横断イベントは、ビーコン領域のnotifyOnEntryプロパティ、notifyOnExitプロパティで指定できます(プロパティのデフォルト値はどちらもYESになります)。たとえば、ユーザが領域の境界線から退出したときにのみ通知が必要な場合は、領域のnotifyOnEntryプロパティの値をNOに設定します。

ユーザがデバイスのディスプレイをオンにするまで、ユーザのビーコン領域への進入の通知を遅らせることも可能です。これを行うには、ビーコン領域の登録時に、ビーコン領域のnotifyEntryStateOnDisplayプロパティの値をYESに設定(そして領域のnotifyOnEntryプロパティをNOに設定)するだけです。ユーザに通知が何回も送信されるのを防止するには、領域への進入ごとに1回だけ位置に関する通知を配信するようにします。

ビーコンの距離測定を使用したビーコンまでの距離の決定

ユーザのデバイスが登録済みのビーコン領域の内部にある場合、アプリケーションではCLLocationManagerクラスのstartRangingBeaconsInRegion:メソッドを使用して、領域内の1つ以上のビーコンとの相対的な距離を決定し、この距離が変化したときに通知を行うことができます(ビーコン領域内でビーコンとの距離測定を行う前には、必ずCLLocationManagerクラスのisRangingAvailableクラスメソッドを呼び出す必要があります)。ビーコンとの相対距離を把握していることは、多くのアプリケーションにとって役に立ちます。たとえば、博物館を考えてみましょう。展示物ごとにビーコンが置いてあります。博物館の専用アプリケーションは、展示物との距離を手がかりに、該当する展示物に関する情報を表示します。

位置情報マネージャは、指定したビーコン領域にあるビーコンが範囲内に入った場合、範囲外に出た場合、あるいは距離が変わった場合に、デリゲートオブジェクトのlocationManager:didRangeBeacons:inRegion:を呼び出します。このデリゲートメソッドには、現在範囲内にあるビーコンを表す、CLBeaconオブジェクトの配列が用意されています。このビーコンの配列は、最も近くにあるビーコンが配列の先頭にあるように、デバイスからの距離によって整列されています。これらのオブジェクトにある各ビーコンについての情報を使用して、各ビーコンとの距離を決定することができます。CLBeaconオブジェクトのproximityプロパティ値には、一般的な意味におけるビーコンとの相対距離が与えられます。

先に説明した博物館アプリケーションを元に作成されたリスト2-3のコードには、ユーザのデバイスからの相対距離を決定するためにproximityプロパティを使用する方法が示されています。このコードは、配列で最も近くにあるビーコンとの相対距離がユーザに比較的近い(CLProximityNear定数によって定義される)場合に、博物館の特区の展示物についての詳細な情報を提示するUIを表示します。

リスト2-3 ビーコンとデバイスの相対距離を判定するコード例

// CLLocationManagerDelegateプロトコルのデリゲートメソッド
- (void)locationManager:(CLLocationManager *)manager
        didRangeBeacons:(NSArray *)beacons
               inRegion:(CLBeaconRegion *)region {
 
   if ([beacons count] > 0) {
      CLBeacon *nearestExhibit = [beacons firstObject];
 
      // 展示物に固有のUIは、
      // ユーザがその展示物の比較的近くにいる場合にのみ表示される。
      if (CLProximityNear == nearestExhibit.proximity) {
         [self presentExhibitInfoWithMajorValue:nearestExhibit.major.integerValue];
      } else {
         [self dismissExhibitInfo];
   }
}

一貫した結果が得られるよう、ビーコンの距離測定は、アプリケーションがフォアグラウンド状態の間のみ行うようにしてください。アプリケーションがフォアグラウンド状態であれば、おそらくデバイスはユーザが手に持っており、ビーコンまでの間に視界を遮るものはあまりないでしょう。フォアグラウンド状態で実行すれば、電池の寿命を延ばすことにもなります。ユーザが実際にデバイスを使っている間だけ、入ってくるビーコン信号を処理することになるからです。

iOSデバイスをiBeaconとして利用

Bluetooth Low-Energyによるデータ共有に対応したiOSデバイスはすべて、ビーコンデバイスとして使うことができます。アプリケーションはフォアグラウンドで動作しなければならないので、iOSデバイスでもiBeaconの処理は可能ですが、これはテスト目的か、POSアプリケーションのように常にフォアグラウンドで動作するものに限ります。それ以外の目的でiBeaconを実装する場合、サードパーティからビーコンとして使える専用ハードウェアを入手する必要があります。

iOSデバイスをビーコンデバイスにするには、Core Bluetoothフレームワークを使用する必要があるため、XcodeプロジェクトにあるアプリケーションがCoreBluetooth.frameworkリンクされていることを確認してください。 このフレームワークのクラスとヘッダにアクセスするには、関連するソースファイルの先頭に#import <CoreBluetooth/CoreBluetooth.h>ステートメントを追加します。

ビーコン領域の作成とアドバタイズ

iOSデバイスをビーコンとして使用するには、最初にビーコン領域の近接UUIDとして使用できる128ビットのUUIDを生成する必要があります、ターミナルを開き、「uuidgen」と入力してください。次の例のような、ハイフンで区切られたASCII文字列の形式で、一意の128ビット値が得られます。

$ uuidgen
39ED98FF-2900-441A-802F-9C398FC199D2

次に、ビーコンの近接UUID用に生成したこのUUIDを使用して、ビーコン領域を作成します。このとき、必要であればメジャー値とマイナー値を定義します。新しい領域では一意の文字列識別子も使用されていることを確認します。このコードは、先のUUIDの例を使用して新しいビーコン領域を作成する方法を示します。

   NSUUID *proximityUUID = [[NSUUID alloc]
      initWithUUIDString:@"39ED98FF-2900-441A-802F-9C398FC199D2"];
 
   // ビーコン領域を作成する。
   CLBeaconRegion *beaconRegion = [[CLBeaconRegion alloc]
      initWithProximityUUID:proximityUUID
                 identifier:@"com.mycompany.myregion"

ビーコン領域を作成したら、Core BluetoothフレームワークのCBPeripheralManagerクラスを使用して、ビーコンの近接UUID(および指定したメジャー値とマイナー値)をアドバタイズする必要があります。Core Bluetoothでは、ペリフェラルとはBluetooth Low-Energyを使用してアドバタイズとデータの共有を行うデバイスのことです。ビーコンのデータをアドバタイズすることは、他のデバイスがビーコンを検出して距離を測定するための唯一の手段です。

Core Bluetoothでペリフェラルデータをアドバタイズするには、CBPeripheralManagerオブジェクトのインスタンスにあるCBPeripheralManagerクラスのstartAdvertising:メソッドを呼び出すことができます。このメソッドではアドバタイズデータの辞書(NSDictionaryのインスタンス)があることを前提にしています。次の例に示すように、CLBeaconRegionクラスのperipheralDataWithMeasuredPower:メソッドを使用して、Core Bluetoothがペリフェラルとしてビーコンをアドバタイズするために必要な他の情報とともにビーコンの識別情報をエンコードする辞書を取得します。

   // アドバタイズデータの辞書を作成する。
   NSDictionary *beaconPeripheralData =
      [beaconRegion peripheralDataWithMeasuredPower:nil];

次にCBPeripheralManagerクラスのインスタンスを作成し、次のコード例のように、他のデバイスから検出されるようアドバタイズを行います。

   // ペリフェラルマネージャを作成する。
   CBPeripheralManager *peripheralManager = [[CBPeripheralManager alloc]
      initWithDelegate:selfqueue:nil options:nil];
 
   // ビーコンのデータのアドバタイズを開始する。
   [peripheralManager startAdvertising:beaconPeripheralData];

アプリケーションをビーコンとしてアドバタイズした後、アプリケーションをフォアグラウンド状態のまま稼働させ、必要なBluetooth信号をブロードキャストする必要があります。ユーザがアプリケーションを停止すると、システムはデバイスをペリフェラルとしてアドバタイズすることをやめます。

ペリフェラルマネージャを使用してBluetooth Low-Energyを使用したデータのアドバタイズを行う方法の詳細については、『CBPeripheralManager Class Reference』および『Core Bluetoothプログラミングガイド』を参照してください。

iOSアプリケーションの領域観測サポートのテスト

iOSシミュレータまたはデバイスで領域観測コードのテストを行う場合は、領域の境界線が横断されてもすぐには領域イベントが発生しないことがあることを理解しておいてください。不確かな通知が行われるのを避けるために、iOSでは一定のしきい値条件が満たされないと領域通知を行いません。特に、ユーザの位置が領域の境界線を横断し、この境界線から最小距離だけ離れてから、この位置を少なくとも20秒間保持しないと、通知は報告されません。

この距離のしきい値は、ハードウェアと現在使用可能な位置情報テクノロジーとによって決められています。たとえば、Wi-Fiが無効になっている場合、領域観測の精度は大幅に低下します。しかしながら、テスト目的の場合は、最小距離は約200メートルであると仮定することができます。