はじめに
ジオフェンスを利用したiOSアプリを開発する機会があったので、備忘録としてまとめたいと思います。
そもそもジオフェンスとは?
仮想的な境界線(半径100mで設定したならば、半径100mの円)を引くことで、
その境界線の侵入離脱をトリガーに自分が行いたい処理を行うことができます。
例えば、
- 通知
- 他機器との接続
などが行えたりします。
通知であれば、ある領域内に入ったときに、
その領域付近のおすすめのお店を出したり、お店のチラシ情報を出したり使い道はいくらでもありそうです。
しかし、登録できる領域の個数に制限があるので、その部分には配慮が必要です。
動作環境
iOS13.3
xcode11.5
実装手順
1. フレームワークの導入
位置情報やジオフェンスを利用するために、CoreLocationをimportします。
import CoreLocation
2. CLLocationManagerのインスタンス生成
メンバ変数として、CLLocationManagerとモニタリング領域に関するCLCircularRegionのインスタンスを作成します。
let locationManager: CLLocationManager = CLLocationManager()
var moniteringRegion: CLCircularRegion = CLCircularRegion()
3. 位置情報取得のユーザー許可設定
アプリで位置情報を利用するには、ユーザー認証を行わなければなりません。
// 位置情報取得のユーザー認証
self.locationManager.requestAlwaysAuthorization()
これに加えて、info.plistへ
Privacy - Location When In Use Usage Description
を設定する必要があり、表示する文言をValueに書くことができます。
4. デリゲートの設定
デリゲートを設定します。
この設定をしないと、後に記載するデリゲートメソッドが反応しなくなります。
// デリゲートの設定
self.locationManager.delegate = self
5. ジオフェンス領域の設定
ジオフェンス領域を設定したい中心地点の緯度・経度を設定し、半径を入力します。
// 中心位置の設定(緯度・経度)
let moniteringCordinate = CLLocationCoordinate2DMake(100.0, 100.0)
// モニタリング領域を作成
self.moniteringRegion = CLCircularRegion.init(center: moniteringCordinate, radius: 100.0, identifier: "name")
6. モニタリング開始・停止・状態取得
5で作成したモニタリング領域を利用することで、モニタリングの開始・停止・状態取得を行えます。
// モニタリング開始
self.locationManager.startMonitoring(for: self.moniteringRegion)
// モニタリング停止
self.locationManager.stopMonitoring(for: self.moniteringRegion)
// 現在の状態(領域内or領域外)を取得
self.locationManager.requestState(for: self.moniteringRegion)
7. 位置情報・ジオフェンスに関するデリゲートメソッド
以下は、デリゲートメソッドです。
extension ViewController: CLLocationManagerDelegate {
// 位置情報の更新時に呼ばれる
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.first!
print("緯度:\(location.coordinate.latitude)、経度:\(location.coordinate.longitude)")
}
// 位置情報取得認可
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("ユーザー認証未選択")
break
case .restricted:
print("位置情報サービス未許可")
break
case .denied:
print("位置情報取得を拒否、もしくは本体設定で拒否")
break
case .authorizedAlways:
print("アプリは常時、位置情報取得を許可")
break
case .authorizedWhenInUse:
print("アプリ起動時のみ、位置情報取得を許可")
break
}
}
// モニタリング開始成功時に呼ばれる
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("モニタリング開始")
}
// モニタリングに失敗時に呼ばれる
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
print("モニタリング失敗")
}
// ジオフェンス領域侵入時に呼ばれる
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("ジオフェンス侵入")
}
// ジオフェンス領域離脱時に呼ばれる
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("ジオフェンス離脱")
}
// ジオフェンスの情報が取得できないときに呼ばれる
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("モニタリングエラー")
}
// requestStateが呼ばれた時に呼ばれる
public func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if state == .inside {
print("領域内です。")
} else {
print("領域外です。")
}
}
}
まとめ
以上の手順で、実装を行うことでジオフェンスを手軽に利用することができます。
実際にジオフェンスを利用すると、位置情報の精度にもよりますが、
なかなかジオフェンス侵入・離脱が予期した位置で反応してくれない場合が多々あります。
特に、離脱に関しては、
最短でも半径+100m程度の距離で離脱のデリゲートメソッドが呼ばれているように感じました。
また、バックグラウンドでジオフェンスを利用する場合には、
他設定が必要になってくるので、また今後記事にしたいと思います。
コード全体
import UIKit
import CoreLocation
class ViewController: UIViewController {
let locationManager: CLLocationManager = CLLocationManager()
var moniteringRegion: CLCircularRegion = CLCircularRegion()
override func viewDidLoad() {
super.viewDidLoad()
// 位置情報取得のユーザー認証
self.locationManager.requestAlwaysAuthorization()
// デリゲートの設定
self.locationManager.delegate = self
// 中心位置の設定(緯度・経度)
let moniteringCordinate = CLLocationCoordinate2DMake(100.0, 100.0)
// モニタリング領域を作成
self.moniteringRegion = CLCircularRegion.init(center: moniteringCordinate, radius: 100.0, identifier: "name")
}
@IBAction func moniteringStartButton(_ sender: Any) {
// モニタリング開始
self.locationManager.startMonitoring(for: self.moniteringRegion)
}
@IBAction func moniteringStopButton(_ sender: Any) {
// モニタリング停止
self.locationManager.stopMonitoring(for: self.moniteringRegion)
}
@IBAction func requestStateButton(_ sender: Any) {
// 現在の状態(領域内or領域外)を取得
self.locationManager.requestState(for: self.moniteringRegion)
}
}
extension ViewController: CLLocationManagerDelegate {
// 位置情報の更新時に呼ばれる
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations.first!
print("緯度:\(location.coordinate.latitude)、経度:\(location.coordinate.longitude)")
}
// 位置情報取得認可
func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
switch status {
case .notDetermined:
print("ユーザー認証未選択")
break
case .restricted:
print("位置情報サービス未許可")
break
case .denied:
print("位置情報取得を拒否、もしくは本体設定で拒否")
break
case .authorizedAlways:
print("アプリは常時、位置情報取得を許可")
break
case .authorizedWhenInUse:
print("アプリ起動時のみ、位置情報取得を許可")
break
}
}
// モニタリング開始成功時に呼ばれる
func locationManager(_ manager: CLLocationManager, didStartMonitoringFor region: CLRegion) {
print("モニタリング開始")
}
// モニタリングに失敗時に呼ばれる
func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
print("モニタリング失敗")
}
// ジオフェンス領域侵入時に呼ばれる
func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
print("ジオフェンス侵入")
}
// ジオフェンス領域離脱時に呼ばれる
func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
print("ジオフェンス離脱")
}
// ジオフェンスの情報が取得できないときに呼ばれる
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("モニタリングエラー")
}
// requestStateが呼ばれた時に呼ばれる
public func locationManager(_ manager: CLLocationManager, didDetermineState state: CLRegionState, for region: CLRegion) {
if state == .inside {
print("領域内です。")
} else {
print("領域外です。")
}
}
}