While the immediate idea might be to make your asynchronous request synchronous, that's rarely a good idea, and do to so in the middle of a segue, such as this case, is likely to be problematic. It's almost never a good idea to try to make an asynchronous method synchronous.
And, as smyrgl points out, the idea of "can't I just return a value from the block" is intuitively attractive, but while you can define your own blocks that return values (as Duncan points out), you cannot change the behavior of requestAccessToEntityType
such that it returns a value in that manner. It's inherent in its asynchronous pattern that you have to act upon the grant state within the block, not after the block.
So, instead, I would suggest a refactoring of this code. I would suggest that you remove the segue (which is likely being initiated from a control in the "from" scene) and not try to rely upon shouldPerformSegueWithIdentifier
to determine whether the segue can be performed as a result of a call to this asynchronous method.
Instead, I would completely remove that existing segue and replace it with an IBAction
method that programmatically initiates a segue based upon the result of requestAccessToEntityType
. Thus:
Remove the segue from the button (or whatever) to the next scene and remove this
shouldPerformSegueWithIdentifier
method;Create a new segue between the view controllers themselves (not from any control in the "from" scene, but rather between the view controllers themselves) and give this segue a storyboard ID (for example, see the screen snapshots here or here);
Connect the control to an
IBAction
method, in which you perform thisrequestAccessToEntityType
, and if granted, you will then perform this segue, otherwise present the appropriate warning.Thus, it might look something like:
- (IBAction)didTouchUpInsideButton:(id)sender { eventStore = [[EKEventStore alloc] init]; [eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted, NSError *error) { // by the way, this completion block is not run on the main queue, so // given that you want to do UI interaction, make sure to dispatch it // to the main queue dispatch_async(dispatch_get_main_queue(), ^{ if (granted) { [self performSegueWithIdentifier:kSegueToNextScreenIdentifier sender:self]; } else { UIAlertView *remindersNotEnabledAlert; remindersNotEnabledAlert = [[UIAlertView alloc] initWithTitle:@"Reminders Not Enabled" message:@"In order for the watering reminder feature to function, please allow reminders for the app under the Privacy menu in the Settings app." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil]; [remindersNotEnabledAlert show]; } }); }]; }