Create iOS 8 and iOS 7 alerts easily with GUNAlert
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:
- Create a
UIAlertView
. - Pass the titles of buttons.
- Handle all actions in a delegate method of your own
UIViewController
. - 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:
- Create a
UIAlertController
. - Create and add an action for each button specifying its title and behavior in a block.
- 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:
- Create a
GUNAlert
- Pass the titles for each button.
- Pass the actions for buttons as blocks.
- 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!