Takahiro Octopress Blog

-1から始める情弱プログラミング

iOS8.1でCLVisitとCMPedometerを試してみた!

iOS8から追加された新しい機能 CLVisitとCMPedometer

さて、久々の更新です。
今更ですが、CLVisitとCMPedometerについて調べてみました。
CLVisitはiOS8以降、CMPedometerはiOS8以降かつM8 モーションコプロセッサを搭載したiPhone6 or iPhone6 Plusでのみ利用可能です。
iOS8はともかく、まだまだ、iPhone6 or iPhone6 Plusは普及しているとは言い難いことでしょう。
実質的に多いのはiPhone5かもしれません…。
しかし、ここはエンジニアとして、1〜2年後を見て、アプリを開発すべきでしょう。
ということで早速、見て行きましょう。

CLVisitで滞在時間を検知

まずは、CLVisitから見ていきます。
CLVisitは特定の場所の滞在時間を取得できるクラスです。
最近、Swiftでソースを書いてあるサイトが多いので、あえてObjective-Cで書いてみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
// ViewController.m

#import "ViewController.h"
#import <CoreLocation/CoreLocation.h>

@interface ViewController () <CLLocationManagerDelegate>

@property (strong, nonatomic) CLLocationManager *lm;
@property (weak, nonatomic) IBOutlet UILabel *label1;
@property (weak, nonatomic) IBOutlet UITextView *textView1;

@end

@implementation ViewController

- (void)viewDidLoad {

  [super viewDidLoad];

  self.lm = [[CLLocationManager alloc] init];
  self.lm.delegate = self;
  
  // 位置情報の取得許可を求めるメソッド
  [self.lm requestAlwaysAuthorization];
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
}

- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status {
  
  if (status == kCLAuthorizationStatusNotDetermined) {
      // ユーザが位置情報の使用を許可していない
  } else if(status == kCLAuthorizationStatusAuthorizedAlways) {
      // ユーザが位置情報の使用を常に許可している場合
      // 滞在時間の取得開始
      [self.lm startMonitoringVisits];
  } else if(status == kCLAuthorizationStatusAuthorizedWhenInUse) {
      // ユーザが位置情報の使用を使用中のみ許可している場合
  }
}

- (void)locationManager:(CLLocationManager *)manager didVisit:(CLVisit *)visit {
  
  NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
  [outputFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];

  NSString *arrivalDate = [outputFormatter stringFromDate:visit.arrivalDate];
  NSString *departureDate = [outputFormatter stringFromDate:visit.departureDate];
  NSString *latitude = [NSString stringWithFormat:@"%f",visit.coordinate.latitude];
  NSString *longitude = [NSString stringWithFormat:@"%f",visit.coordinate.longitude];

  NSString *message = [[NSString alloc] initWithFormat:@"緯度: %@\n経度: %@\n到着時間: %@\n出発時間: %@", latitude, longitude, arrivalDate, departureDate];
  // ローカルプッシュ通知
  [self sendLocalNotificationForMessage:message soundFlag:NO];
  dispatch_async(dispatch_get_main_queue(), ^{
      self.textView1.text = [[NSString alloc] initWithFormat: message];
  });
}

/**
 ローカルプッシュ通知処理
 @param message メッセージ
 @param sound 通知音の設定
 */
