ALAssetsLibrary を触るときの権限取得を事前に確認する

ちょっと不便

ALAssetsLibrary を触るときにはユーザーに AssetsLibary へのアクセスを許可してもらわないといけません。個人的には先にアクセス許可の伺いを立ててからその結果に応じてアプリとしての振る舞いを切り分けたいと思います。しかし、ALAssetsLibary にはそのようなメソッドは提供されていません。似たようなアクセス許可を得るものには ACAccountStore がありますが、こちらはこのようなメソッドがあり結果に応じて振り分け可能になっています。

- (void)requestAccessToAccountsWithType:(ACAccountType *)accountType options:(NSDictionary *)options completion:(ACAccountStoreRequestAccessCompletionHandler)completion

ACAccountStore Class Reference

カテゴリメソッドを書いてみた

ALAssetsLibrary でも同様の振る舞いができるようなカテゴリメソッドを書いてみました。

Category method to get permission before using ALA ...

使用例

- (void)viewDidLoad
{
    [super viewDidLoad];

    [ALAssetsLibrary csn_requestAccessToAssetsLibraryWithCompletionBlock:^(BOOL granted, NSError *error) {
        dispatch_async(dispatch_get_main_queue(), ^{
            if (granted && error == nil) {
                UIImagePickerController *picker = [[UIImagePickerController alloc] init];
                [self presentViewController:picker animated:YES completion:NULL];
            } else {
                UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Needs Permission" message:nil delegate:nil cancelButtonTitle:@"close" otherButtonTitles:nil];
                [alert show];
            }
        });
    }];
}

エラー処理は適当に書いていますがこれで事前にアクセス権を確認したのちに任意の処理にすすめられます。

実装のポイント

アクセス許可のアラートは実際に ALAssetsLibrary に触らないと出ません。ですので、- (void)enumerateGroupsWithTypes:(ALAssetsGroupType)types usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock で触ってしまいます。アラートを出してもらうのが目的なので得られた ALAssetsGroup には何もせずに即座に *stop に YES を代入して enumeration を止めつつ呼び出し元にコールバックします。

ユーザーが拒否した場合には failureBlock が呼ばれるのでそこで呼び出し元にコールバックします。

ハマったのが、この enumeration は NSArray の enumeration と違い stop に YES を代入しても即座にループは止まらず都合2回 usingBlock が呼び出されます。推測ではありますが、- (void)enumerateGroupsWithTypes:(ALAssetsGroupType)types usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock は「列挙すべき group がなくなった場合には groupnil の状態で usingBlock が呼び出される」というデザインになっています。この API デザインを踏襲するために stop に YES が代入されてももう一度 usingBlock が呼び出されるのだと思います。

まとめ

少しトリッキーな実装になっていますが、リファレンスやヘッダファイルに書かれていることをベースにして実装しているので特に害はないと思います。使いやすいと思うので良かったら使ってみてください。