Create iOS 8 and iOS 7 alerts easily with GUNAlert

Michal Taszycki •

With the release of iOS 8, Apple gave us new and better way of handling modal alerts. However, the new API doesn’t work on earlier versions of iOS. For those of us who need to support them, this usually means a lot of code duplication and polluting the code with conditions while trying to support both UIAlertView and UIViewController.

To get rid of those problems, we’ve developed a small library called GUNAlert. It makes displaying alerts simple on all iOS versions. Here’s how it can help you in your project.

What seems to be the problem, officer?

If you’ve been programming iOS apps for some time you’ve probably got used to the old way of displaying alerts:

  1. Create a UIAlertView.
  2. Pass the titles of buttons.
  3. Handle all actions in a delegate method of your own UIViewController.
  4. Tell the alert’s view to show itself.

The way we display alerts in iOS has dramatically changed in iOS 8. Now we need to:

  1. Create a UIAlertController.
  2. Create and add an action for each button specifying its title and behavior in a block.
  3. Present alert’s controller in a standard way.

Now, if we need to support both, then we have to use either UIAlertView and UIAlertController conditionally. On top of that we have to add a delegate method to our controller to handle actions in the old way, as well as handling the same actions as blocks in the iOS 8 version.

If we are not careful we’ll introduce duplication and increase code complexity significantly. Don’t forget that we’ll do that in every controller that is supposed to display an alert.

The solution

With GUNAlert, you won’t need to worry about API differences, conditions and code duplication. You can use it in any of your controllers like that:

  1. Create a GUNAlert
  2. Pass the titles for each button.
  3. Pass the actions for buttons as blocks.
  4. That’s it.

GUNAlert will decide if it needs to use UIAlertView or UIAlertController to do the job. It will also handle the delegate awkwardness for you. Your controllers will be cleaner and you’ll have one less thing to worry about.

If that sounds like a useful thing to you, feel free to grab the source code from GitHub or install it via CocoaPods.

If you don’t feel convinced yet, take a look at the code examples below.

Getting real

Let’s imagine the following situation: The user is buying something inside our app. She tapped on a button to make a payment. We want to make sure she’s aware that her credit card will be charged. We decided that showing a simple alert with “Yes” and “No” buttons is enough to satisfy the requirement.

Using the UIAlertView in iOS 7

First we instantiate UIAlertView with title and message to warn the user, delegate to react on button presses, and finally titles of our buttons.

In a delegate method alertView:clickedButtonAtIndex: we need to confirm the payment if the user has pressed the “Ok” button.

Finally, we can send a show message to display the alert.

@interface PaymentViewController() <UIAlertViewDelegate>
@end
@implementation PaymentViewController
- (IBAction)makePaymentButtonPressed:(id)sender {
    UIAlertView alertView = 
        [[UIAlertView alloc] initWithTitle:@"Payment confirmation!"
                                   message:@"Do you want to confirm the payment?"
                                  delegate:self
                         cancelButtonTitle:@"No"
                         otherButtonTitles:@"Yes", nil];

    [alertView show];
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) {
        [self confirmPayment];
    }      
}
@end

Using the UIAlertController in iOS 8

Here we need to instantiate UIAlertController with title and message and UIAlertControllerStyleAlert as preferred style.

Then we add “Yes” and “No” actions one after the other by specifying its title and pass a block to the handler.

Finally, we can show the alert by sending it in a message presentViewController:animated:completion: to our PaymentViewController

@implementation PaymentViewController
- (IBAction)makePaymentButtonPressed:(id)sender {
    UIAlertController* alertController = 
        [UIAlertController alertControllerWithTitle:@"Payment confirmation!"
                                            message:@"Do you want to confirm the payment?"
                                     preferredStyle:UIAlertControllerStyleAlert];
   
    [alertController addAction: 
        [UIAlertAction actionWithTitle:@"No" 
                                 style:UIAlertActionStyleDefault
                               handler:^(UIAlertAction * action) {}]];
    [alertController addAction: 
        [UIAlertAction actionWithTitle:@"Yes" 
                                 style:UIAlertActionStyleDefault
                               handler:^(UIAlertAction * action) {
                                   [self confirmPayment];
                               }]];

    [self presentViewController:alertController animated:YES completion:nil];
}
@end

Using both in the same application

It can be solved by detecting if UIAlertController class exists and choosing the appropriate implementation.

@interface PaymentViewController() <UIAlertViewDelegate>
@end
@implementation PaymentViewController
- (IBAction)makePaymentButtonPressed:(id)sender {
    if (NSClassFromString(@"UIAlertController")) {
        UIAlertController* alertController = 
            [UIAlertController alertControllerWithTitle:@"Payment confirmation!"
                                                message:@"Do you want to confirm the payment?"
                                         preferredStyle:UIAlertControllerStyleAlert];
       
        [alertController addAction: 
            [UIAlertAction actionWithTitle:@"No" 
                                     style:UIAlertActionStyleDefault
                                   handler:^(UIAlertAction * action) {}]];
        [alertController addAction: 
            [UIAlertAction actionWithTitle:@"Yes" 
                                     style:UIAlertActionStyleDefault
                                   handler:^(UIAlertAction * action) {
                                       [self confirmPayment];
                                   }]];

        [self presentViewController:alertController animated:YES completion:nil];
    } else {
        UIAlertView alertView = 
            [[UIAlertView alloc] initWithTitle:@"Payment confirmation!"
                                       message:@"Do you want to confirm the payment?"
                                      delegate:self
                             cancelButtonTitle:@"No"
                             otherButtonTitles:@"Yes", nil];

        [alertView show];
    }
}

- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
    if (buttonIndex == 1) {
        [self confirmPayment];
    }      
}
@end

Now imagine that in every controller you’ll need similar code to simply display an alert. Sounds pretty horrible doesn’t it? Some small refactorings could improve the code above a bit, but your controllers would still need to decide which alert implementation to use. That doesn’t sound like a controller’s responsibility.

Using the GUNAlert

@interface PaymentViewController()
@property (strong, nonatomic) GUNAlert* alert;
@end

@implementation PaymentViewController
- (void)viewDidLoad
{   
    [super viewDidLoad];
    self.alert = [[GUNAlert alloc] initWithViewController: self];
}
- (IBAction)makePaymentButtonPressed:(id)sender {
    [self.alert cancelOkAlertWithTitle:@"Payment confirmation!"
                               message:@"Do you want to confirm the payment?"
                           cancelTitle:@"No"
                         cancelHandler:^() {}
                               okTitle:@"Yes"
                             okHandler:^() {[self confirmPayment];}];
}
@end

Note that PaymentViewController doesn’t need to implement UIAlertViewDelegate nor know about which type of alert API is being used. All we need to do is to instantiate GUNAlert class, keep it as strong reference and pass it required arguments.

And the resulting code seems to be… slightly simpler.

Make sure to check our GitHub repository to see other examples of how to use GUNAlert or grab a source code directly from there. You can also install it via CocoaPods.

Enjoy!


Sign up to receive these posts via email


If you enjoyed this post, please Subscribe via RSS and follow us on Twitter.