ちょっと話題の記事

[iOS8] iOS8でiBeacon実装の落とし穴

2014.11.28

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

お久しぶりです。大村です。
弊社のブログに対して、意外と最近検索件数で多いキーワードが「iBeacon」だったりします。
iPhone6にはNFCチップが搭載され、もしやiBeaconを捨てたのか?とさえ思えた昨今、iPhone6に搭載されているNFCはApple Pay専用であることが判明し、まだまだBeaconの出番はアリそうだと安堵しました。

弊社でもこれまでこの記事にあるようにiBeaconの勉強会を開いたり、業務でiBeaconを使うプロダクトを提案したりなど行ってきましたが、iOS8になったことで、今までの実装通りではうまくいかない事がありました。
そんなtips的な記事になります。

基本的には、前回iOS7の時に初出した「[iOS 7] 新たな領域観測サービス iBeacon を使ってみる」での実装でいいのですが、困ったことにiOS8ではこの実装でうまく動かせられません。
それが、iOS8で追加要件が増えた箇所の問題でした。

iBeaconが使っているCoreLocationクラスが、今回からセキュリティポリシーレベルが変更され、2パターン選べるようになりました。
Always:バックグラウンドでも動作させるときに使う権限
WhenInUse:アクティブなときだけ動作させるときに使う権限
通常の場合はWhenInUse使えばいいのですが、バックグラウンドでrangeを監視し続けるアプリが多くなると思うので、ここではAlways権限を指定することにしましょう。

まずは実装を

SwiftにてiBeaconを動かすコードを書きます。
今回は横着してAppDelegateにコードを書き込みます。

まずはライブラリを使えるようにします。

フレームワーク

AppDelege.swiftに以下のコードを書き込みます。

import UIKit
import CoreLocation

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, CLLocationManagerDelegate {

    var window: UIWindow?
    var locationManager: CLLocationManager!
    var uuid:NSUUID?
    var beaconRegion:CLBeaconRegion?

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        // Override point for customization after application launch.
        /* beaconの設定を行う */
        self.uuid = NSUUID(UUIDString: "xxxxxxxxxxx") /* proximity UUID文字列 */
        self.beaconRegion = CLBeaconRegion(proximityUUID: self.uuid, identifier: "説明文") /* 認知可能なBeaconの設定を行う。この方法だとMajor/Minor関係ない */
        self.locationManager = CLLocationManager()
        self.locationManager.delegate = self; /* 権限追加の結果が呼ばれる */
        
        self.locationManager.requestWhenInUseAuthorization() /* アクティブなときのみ動作する */
        // self.locationManager.requestAlwaysAuthorization() /* バックグラウンドでも動作する */
        
        return true
    }

    func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
        switch status
        {
        case .Authorized, .AuthorizedWhenInUse:
            /* 許可された時はiBeaconをOnにする */
            self.locationManager.startMonitoringForRegion(self.beaconRegion)
            break;
        default:
            /* 許可されてない */
            break;
        }
    }
    
    func locationManager(manager: CLLocationManager!, didEnterRegion region: CLRegion!) {
        /* 入った際の処理を書く */
    }
}

ちなみに、今までと変わったところで言えば、self.locationManager.requestAlwaysAuthorization()でユーザーに権限許可を求めているところでしょうか。
実際は権限がもう選択されていればここをスキップするなどの判定を入れる必要があります。
これによって、任意のタイミングでユーザーに権限許可を求めることが出来ます。

権限の説明記載をお忘れなく

いざ、このまま動かそうと思ったけど、うんともすんとも動きません。
というより、権限が聞かれません。

今回から、info.plistに、どの用途で使うかを明記しなければならなくなりました。
具体的には、info.plistに「NSLocationAlwaysUsageDescription」もしくは「NSLocationWhenInUseUsageDescription」っていうキーを追加します(両方あっても問題ありません)。
これを以下のように入力します。

 レンジ設定

すると、実行したら問い合わせ画面が出てきます。

 確認画面

これで無事、iOS8でもiBeaconを使うことが出来ます。

最後に

許可権限に関してのポリシーが厳しくなりましたが、何も表示されず困ってました。
CoreLocationを使う時は、気をつけて下さい。

そうそう、最近iBeaconで何が出来ますか?ってよく聞かれます。
iBeaconでできることはiBeacon端末を見つけましたってところまでです。
逆に言うとそれしか出来ません。
あることをしようとして、それを達成するためにiBeaconを使うってことは簡単ですが、逆は厳しいです。
ってことをちょろっと言いたくなりました。そんな感じです。

あ、メイド喫茶に入店したかどうかをiBeaconで察して、メイド喫茶のアプリを立ち上げるとか、そういうソリューションやってみたいですどうですかね(私利私欲ですねすいません(笑