- (void)sendLocalNotificationForMessage:(NSString *)message soundFlag:(BOOL)soundFlag {
  
  UILocalNotification *localNotification = [UILocalNotification new];
  localNotification.alertBody = message;
  localNotification.fireDate = [NSDate date];
  if(soundFlag) {
      localNotification.soundName = UILocalNotificationDefaultSoundName;
  }
  [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}

@end

このアプリをインストールしたiPhone6を持って、ぶらぶらしてみたところ…

緯度: 35.☓☓☓☓☓☓
経度: 139.☓☓☓☓☓
到着時間: 2014-11-30 16:00:09
出発時間: 2014-11-30 16:22:20

といった結果が得られました。
その場を離れてから5〜6分後にローカルプッシュ通知が来ました。
※トリガーが時間なのか位置情報なのかは未検証

因みに、 アプリを1度起動すれば、その後、Backgroundで起動していなくても、検知してくれます。
これは滞在時間の取得以外にも何かに使えそうですね。
また、移動中は検知しなかったので、滞在と見なす時間が決まっているのでしょう。

CMPedometerで歩行情報を取得

次に、CMPedometerを見ていきます。
CMPedometerはiOS7で追加されていたCMStepCounterのパワーアップバージョンです。
これもObjective-Cでソースを書いてみます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
// ViewController.m

#import "ViewController.h"
#import <CoreMotion/CoreMotion.h>

@interface ViewController ()

@property (strong, nonatomic) CMPedometer *pmp;
@property (weak, nonatomic) IBOutlet UILabel *label1;
@property (weak, nonatomic) IBOutlet UITextView *textView1;

@end

@implementation ViewController

- (void)viewDidLoad {
  [super viewDidLoad];

  self.pmp = [[CMPedometer alloc] init];
  BOOL check = [self confirmCMPedometer];
  if(check) {
      // モーション計測の開始
      [self startPedometer];
  }
}

- (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
}

/**
 CMPedometerが利用可能か判断する処理
 return 判断結果(YES: 利用可能, NO: 利用不可能)
 */
- (BOOL)confirmCMPedometer {
  if([CMPedometer isStepCountingAvailable] && [CMPedometer isDistanceAvailable] && [CMPedometer isFloorCountingAvailable]) {
      return YES;
  } else {
      return NO;
  }
}

/**
 歩行動作を計測する処理
 */
- (void)startPedometer {
  
  [self.pmp startPedometerUpdatesFromDate:[NSDate date]
                              withHandler:^(CMPedometerData *pedometerData, NSError *error) {
                                  dispatch_async(dispatch_get_main_queue(), ^{
                                      // step数の取得
                                      NSNumber *step = pedometerData.numberOfSteps;
                                      // 距離
                                      NSNumber *distance = pedometerData.distance;

                                      // 日時取得のためにFormatを設定
                                      NSDateFormatter *outputFormatter = [[NSDateFormatter alloc] init];
                                      [outputFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
                                      // 開始日時
                                      NSString* startDate = [outputFormatter stringFromDate:pedometerData.startDate];
                                      //終了日時
                                      NSString *endDate = [outputFormatter stringFromDate:pedometerData.endDate];

                                      // 階段の昇降
                                      NSNumber *floorsAscended = pedometerData.floorsAscended;
                                      NSNumber *floorsDescended = pedometerData.floorsDescended;

                                      NSString *str = [[NSString alloc] initWithFormat:@"Step Count Result\n\n歩数: %@\n距離: %@[m]\n開始時間: %@\n終了時間: %@\n上った回数: %@\n下りた回数: %@", step, distance, startDate, endDate, floorsAscended, floorsDescended];
                                      self.textView1.text = str;

                                      // ローカルプッシュ通知
                                      [self sendLocalNotificationForMessage:str soundFlag:NO];
                                  });

  }];
}

/**
 ローカルプッシュ通知処理
 @param message メッセージ
 @param sound 通知音の設定
 */
- (void)sendLocalNotificationForMessage:(NSString *)message soundFlag:(BOOL)soundFlag {
  
  UILocalNotification *localNotification = [UILocalNotification new];
  localNotification.alertBody = message;
  localNotification.fireDate = [NSDate date];
  if(soundFlag) {
      localNotification.soundName = UILocalNotificationDefaultSoundName;
  }
  [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}

@end

このアプリをインストールしたiPhone6を持って、ぶらぶらしてみたところ…

歩数: 1128
距離: 774.0900000002002 [m]
開始時間: 2014-11-30 15:57:22
終了時間: 2014-11-30 16:29:32
上がった回数: 2
下がった回数: 0

といった結果が得られました。
これはローカルプッシュ通知が届くタイミングが任意なのか、法則性はなさそうでした。
※トリガーが時間なのか、歩数なのか、距離なのか未検証

因みに、 アプリを1度起動すれば、その後、Backgroundで起動していなくても、検知してくれます。
これは歩数計測以外にも何かに使えそうですね。

ということで本日はここまで。

Comments