Blocksが浸透して、システムの実装でもコールバックを受け取って実行するタイプのものが増えてきました。
返ってきた値を見て、必要に応じてUIAlertViewやUIActionViewを出したい、というときなどにハマったので、覚書。
たとえば、Twitterアカウントを利用する際、使用できるアカウントがあるかのチェックをしたい際にはrequestAccessToAccountsWithType:options:completion:
メソッドを使用しますが、
このcompletionで指定したブロックが実行されるのはメインスレッドではないため、ここでUIを操作しようとするとたいへんなことになります。
.m
ACAccountStore* store = [[ACAccountStore alloc] init];
ACAccountType* type = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[store requestAccessToAccountsWithType:type options:nil completion:^(BOOL granted, NSError *error) {
// bad pattern
// grantedに応じてアラートを出したい
if (!granted) {
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"エラー" message:@"" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
}
}];
そこで、dispatch_get_main_queue()
を使用して、メインスレッドで処理を実行させます。
.
[store requestAccessToAccountsWithType:type options:nil completion:^(BOOL granted, NSError *error) {
// it works!
if (!granted) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:@"エラー" message:@"" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
[alert show];
});
}
}];
参考
EKEventStore#requestAccessToEntityTypeのcallbackがmain thread以外の場所で実行される件とその対処