iOS 8 の Location Notification を使って目的地に着いたら教えてくれるアプリを作る

こんにちは、12/8担当の @koogawa です！よろしくお願いします。



iOS 8から UILocalNotification に __Location Notification__ という機能が新しく追加されました。従来の  UILocalNotification では時刻指定によるユーザへの通知が可能でしたが、iOS 8 からは __位置情報ベースによる通知__ が可能になりました。これが Location Notification です。

この機能により、領域観測（指定した領域へのユーザの出入りを検出する機能）がより簡単に実装できるようになりました。というわけで、今回は「指定した目的地に着いたら教えてくれるアプリ」を作ってみました。


## CoreLocation.framework追加


まずは位置情報を扱うために `CoreLocation.framework` を追加します。

![スクリーンショット 2014-11-16 11.04.01.png](https://qiita-image-store.s3.amazonaws.com/0/8292/3f1530a9-5ce6-94a7-a3b7-9fca70360187.png)


## 位置情報の使用許可

ユーザに位置情報の使用許可をリクエストします。

```objc
- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status
{
    switch (status)
    {
        case kCLAuthorizationStatusDenied:
            [manager requestWhenInUseAuthorization];
            break;
            
        default:
            break;
    }
}
```

Location Notificationでユーザの位置情報を監視するためには、「Appの使用中のみ許可」を設定してもらう必要があります。そのため `requestWhenInUseAuthorization` メソッドを使ってユーザに位置情報の使用許可をリクエストします。

iOS 8での位置情報の扱いについては次の記事を参照してください。

* [iOS 8から位置情報を取得する方法が変わるよ](http://qiita.com/koogawa/items/6ec24ca3201977e9642b)

## 通知の許可設定

通知を出すための設定を行います。iOS 8からはLocal Notificationを使う場合でもユーザの許可が必要になりました。

```objc
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    // Override point for customization after application launch.
    if ([application respondsToSelector:@selector(registerUserNotificationSettings:)])
    {
        UIUserNotificationSettings *settings =
        [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert|UIUserNotificationTypeSound
                                          categories:nil];
        [application registerUserNotificationSettings:settings];
    }
    
    return YES;
}
```

今回はバッジを表示する必要はないので、`UIUserNotificationTypeAlert` と `UIUserNotificationTypeSound` のみ登録しています。

## Location Notificationの実装

`UILocalNotification` を使って Location Notification を実装します。

```objc
- (void)setNotification
{
    // 設定する前に、設定済みの通知をキャンセルする
    [[UIApplication sharedApplication] cancelAllLocalNotifications];

    // 目的地を設定
    CLLocationCoordinate2D target = CLLocationCoordinate2DMake(35.6663268, 139.731032);  // 六本木
    CLLocationDistance radius = 100.0;  // 半径何メートルに入ったら通知するか
    NSString *identifier = @"identifier"; // 通知のID
    
    CLCircularRegion *region = [[CLCircularRegion alloc] initWithCenter:target
                                                                 radius:radius
                                                             identifier:identifier];

    // Notificationセット
    UILocalNotification *localNotification = [[UILocalNotification alloc] init];
    localNotification.alertBody = @"目的地に着きました！";
    localNotification.soundName = UILocalNotificationDefaultSoundName;
    localNotification.alertAction = @"OK";
    localNotification.regionTriggersOnce = YES;
    localNotification.region = region;

    [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
```

目的地の情報は `CLCircularRegion` で設定します。領域の中心点と半径をメートルで指定できます。

この `CLCircularRegion` を `UILocalNotification` の `region` プロパティにセットします。このとき `regionTriggersOnce` プロパティに `YES` をセットすることで、領域に入った際に一度だけ通知を出すよう設定できます（デフォルトはYES）。

最後に`UILocalNotification` を `scheduleLocalNotification` で登録して完了です。

この状態でユーザが目的地に領域に入ると、次の通知が表示されます。

![b68c0ec6-0c0f-d23f-a49c-10c24bef7c42.png](https://qiita-image-store.s3.amazonaws.com/0/8292/d6a55158-e240-2ee3-23f3-f3b0b657b863.png)



## 地図で可視化

この実装だけで領域観測が可能になるのですが、今どこに目的地がセットされているのか？が視覚的にわかったほうが便利なので、地図で表示してみました。（青い点が現在地、赤い円が目的地です）

![e940bebb-f8fa-8aed-d5b4-11cd4082637b.png](https://qiita-image-store.s3.amazonaws.com/0/8292/b85454c8-49bf-2839-38fd-505ce6e8881f.png)


また、せっかくなので地図をタップすることで簡単に目的地をセットできるようにしてみました。地図に円を描く方法は次の記事を参照してください。

* [MKMapViewでタップした位置に円を描く](http://qiita.com/koogawa/items/3eb6ab3615744e8d55b9)

サンプルソースはこちらに置いてあるのでぜひ使ってみてください。

* [https://github.com/koogawa/LocationNotificationSample](https://github.com/koogawa/LocationNotificationSample)

## 注意点

* ユーザに位置情報の使用を許可してもらう必要があります（「このAppの使用中のみ許可」権限が必要）
* Location Notificationを設定する時点で既に目的地領域内にいる場合は何も起きません
* アプリ起動直後などNotificationを設定するタイミングが早過ぎるとうまく動作しないようです（位置情報が取得できた後に設定することが重要）

## 参考にさせて頂いた資料

* [Location Notification を使って領域観測で通知する](http://dev.classmethod.jp/references/ios8-location-notification/) - 私の質問にも親切に答えて頂きました。諏訪さん、ありがとうございました！