GeoFenceとCoreMotionを組み合わせたバックグラウンド常駐監視の一案
仕事へのパッションが抑えられません。
ジオフェンシングとモーションセンサーを使って
バックグラウンドやロック状態でも何かをしたいときの一案。
ジオフェンシングって知ってますか?
ではEnjoy ジオフェンシングとモーションセンサー!
やりたいこと
ある領域内にいるうちは定期的に何かしたい。
何かのトリガーでバックグラウンドでアプリを起動させても動くのって10秒ほど。
がんばって延長させても最長10分弱。
そうじゃなくて定期的に何かしたい。
やることの要約
- 指定領域内に入ったらモーションセンサー開始
- モーションセンサーで歩くたびに何かする
- 指定領域内は継続
- バックグラウンドでもロック時も動作開始、継続
- 指定領域から出たらモーションセンサー停止
前提
- Xcode6.3.1
- 検証機はiPhone6plus iOS8.3。
- AppStoreに申請したことはない。
- SwiftじゃないよObjective-Cです。
「・・・」のところは本筋から外れるので間引いています。
気になる方はサンプルコードからどうぞ。
サンプルコード
実装のまえに
プロテイ、、プロジェクトの設定をします。
位置情報「常に使用」を有効にします。
* Info.plistにNSLocationAlwaysUsageDescription
を追加
バックグラウンドで動かす設定をします。
* TARGETS
- Capabilities
- Background Modes
をON
* Location updates
にチェックをつける
やっと実装
まずはインポート。
余談ですがframeworkに追加しなくてよくなったのね。
framework追加なしでエラーになりません。
#import <CoreLocation/CoreLocation.h> #import <CoreMotion/CoreMotion.h>
CLLocationManager初期化と監視領域の指定。
CLLocationCoordinate2DMake
で指定した緯度軽度を中心に、
CLLocationDistance
で指定した領域を監視します。
//CLLocationManager初期化 self.locationManager = [[CLLocationManager alloc] init]; ・・・ self.locationManager.delegate = self; //Region初期化 CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(37.324540, -122.041241);//アメリカ(クパチーノ) CLLocationDistance radiusOnMeter = 100.0;//範囲指定 self.geoRegion = [[CLCircularRegion alloc] initWithCenter:coordinate radius:radiusOnMeter identifier:@"jp.roadto.ROADTO-Geo"]; //位置情報の許可状況に合わせて動作開始 if ([self.locationManager respondsToSelector:@selector(requestWhenInUseAuthorization)]) { // iOS8 switch ([CLLocationManager authorizationStatus]) { case kCLAuthorizationStatusNotDetermined: NSLog(@"位置情報の許可が未設定なのでユーザに確認する"); [self.locationManager requestAlwaysAuthorization]; break; case kCLAuthorizationStatusAuthorizedAlways: NSLog(@"「常に許可」されている"); break; ・・・ } } else { // iOS7以下 [self.locationManager startUpdatingLocation]; }
続きましてCLLocationManagerDelegateメソッドたち
requestStateForRegion
で監視が始まったときとか
requestAlwaysAuthorization
の位置情報使用確認後とかに通知されます。
領域内に入ったときにモーションセンサーをONにし、
領域外になったときにモーションセンサーをOFFにします。
説明はコメントにて。
#pragma mark - CLLocationManagerDelegate // CLLocationManager初期化時または // アプリの位置情報の許可状態ステータスが変更されたときに通知される -(void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status { switch (status) { case kCLAuthorizationStatusNotDetermined: break; case kCLAuthorizationStatusAuthorizedAlways: NSLog(@"「常に許可」"); // 領域監視スタート [self.locationManager startMonitoringForRegion:self.geoRegion]; break; ・・・ } } //領域監視がスタートしたときに通知 - (void)locationManager:(CLLocationManager *)manager didStartMonitoringForRegion:(CLRegion *)region { //現在の領域状態を確認。 //「- locationManager:didDetermineState:forRegion:」が呼ばれます。 [self.locationManager requestStateForRegion:region]; } //領域内に入ったときに通知 - (void)locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region { NSLog(@"領域内になったよ"); //モーションセンサー 開始 内容は次の章で説明。 [self startMotionActivity]; } //領域の状態について通知。状態が変わるたびに呼び出される。 - (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region { switch (state) { case CLRegionStateInside: NSLog(@"領域内"); //モーションセンサー 開始 内容は次の章で説明。 [self startMotionActivity]; break; ・・・ } } //領域から出たことを通知 - (void)locationManager:(CLLocationManager *)manager didExitRegion:(CLRegion *)region { NSLog(@"領域外になったよ"); //モーションセンサー 停止 [self stopMotionActivity]; }
あとはモーションセンサー
CMMotionActivityManager
を使ってモーションを取ります。
今回は歩いているか否かを使っていますが、他にもいろんな状態をとれます。
「//do something」でしたい何かの処理を書きます。
#pragma mark - モーションセンサー //モーションセンサー 初期化 - (CMMotionActivityManager *)getActivityManager { if (!self.motionActivityManager) { self.motionActivityManager = [[CMMotionActivityManager alloc] init]; } return self.motionActivityManager; } //モーションセンサー 開始 - (void)startMotionActivity { NSLog(@"モーションセンサー 開始"); //iOS7,8ともに必要 [self.locationManager startUpdatingLocation]; //←キモ。iOS7,8ともに必要です。 if([CMMotionActivityManager isActivityAvailable]){ NSOperationQueue *queue = [NSOperationQueue mainQueue]; CMMotionActivityManager *motionActivityManager = [self getActivityManager]; [motionActivityManager startActivityUpdatesToQueue:queue withHandler:^(CMMotionActivity *motionActivity){ //do something NSString *log = [NSString stringWithFormat:@"motion %@", motionActivity.walking ? @"そうだね。プロテインだね。" : @"んーんーんー。"]; NSLog(log); ・・・ }]; } } //モーションセンサー 停止 - (void)stopMotionActivity { NSLog(@"モーションセンサー 停止"); CMMotionActivityManager *motionActivityManagerUpdate = [self getActivityManager]; [motionActivityManagerUpdate stopActivityUpdates]; }
テストに出ます
「←キモ」のところ。
startMotionActivity内で「-locationManager startUpdatingLocation」を呼んでいます。
これ自体は位置情報の更新監視をスタートさせるものですが
なぜかこれを書くとモーションセンサーもバックグラウンドで動いてくれます。
バックグラウンドでもロック状態でも起動するし、
フォアグラウンドで起動して、バックグラウンドやロック状態になっても動き続けます。
尚、「M7」モーションセンサーはiPhone5sから搭載なので、
iPhone5以前は使えません。
結果
サンプルコードを起動させます。
サンプルコードでは画面に出力させています。
歩いているかどうかを判定しています。
歩いてると出てますね。「そうだね。プロテインだね。」
バックグラウンドでも動いていますね。「そうだね。プロテインだね。」
以上です。
エヴィバディパッション!
そうだね。プロテインだね。
— ひとのみち (@hitonomichi) 2015, 5月 29