Matt Rajca

Cleaner UIAlertView API with Blocks

August 23, 2013

Say you’re working on an iOS view controller that presents UIAlertViews in numerous locations. Implementing button actions soon becomes a nightmare as you have to identify and handle each UIAlertView case-by-case in delegate methods such as -alertView:didDismissWithButtonIndex:.

Fortunately, we can simplify this and reduce boilerplate code with a nifty category on UIAlertView that introduces a Block-based method of presenting alerts:

[UIAlertView presentWithTitle:@"Hello"
					  message:@"Good day!"
					  buttons:@[ @"Cancel", @"Allow" ]
				buttonHandler:^(NSUInteger index) {
					
					if (index == 1) { /* allow */ }
					
				}];

With this API, we can present alerts and handle their buttons’ actions inline.

Under the hood, we assign the UIAlertView’s delegate object to itself and take advantage of the Associated Objects API to associate the button handler block with the alert view (Blocks are first-class Objective-C objects). When a button is tapped and the alert view’s delegate method fires, we invoke the cached button handler block.


UIAlertView+Additions.h

#import <UIKit/UIKit.h>

@interface UIAlertView (Additions)

+ (void)presentWithTitle:(NSString *)title
				 message:(NSString *)message
				 buttons:(NSArray *)buttons
		   buttonHandler:(void(^)(NSUInteger index))handler;

@end

UIAlertView+Additions.m

#import "UIAlertView+Additions.h"

#import <objc/runtime.h>

@implementation UIAlertView (Additions)

static const char *HANDLER_KEY = "com.mattrajca.alertview.handler";

+ (void)presentWithTitle:(NSString *)title
				 message:(NSString *)message
				 buttons:(NSArray *)buttons
		   buttonHandler:(void (^)(NSUInteger))handler {
	
	UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title
													message:message
												   delegate:nil
										  cancelButtonTitle:nil
										  otherButtonTitles:nil];
	
	[alert setDelegate:alert];
	
	[buttons enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
		[alert addButtonWithTitle:obj];
	}];
	
	if (handler)
		objc_setAssociatedObject(alert, HANDLER_KEY, handler, OBJC_ASSOCIATION_COPY_NONATOMIC);
	
	[alert show];
}

- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
	id handler = objc_getAssociatedObject(alertView, HANDLER_KEY);
	
	if (handler)
		((void(^)())handler)(buttonIndex);
}

@end