この記事は,フラー株式会社 Advent Calendar 2021 の 16 日目の記事です
15日目の記事は @aikosogabe さんによる Reactで教習所Lifeをちょっと助けてくれるかもしれないアプリをつくってみた でした
この題材を選んだのはiBeaconについて触れる機会があったので調べた内容をまとめておきたいなと思ったのと、そこそこ有名だけど使われている機会に巡り合えないなと思っていたからです
(発信機にコストがかかってしまうので、だいたい同じような機能はGPSでの判定を使っているケースが多いからだと思いますが...)
読んでみて何かいいアイデアが思いついたら、コメントして教えてください
#iBeaconとは
iBeaconについて調べてみると、こんな感じで
- Apple社の商標
- Bluetooth low energy(通称BLE)のブロードキャスト通信を利用したiOSの近接通知機能
- iOS7から搭載された機能
- iBeaconとはこの通信プロトコル自体であり、この機能そのものを指すこともある(今回はこの機能について指しています)
英語のため自分は読めなかったのですがAppleの資料はこちらです
https://developer.apple.com/ibeacon/
ちなみにiOS7が発表されたのは2013年9月18日だそうでこの時にコントロールセンターやAirDrop,CarPlayの機能が追加されたらしいです
この機能が追加されたのってそんな前だったんですね...
簡単に仕組みを
発信側の端末(ビーコン端末)と受信側の端末(iOSアプリ等)を用意して
発信側の端末がIDなどの情報を一定の時間間隔でBLEに基づいたアドバタイズ(定期的に発信を行うこと)させます
イメージはこんな感じです、点線がアドバタイズされている情報だと思ってください
アドバタイズされた情報(基本的にはUUIDのみ)を受信側の端末で受け取ったらその情報に基づいたデータの取得などを行います、それによって受信側の端末では発信されたそれぞれのビーコン端末によって違った表現などが可能になります
複数のビーコン端末を用意する場合はメジャー値マイナー値を設定することでどの端末の情報を受信したのか見分けることができます
iOSアプリで受信してみる
パーミッション
必要なのは位置情報取得のパーミッションで
アプリの使用中のみであればNSLocationWhenInUseUsageDescription
バックグラウンドであればNSLocationAlwaysUsageDescription
の許可が必要になります
BLEを使うのにBluetoothではなく位置情報取得の許可が必要になるのかと思った方もいると思いますが、Bluetooth自体の電源がオフになっていると利用できないので注意してください
領域監視
ビーコンの発信領域にいるかいないかの測定を行い領域内である場合にイベントの発火などを行う時に使用します
// BeaconRegionで受信したいアドバタイズのUUIDを指定しておきます
let beaconRegion = CLBeaconRegion(uuid: UUID(uuidString: ""), identifier: "identifier")
// CLLocationManagerDelegate経由で先述のパーミッションの変更を受け取ります
// アプリ使用中のみ許可を選択されたときに受信したい場合このようにして領域監視を開始します
func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
if manager.authorizationStatus == .authorizedWhenInUse {
manager.startMonitoring(for: beaconRegion)
} else {
manager.stopMonitoring(for: beaconRegion)
}
}
// 領域監視が開始されたらこの関数内で領域内であるかの判定を行いハンドリングさせます
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
// どのビーコンであるのかUUIDやMajor値Minor値はCLBeaconRegionから取得できます
guard let beaconRegion = region as? CLBeaconRegion else { return }
switch state {
case .inside: // 領域内
case .outside: // 領域外
case .unknown: // 不明
}
}
レンジング
ビーコンとの距離測定を行う場合はレンジングを実施することもできます、計測可能な距離はこの4パターンです
遠い(far)、近い(near)、とても近い(immediate)、わからない(unknow)
// 先ほどの領域監視を行う関数内で領域内の時だけレンジングを開始する場合は下記のように記載します
func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
guard let beaconRegion = region as? CLBeaconRegion else { return }
if case .inside = state {
manager.startRangingBeacons(satisfying: beaconRegion.beaconIdentityConstraint)
} else {
manager.stopRangingBeacons(satisfying: beaconRegion.beaconIdentityConstraint)
}
}
// レンジングさせた結果によってハンドリングさせる場合は下記のように記載します
func locationManager(_ manager: CLLocationManager, didRange beacons: [CLBeacon], satisfying beaconConstraint: CLBeaconIdentityConstraint) {
// UUIDやMajor値Minor値はCLBeaconから取得できます
switch beacon.first!.proximity {
case .far:
case .immediate:
case .near:
case .unknown:
@unknown default: return
}
}
使い道を考えてみる
上記の実装をしてみて思ったことは
エラーのハンドリングが難しいかなと思いましたが受信するだけであれば思っていたより簡単なのかなと思いました
ただ昨今厳しくなっている位置情報のパーミッションを取得しなければいけないのは大変ですね...
できることが分かったので精一杯使い道を3つ考えてみました、もしいいアイデアあったらコメントしてくださいね(圧)
美術館や博物館の音声ガイダンス
レンジングできるので美術品などの紹介したいものに近づいた時に紹介を開始できるのでいいのかなと思いました
狭い範囲でハンドリングさせたい時はGPSだけで判定させるよりビーコンの方が向いてるかなと思います、ビーコンであれば簡単に位置を動かすことができるのでそれもメリットかなと思います
迷子の防止
公園やキャンプ場で子供を自由に遊ばせたい時に
子供に発信機を持たせることで領域内にいるかどうかの判定ができるので遠くへ行ってしまった場合にアプリ側へ通知させることもできるかなと思います
(まあAirTagの方が使い勝手はいいと思いますが...)
スタジアムやコンサートホールの座席案内
スポーツを観に行った時にチケットの番号がわからなくて遠回りしたことがあるのですが、入場場所や座席のブロックへ近づいたら通知してもらえるのに使えないかなと
あとはビールの売り子さんが近づいた時に通知してもらえれば買い逃したなんて経験が減るのかなと🍺
(とりあえずコロナが終息しないと実現しないと思いますが)
おわり
iBeaconの使い方と雑ですがその使い道について考えてみました、
今後もしいい使い道のアイデアが思いついたらまた何か作ってみようと思います
ありがとうございました
あ、発信機も意外と安く買えるのでぜひ気になる方は試してみてください