【Domain=kCLErrorDomain Code=5】3つ目の原因
お晩です。
今日は仕事中に1時間ほど無駄にしたので、そのことを記録しようと思います。
てかこれ、Qiitaにも投稿しとこう。
Domain=kCLErrorDomain Code=5
iBeaconのビーコン領域監視アプリを作成していると、時折上記のエラーに出くわすことがあるかと思います。
これについてネットで調べると、2つ、原因が見つかります。
- regionがnil
- 監視しているregionが20個超
また、原因が何とは特定していなくても、iOSデバイスを再起動すれば解消される、と説明しているweb記事もあります。
ビーコンを検知しない
すでにビーコンの検知まで確認した試作アプリからビーコン検知機能をぶっこ抜いて新アプリにぶち込んだのに、ビーコンを検知しません。
何故でしょう?
原因究明のため、func locationManager(_: didStartMonitoringForRegion)
でrequestStateForRegion
を呼んでみました。
すると、即座にfunc locationManager(_: monitoringDidFailForRegion)
が呼ばれるではありませんか。
教えてgoogle先生
func locationManager(_: monitoringDidFailForRegion)
で検出されたエラーはDomain=kCLErrorDomain Code=5
でした。
原因究明のため、println
を仕込みます。
しかし、登録済みの監視領域を取得してみても、nil
ではないし、20個を超えてもいません。
iOSデバイスの再起動を行ってみても、現象は発生し続けました。
端末を変えても結果は同じ。
iOSのバージョンに関係あるかもと思い、7.1.2
と8.3
で試してみましたが、これも結果は変わりません。
原因判明
もうこうなったら仕方がないので、設定リセットしてやるぜ、と設定アプリを起動して、ようやく気が付きました。
BluetoothがONになっていません。
iBeaconはBluetooth4.0以降に統合されたBluetooth Low Energyのサブセットです。
なので、BTがONじゃないと機能が使えません。
ていうかですね、BTのONを促すアラートダイアログが悪いんですよ。
領域監視開始する際かLocationManagerのインスタンス作る時か忘れましたけど、位置情報サービスがONになってなかったらONにするダイアログ表示されますし、アプリでの位置情報の使用許諾もダイアログ表示されるんですよ。
で、BTがOFFの場合も、ダイアログ表示されるんです。
ところがね、BTのダイアログ、設定
とOK
の二択なんですよ。
そりゃあ、よく読まなけりゃOK
押しますがな。
アプリの動作確認する際に、ダイアログで位置情報もBTもONにしたと思い込んでいたけれど、実際にはBTはONじゃありませんでした、というオチです。
結論
BTがONじゃなくて位置情報だけ許可していた場合、領域監視を開始すると、Domain=kCLErrorDomain Code=5
が出ます。
【原因】
【対処方法】
【省略コード】
// MARK:CLLocationManagerDelegate extension ViewController { // CLLocationManagerインスタンスを生成すると呼ばれる func locationManager(manager: CLLocationManager!, didChangeAuthorizationStatus status: CLAuthorizationStatus) { // 領域監視開始 manager.startMonitoringForRegion(myRegion) // デバッグ用 for region in manager.monitoredRegions { println("monitoring region count \(manager.monitoredRegions.count)") println(region) } } // startMonitoringForRegionに対するデリゲート func locationManager(manager: CLLocationManager!, didStartMonitoringForRegion region: CLRegion!) { // 監視状況のチェック // エラー発生時、monitoringDidFailForRegionに拾わせるため manager.requestStateForRegion(myRegion) } // requestStateForRegionに対するデリゲート // requestStateForRegionを呼んでなくても領域を検知した場合は呼ばれる func locationManager(manager: CLLocationManager!, didDetermineState state: CLRegionState, forRegion region: CLRegion!) { println("didDetermineState: \(state.name)") } // startMonitoringForRegion後のエラーを拾う func locationManager(manager: CLLocationManager!, monitoringDidFailForRegion region: CLRegion!, withError error: NSError!) { println("monitoringDidFailForRegion") // withError: Error Domain=kCLErrorDomain Code=5 // 1. region が nil // 2. 監視している region が 20 個を超えた // 3. Bluetooth を ON にしてない } }
extension CLRegionState { var name : NSString { get{ var enumName = "CLRegionState" var valueName = "" switch self { case .Unknown: valueName = enumName + "Unknown" case .Inside: valueName = enumName + "Inside" case .Outside: valueName = enumName + "Outside" } return valueName } } }
「CoreBluetoothのEnum値」
ログ出し用のname
プロパティ、重宝させていただいております。
[Swift]switch文のcaseやdefault節で何も処理しない場合はbreakが必要
自分もそう思っていました。
でも上記のname
拡張で、変だなって。
なるほど、switchは取り得る値全てをカバーしていればdefaultは必要ないんですね。
以上