rakuishi.com

[iOS SDK] プライバシー設定に対応したカレンダーへのアクセスを行う方法(iOS 5, iOS 6 両対応)

iOS 6 以降では、連絡帳やカレンダーへのアクセスが厳しくなり、アクセスへの許可をユーザーに求める必要があります。こんな感じのアラートが表示されるのを見たことがあるはずです。

この記事では、iOS 5 と iOS 6 における処理の切り分け、iOS 6 以降でのカレンダーへのアクセスの許可の取り扱い、地雷ポイントの回避方法について実際のコードを貼り付けて紹介します。

プライバシー設定に対応したカレンダーへのアクセスを行う方法

EventKit.framework を読み込みます。

#import <EventKit/EventKit.h>

以下に、プライバシー設定に対応したカレンダーへのアクセスの流れを貼り付けます。地雷ポイントが結構ありますが、うまく回避できていると思います。

saveNewEvent: というのは、僕が独自に実装しているメソッドで、ここで予定の追加を行なっています。

EKEventStore *eventStore = [[EKEventStore alloc] init];

// iOS 6 と iOS 5 で処理を分ける
float version = [[[UIDevice currentDevice] systemVersion] floatValue];
// iOS 5: ユーザーに許可を求める必要がない
if (version < 6.0) {
    [self saveNewEvent:eventStore];
    return;
}

// iOS 6: ユーザーに許可を求める必要がある
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
switch (status) {
    case EKAuthorizationStatusNotDetermined: {
        __weak id weakSelf = self;
        // ユーザーにまだアクセスの許可を求めていない場合
        // 「このアプリがカレンダーへのアクセスを求めています」というアラートが表示される
        [eventStore requestAccessToEntityType:EKEntityTypeEvent
                                   completion:^(BOOL granted, NSError *error)
        {
            if (granted) {
                // 「OK」をタップ
                [weakSelf saveNewEvent:eventStore];
            } else {
                // 「許可しない」をタップ
                // UIAlertView の表示を main thread で行う
                dispatch_async(dispatch_get_main_queue(), ^{
                    [[[UIAlertView alloc] initWithTitle:@"確認"
                                                message:@"このアプリのカレンダーへのアクセスを許可するには、プライバシーから設定する必要があります。"
                                               delegate:nil
                                      cancelButtonTitle:@"OK"
                                      otherButtonTitles:nil]
                     show];
                });
            }
        }];
    }
        break;
    case EKAuthorizationStatusAuthorized:
        // ユーザーから許可されている
        [self saveNewEvent:eventStore];
        break;
    case EKAuthorizationStatusRestricted:
        // 「設定」→「一般」→「機能制限」→「カレンダー」→
        // 「変更を許可しない」が選択されている
    case EKAuthorizationStatusDenied:
        // ユーザーから拒否されている
        // ユーザーにアクセスの許可を求めた後、「許可しない」をタップするとこれが呼ばれる
        // 「設定」→「プライバシー」→「カレンダー」からアプリを許可してもらう必要がある
        [[[UIAlertView alloc] initWithTitle:@"確認"
                                    message:@"カレンダーに対する変更を機能制限されているか、プライバシーから許可されていません。"
                                   delegate:nil
                          cancelButtonTitle:@"OK"
                          otherButtonTitles:nil]
         show];

        break;
    default:
        break;
}

ユーザーにまだアクセスの許可を求めていない場合は、先の画像のようにアラートが表示されます。

ここで「許可しない」を選ぶと、アプリを再インストールしない限り、アラートが再び表示されることはありません(たぶん)。そのためユーザーは、設定アプリをたどって、プライバシーからオンにする必要があります。

それをユーザーに告知させるために、許可されていない時に呼ばれる status で、アラートを表示して、許可を促す必要があります。

requestAccessToEntityType:completion: は、block で許可されたか(granted)を返しますが、ここで実行される thread は main thread ではありません(iOS では、main thread 以外で見た目の変更を行うと死にます)。

そこで main thread で行うようにコードを書いてあげる必要があります。

dispatch_async(dispatch_get_main_queue(), ^{
    [[[UIAlertView alloc] initWithTitle:@"確認"
                                message:@"このアプリのカレンダーへのアクセスを許可するには、プライバシーから設定する必要があります。"
                               delegate:nil
                      cancelButtonTitle:@"OK"
                      otherButtonTitles:nil]
     show];
});

ちなみに、プライバシーからアプリをオン・オフするとバックグラウンドで動いていたアプリは強制的に落とされます。プライバシー設定を利用するアプリは、強制的に落とされる可能性も考慮する必要があります。

参考:EKEventStore Class Reference