Matt Rajca

blog projects github twitter email

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