iBeaconのビーコン領域監視アプリを作成していると、時折下記のエラーに出くわすことがあるかと思います。
Domain=kCLErrorDomain Code=5
これについてネットで調べると、2つ、原因が見つかります。
- regionがnil
- 監視しているregionが20個超
また、原因が何とは特定していなくても、iOSデバイスを再起動すれば解消される、と説明しているweb記事もあります。
上記2件の原因のいずれにも当てはまらず、iOSデバイスの再起動でもDomain=kCLErrorDomain Code=5
が解消されなかったので、その解決方法を記そうと思います。
現象
- ビーコンを検知しない
-
func locationManager(_: monitoringDidFailForRegion)
でDomain=kCLErrorDomain Code=5
原因
- BluetoothがOFF
領域監視開始(CLLocationManagerインスタンス生成?)時に出るダイアログで位置情報サービスはON
にしてあったし、アプリの位置情報の許諾も出していたんです。
が、BTのダイアログでOK
を選んでしまったため(設定
とOK
の二択)、BTがOFFのまま……。
それに気付かず延々1時間、ネット徘徊とログ取りを繰り返してしまいました。
偶然でも原因に気が付けて良かったです。
上の話は仕事中にあった出来事です。
下記コードは家に帰ってから記憶を頼りに適当に打ったものですので、動作確認等はしていません。
コンパイルエラーは出ていませんが(Xcode6.3)。
ターゲットはiOS7.1を想定しているのでAuthorizationの処理が甘いです。
あ、UUIDだけは変えました。
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
var myLocation: CLLocationManager!
var myRegion: CLBeaconRegion!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
myLocation = CLLocationManager()
myLocation.delegate = self
myRegion = CLBeaconRegion(proximityUUID: NSUUID(UUIDString: "uuidgenで求めてください"), identifier: "hoge")
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
// 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は必要ないんですね。
以上です。