Creating your first “Today” widget on iOS 8.0

Widgets? On iOS?!

At last us developers have been empowered by iOS to make our own widgets. This means that there are now additional available points of contact where we can interact with our users and allow them to use our app. Today we’re going to talk about one of these “extension points” – the “Today” extension.

The “Today” extensions are the ones available through the drop-down notification center drawer (that’s the one the comes down from the top). You’ll notice that in iOS 8.0, there’s a new “Edit” option there – it will open up a list of available extensions to add/remove to this “Today” view:

Screen Shot 2014-10-11 at 2.24.01 PM

I’m going to show you how to add a widget to your application that will be available in this list – this means that it will be available to your users once it’s added by them to their “Today” view.

Some things you should know about “Today” widgets

  • They are compiled separately as a standalone binary. This means that they can’t interact directly with your app or access the same memory.
  • They are essentially view controllers, meaning you can do anything you would do in a view controller within your widget. Keep in mind that the controls you put in there need to be accessed by the user in the notification center context so for instance UITextFields aren’t suitable for this controller (though they are technically addable).
  • They are required to be efficient. The basic guidelines for efficient data fetch, caching and proper multithreading is crucial here. Your app (and widget) will be rejected if your “Today” widget is unresponsive and interferes with the overall awesomeness of the notification center.

Ladies and gentlemen, this is your widget speaking

Time to create our widget.

What we’re going to do today is create a flight info widget that acts as a companion widget for a flight check-in app. It offers up-to-date information about your flight in terms of boarding, takeoff and landing times as well as an inline option to transfer to your application for the actual online checkin process (which we won’t actually implement due to laziness).

First things first, let’s actually create a new widget. In order to do this, go to your project details in XCode 6 by selecting the project file in the navigator and click the “+” on the bottom left in order to add a new target. Next, select “Application Extension” under iOS. You’ll see the following menu drop down:

 

Select “Today Extension” and click “Next”. Now you’ll notice a dialog that looks very similar to the one you get when opening a new project. Remember when I said it’s a separate binary?

OK, now let’s get down to business. You’ll notice a new folder in your project which will contain a designated storyboard and a single view controller class. The storyboard is there to help you define your widget layout as you would any controller. In our demo application, we’re going to add some static and dynamic labels, some image views with preset icons, 2 buttons, a title label and an activity indicator, so it would look something like this:

 

Now let’s write some code.

Updating your data

The underlying controller is where the magic happens. First thing you will notice is that your controller is pre populated with a widgetPerformUpdateWithCompletionHandler: method. This method requires you to perform your updates and notify the OS when you’re done with the following possible outcomes:

  • NCUpdateResultNewData – New data is available. The widget might need to update layout.
  • NCUpdateResultNoData – There was no new data available since the last update.
  • NCUpdateResultFailed – We’ve failed the attempt to update.

For the sake of simplicity, we’re going to work with a simulated fetch of our data. This is going to be performed by calling an asynchronous method with a block callback:

- (void)fetchFlightInfoWithCompletion:(FlightInfoCompletion)completion
{
    //Preset dummy info
    static NSString *boardingTime = @"12:05";
    static NSString *takeoffTime = @"12:35";
    static NSString *landingTime = @"18:10";
    static BOOL checkedIn = YES;

    //Simulate asynchronous fetch
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(5);

        //Update data
        self.lastKnownTakeoffTime = takeoffTime;
        self.lastKnownBoardingTime = boardingTime;
        self.lastKnownLandingTime = landingTime;
        self.lastKnownCheckinStatus = checkedIn;
        self.dataLoaded = YES;

        //Call completion on main thread
        dispatch_async(dispatch_get_main_queue(), ^{
            if (completion)
            {
                completion(YES,boardingTime,takeoffTime,landingTime,YES,nil);
            }
        });
    });
}

Every time the operating system is ready for updating our widget, it’s going to call the following widgetPerformUpdateWithCompletionHandler: method

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler {
    if (!self.dataLoaded)
    {
        //Start fetch
        [self.aiLoading startAnimating];
        [self fetchFlightInfoWithCompletion:^(BOOL newData, NSString *boardingTime, NSString *takeoffTime, NSString *landingTime, BOOL checkedIn, NSError *err) {
            [self.aiLoading stopAnimating];
            if (err) //An error occured - make sure to notify the OS
            {
                completionHandler(NCUpdateResultFailed);
            }
            else if (newData) //New data is available - update UI and notify OS there's new data
            {
                self.lblBoarding.text = boardingTime;
                self.lblTakeoff.text = takeoffTime;
                self.lblLanding.text = landingTime;
                [self updateCheckinStatusUI:checkedIn];
                completionHandler(NCUpdateResultNewData);
            }
            else //Nothing new - no data to update
            {
                completionHandler(NCUpdateResultNoData);
            }
        }];
    }
    else
    {
        //refresh for next time and force refresh
        [self fetchFlightInfoWithCompletion:nil];
        self.dataLoaded = NO;

        //update UI
        self.lblBoarding.text = self.lastKnownBoardingTime;
        self.lblTakeoff.text = self.lastKnownTakeoffTime;
        self.lblLanding.text = self.lastKnownLandingTime;
        [self updateCheckinStatusUI:self.lastKnownCheckinStatus];

        //The data fetched is not new, make sure to notify the OS
        completionHandler(NCUpdateResultNoData);
    }
}

In order to make sure we’re updated before this is called, a good option would be to call the update method from viewWillAppear:

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];

    //Make sure we're up to date next time we're viewed
    [self fetchFlightInfoWithCompletion:nil];
}

Layout

In terms of layout, we need to make sure to notify about our preferred content size. This is ideal to do in awakeFromNib:

- (void)awakeFromNib
{
    [super awakeFromNib];

    [self setPreferredContentSize:CGSizeMake(kMyFlightWidgetWidth, kMyFlightWidgetHeightWhenNotCheckedIn)];
}

User interactions and referring to your app

Now let’s take care of what happens when tapping “Check in”. What we want to do is open our app with additional data to handle. The best way to do this is with openURL (and of course implementing handleOpenURL in the target application). We’re not going to elaborate about this but make sure you register your app for the proper URL scheme so that it will be launched when calling openURL.

- (IBAction)checkInPressed:(id)sender
{
    [[self extensionContext] openURL:[NSURL URLWithString:@"myflight://check-in"] completionHandler:^(BOOL success) {

    }];
}

When the user wants to manually refresh the data, this is called:

- (IBAction)refreshPressed:(id)sender
{
    [self.aiLoading startAnimating];
    [self fetchFlightInfoWithCompletion:^(BOOL newData, NSString *boardingTime, NSString *takeoffTime, NSString *landingTime,BOOL checkedIn,NSError *err) {
        [self.aiLoading stopAnimating];
        if (newData)
        {
            self.lblBoarding.text = boardingTime;
            self.lblTakeoff.text = takeoffTime;
            self.lblLanding.text = landingTime;
            [self updateCheckinStatusUI:checkedIn];
        }
    }];
}

That’s all folks!

This is how our little flight widget appears in the notification center:


That’s about it – we now have a functioning widget available when your application is installed. You can try it by running its target as you would any other app. The dummy project and the widget itself can be downloaded here.

Icons used in the demo project were taken from icons8

 

Using notification actions

Hi guys! Sorry I was out of it for some time now, it was a pretty busy year so far.

I wanted to talk about a cool new feature available in iOS 8.0 – notification actions!

Once upon a push…

Up until iOS 7.0, whenever you got a remote notification while an app was in the background you could either:

A. Ignore it

B. Read it and ignore it

C. Read it and swipe / tap it

The third option would normally mean that the app gets relaunched and does whatever action it’s designated to do in that case (normally navigate to a certain screen in the app or do something general).

So what’s new?

Starting iOS 8.0, we have a fourth option:

D. Choose something to do from a menu

Think about the following scenario: You’ve created an awesome game that lets users around the world fight about nothing. Whenever someone challenges you to fight you should either respond with “Fight” or “Surrender”. Normally we’d present a notification to the user saying “dumdum86 has challenged you to fight about nothing” and you would need to open the notification by swiping (or tapping) and then choose either “Fight” or “Surrender” within the app. Luckily with the new notification actions we can let the user give us his input from the notification itself, which looks something like this:

Screen Shot 2014-08-30 at 4.14.32 PM

Note that the “Fight” and “Surrender” buttons appear to the user when he swipes the notification banner downward, otherwise the notification is simply standard text.

In the lock screen, the notification appears like it always had with one exception: you can now swipe to reveal actions:

 

Screen Shot 2014-08-30 at 4.17.14 PMScreen Shot 2014-08-30 at 4.17.11 PM

 

 

 

 

 

 

 

 

 

 

 

 

 

On a side note: If the user has chosen to display alerts from your app within an alert rather than a banner, he’ll see the message along with “close” and “options” buttons. If he taps “options”, he’ll see a second alert with the same message but this time with up to 4 options. When using the banners you can only show up to 2. For example - the following can be presented in an alert (the open and close options are added by the OS):

Screen Shot 2014-08-30 at 5.10.46 PM

Shut up and gimme teh codez!

The way to do this is to configure and register something called UIUserNotificationSettings. This class (available iOS 8.0 and up) is used to encapsulate the types of notifications that can be displayed to the user by the app. We will create 2 instances of a UIUserNotificationAction (one for fighting and one for surrendering), set them as actions within a UIMutableUserNotificationCategory which will be used for all “fighting” challenges and then add this category to our settings:


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    //1
    UIMutableUserNotificationAction *declineAction = [[UIMutableUserNotificationAction alloc] init];
    declineAction.identifier = @"REJECT_IDENTIFIER";
    declineAction.title = @"Surrender";
    declineAction.activationMode = UIUserNotificationActivationModeBackground;
    declineAction.destructive = NO;
    declineAction.authenticationRequired = NO;

    //2
    UIMutableUserNotificationAction *acceptAction = [[UIMutableUserNotificationAction alloc] init];
    acceptAction.identifier = @"ACCEPT_IDENTIFIER";
    acceptAction.title = @"Fight";
    acceptAction.activationMode = UIUserNotificationActivationModeForeground;
    acceptAction.destructive = NO;
    acceptAction.authenticationRequired = NO;

    //3
    UIMutableUserNotificationCategory *fightCategory = [[UIMutableUserNotificationCategory alloc] init];
    fightCategory.identifier = @"FIGHT_CATEGORY";
    [fightCategory setActions:@[declineAction,acceptAction] forContext:UIUserNotificationActionContextDefault];

    //4
    NSSet *categories = [NSSet setWithObjects:fightCategory, nil];
    UIUserNotificationType types = UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:categories];
    [[UIApplication sharedApplication] registerUserNotificationSettings:settings];

    // Override point for customization after application launch.
    return YES;
}

Here’s a breakdown of the above:

1 – Creation and configuration of an action for rejecting a fight. Note that the activationMode is set to “UIUserNotificationActivationModeBackground” – this specifies that the app doesn’t need to be foregrounded in order to respond to this action. (No need to bother the user if he isn’t up for a fight). The “destructive” property is NO because we don’t need the option to be highlighted as destructive and the “authenticationRequired” property is NO because we don’t need the user to enter his passcode to respond to this action.
2 – Creation and configuration of an action for accepting a fight. Note that the “UIUserNotificationActivationModeForeground” option specifies that the app needs to be launched in the foreground in order to respond to this action. This time “authenticationRequired” is YES because the user must unlock his device in order to play.
3 – Creation and configuration of a “fight” category used to encapsulate these actions. We use setActions:forContext: with the “UIUserNotificationActionContextMinimal” context, allowing us to show up to 4 options when displaying an alert (not a banner). If we also wish to specify a minimal subset of these options for when the notification is displayed as a banner, we can also register part of the options as “UIUserNotificationActionContextMinimal”.
4 – Creation of a settings object that includes all of the above + registering these settings with the app.

Once we have registered these actions with our app, we’re ready to present notifications and respond to the actions if they are chosen. The way to do this is to implement one of the following methods in your App Delegate (depending on whether this is a remote or local notification):

1. application:handleActionWithIdentifier:forLocalNotification:completionHandler:
2. application:handleActionWithIdentifier:forRemoteNotification:completionHandler:

For the sake of simplicity (and my free time) let’s assume we’re working with local notifications only. Here’s an example of a valid implementation:

- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification completionHandler:(void (^)())completionHandler
{

    if ([identifier isEqualToString:@"ACCEPT_IDENTIFIER"])
    {
        [[FightManager sharedManager] fightLikeAnIdiot];
    }
    else if ([identifier isEqualToString:@"REJECT_IDENTIFIER"])
    {
        [[FightManager sharedManager] surrenderLikeABaby];
    }

    completionHandler();
}

 

* Note: According to Apple’s docs you must call the completion handler at the end of your method. So do it. Because Apple.

Great, now all that’s left to do is call a notification in order to demonstrate our new feature:

- (IBAction)someButtonPressed:(id)sender
{
    UILocalNotification *notification = [[UILocalNotification alloc] init];
    notification.fireDate = [NSDate dateWithTimeInterval:10.0 sinceDate:[NSDate date]];
    notification.category = @"FIGHT_CATEGORY";
    notification.alertBody = @"dumdum86 has challenged you to fight about nothing.";
    [[UIApplication sharedApplication] scheduleLocalNotification:notification];
}

 

The above code responds to a tap of a button, creates a local notification that’s scheduled to fire in 10 seconds and presents the message shown at the begging of the post. The category identifier must be the same FIGHT_CATEGORY previously specified when registering the actions with the app. Of course this is just for demonstration purposes so everything in there is hard-coded.

That’s it! Theres much more to play around with but the main idea should be enough to get you started. Enjoy :)

Quick Tip: Peeking into objects

Hi everyone!

Remember this post from way back about creating custom LLDB summaries for your objects so that you can conveniently inspect objects while debugging? Well the folks at Cupertino have been hard at work and have shipped an awesome feature with the new XCode 5.1 – “Debug Quick Look” which renders my previous post pretty much useless (and I mean useless in the sense that it is now a magnitude easier to accomplish something even better).

So basically this is how it works- You can now provide a visual “meaning” to objects you instantiate by implementing the following method:

- (id)debugQuickLookObject{
    //TODO: Return anything you want to see while debugging
}

Let’s take a look at the following example – I’ve created a data type that maps an NSString to a UIColor (don’t ask me why). It’s really easy to think of how you’d like this visualized for debugging purposes, so rather than seeing something like this:

{
   Coffee = "UIDeviceRGBColorSpace 0.6 0.4 0.2 1";
   Deep = "UIDeviceRGBColorSpace 0.5 0 0.5 1";
   Ocean = "UIDeviceRGBColorSpace 0 0 1 1";
   Tree = "UIDeviceRGBColorSpace 0 1 0 1";
}

…we can now use the debugQuickLookObject and see something like this:
Screen Shot 2014-02-08 at 6.50.37 PM
Here’s the code that is responsible for generating this wonder:

- (id)debugQuickLookObject
{
    NSMutableAttributedString *attrStr = [[NSMutableAttributedString alloc] init];
    [self.theDict enumerateKeysAndObjectsUsingBlock:^(NSString *str, UIColor *color, BOOL *stop) {
        NSAttributedString *colorAttrStr = [[NSAttributedString alloc] initWithString:[str stringByAppendingString:@"\n"] attributes:@{NSForegroundColorAttributeName : color}];
        [attrStr appendAttributedString:colorAttrStr];
    }];
    return attrStr;
}

Now remember, this is code that actually runs while your process is paused so you want to:

  1. Run as little code as possible (creating a caching mechanism is recommended)
  2. Avoid changing your application’s internal state – anything that runs in the debugQuickLookObject method actually interacts with your application’s classes, instances and general state so don’t mess with anything (think of this as Marty avoiding his parents in Back to the Future).

The cool thing about this feature is that you can basically return anything that has visual representation (Strings, Attributed strings, images, bezier paths, views, urls, etc…) and see them while debugging. To access the provided quick look object, all you need to do is hover over the instance and click the little eye icon.

I can already think of dozens of places in my code where everyone collaborating with me could benefit from this visualization (including myself). Also, it would be nice to see this becoming a community thing – Developers writing open source projects could enrich documentation using custom quick looks and generally anyone could write quick looks for existing complex and hard-to-debug code.

The power of concurrency

Hi everyone, and welcome to Saturday night’s advanced session regarding concurrency in iOS.

We all know that some of Apple’s more powerful devices come equipped with multiple cores and we mostly just trust the operating system to utilize this advantage whenever possible, right? Well, turns out there are some cases where we need to explicitly tell the operating system – ‘hey, this code should be executed concurrently’ (iOS is great, but it’s not a magician). Today, we will discuss one of these cases – concurrent enumeration.

Let’s say we have a huge data collection of independent data points and that we wish to execute some heavy mathematical operation on each of them. Now the mathematical operation(s) we wish to apply on a single object is completely orthogonal to all other objects meaning – this is code that’s ideal for running concurrently between processor cores. The way to do this is to enumerate this data collection with the enumerateObjectsWithOptions:usingBlock: method, like so:

[[arr copy] enumerateObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(NSNumber *num, NSUInteger idx, BOOL *stop) {
    //Do some heavy mathematical stuff with 'num'
}];

Just to emphasise the performance gain in using this method rather than fast enumeration, check out the output of running a similar scenario on the iOS simulator (yes, that’s why you see 4 cores):

Using simple, fast enumeration:

Simple

Simple enumeration – takes about 5 seconds to run 10000 iterations. You can see that operations run on one core, while the others are simply kicking back with a beer.

 

Using block enumeration with the NSEnumerationConcurrent option:

Concurrent

Now THAT’S team spirit! About 0.5 seconds to run 1000000 iterations. Unbelievable performance gain.

 

That’s it for today! Hope you find creative, safe ways to use this technique and remember – with great power comes great responsibility, so don’t abuse this API by running things in parallel that shouldn’t.

The Capper

I’ve recently written a little handy utility that some might find useful. The main idea: helping programers and designers understand the idea of resizable images and calculate the correct cap insets for an image based on how you want it to look.

This is a stretched image without defining cap insets. Ewwww.

This is a stretched image without defining cap insets. Ewwww.

After playing around with the controls - viola! Also, you'll find all the code you need at the bottom.

After playing around with the controls – viola! Also, you’ll find all the code you need at the bottom.

Using this tool is pretty easy – download the XCode project from gitHub or CocoaControls, add the image you want to fiddle around with into the project and update the image’s name in Configuration.h

I know the name I chose kinda sounds like “The Crapper”. I only noticed it a moment before pushing to git. Screw you.

Enjoy!

Quick tip: Bridging the gap between ARC and MRR

Since ARC was introduced, more and more developers have seen the light and joined the hype – it’s less prone to human error, it saves us a lot of code and of course it reduces the headache associated with manual retain-release or ‘MRR’. Of course I believe that a good Objective-C developer needs to be familiar with MRR just as any of us were back in 2011, though I always encourage starting new projects with the “use ARC” checkbox ticked on.

One of my favorite benefits of the way ARC does things is that it won’t let you call methods that aren’t visible / defined in your code. This means that there are naturally many less run-time exceptions of the all time favorite “unrecognized selector sent to instance” kind:

Thanks ARC! How responsible of you to say so…

But things weren’t always this convenient. Objective-C is known to be the relaxed stoner neighbour of programming languages. It lets you pass virtually anything to anything else, doing all the work dynamically at runtime and giving you a false sense of security while you write your code. In the pre ARC era if you weren’t careful enough, your app would have the potential to crash more frequently than an airline in southeast Asia during monsoon season:

Hey man relax, I mean come on… what’s the worst that can happen? Hey are you gonna finish those fries?

So what if we are still stuck on MRR? What if we’re working on an old project or simply want to leave memory management to ourselves? Are we doomed to work with this inconvenient disadvantage? Nope. Just add “-Werror=objc-method-access” to your “warning flags” section in the target build settings and this specific warning will turn into an error. Enjoy!

App Review: Dots – A Game About Connecting (or being an addict)

I recently came across the phenomenon called “Dots” – one of the more addictive iPhone games I’ve had the chance to play. The main idea is simple: you have exactly 60 seconds to connect as many dots as you can by color. Sounds boring, right? Well, there are some special patterns, powerups and other features that make this a simply hypnotic experience.

The first time you open the app, you’ll get a chance to play a very unusual tutorial. I say unusual because you don’t exactly understand what’s going on and what you’re supposed to do. I found it pretty intuitive but I guess this can be regarded as a screening process for people who won’t get the game anyway.

After finishing the tutorial, you’ll find yourself smoothly transported to the main menu screen. Right away you’ll notice how smooth, bouncy and fun everything is – even when dealing with boring tasks such as toggling the sound on/off, or typing in your username.

IMG_2464

It’s pretty clear which button they want me to tap…

IMG_2465

So I’ll tap the other one. That will show them!

Once you start playing, there’s very little chance you won’t get hitched – the playability is amazing, the overall experience is smooth and addictive. Make dots disappear by connecting them, make them disappear even more by connecting squares, shrink, expand, freeze time and waste it too! Wonderful…

IMG_2469

IMG_2470

IMG_2467

Yay for you...

Yay for you… Now go back to work

It’s also apparent that these folks gave a lot of thought to the audio – the sounds are mellow, fun and create a wonderful ambience for the whole experience. As you play more and more, not only will you loose touch with your family and friends, but you’ll also gain “dots” – this is the currency used in the game to purchase powerups, such as this one:

IMG_2468

Sounds like an adequate name for fat people pants 

You can also see how other people are doing after each game or through the “High Scores” section in the main menu. While writing this post, my high score was 310 and the leaderboard  number one had 608 (OMG!!! How?? Probably sold his house to buy dots via in-app purchase).

So what makes this app a winner in my opinion?

1. Extremely clean, smooth and intuitive UI

2. Simple-to-grasp concept with a nice range of stuff to learn as you go along.

3. Perfect audio. You’ll understand when you hear it.

4. Beautiful animations

Download at your own risk here, don’t come to me when you realize you forgot to pick up the kids because you were too busy breaking your own record.

Quick Fix: Facebook release an iOS SDK update. Time for renovations.

Update: 

Looks like the people at Facebook are hard at work… a couple of weeks after writing this post, Facebook released yet another (pretty major) update to the SDK. Find out what you need to do here:

https://developers.facebook.com/docs/tutorial/iossdk/upgrading-from-3.2-to-3.5/

Since September 2012, Facebook has kept the iOS SDK on the down-low for some reason and the latest version of the SDK up until a few weeks ago was 3.1.1. This, in my opinion was a huge problem for developers that had to settle for a hybrid, half-baked SDK that used deprecated methods as a formal practice. For instance, in order to invoke facebook dialogs, you had to import the “Deprecated Headers” folder and utilize it’s methods. How lame.

Well, the good news is that 3.2 (and 3.2.1 following very shortly after) is out and it contains many improvements, better error handling and most importantly – uses blocks instead of delegation wherever it can. You will no longer find yourself importing the deprecated headers in order to open a web dialog and from now on you can use the new FBWebDialogs class to do your magic.

facebook__y_u_no____by_jaret246-d4arl9w

Natural response, I get it.

Alongside the SDK Facebook did some great work and released more useful tutorials to make sure the transition to the new SDK is smooth and clear:

I highly recommend taking a couple of hours to upgrade your code to support these API changes – you never know when deprecated methods will start breaking things…

Quick Fix: Icons that are things

I recently found some nice UI/UX related material available on Pinterest and amongst it I found some exquisite app icons (not necessarily belonging to actual published apps). I found it interesting that the ones that caught my eye the most were the ones that represent real objects (TV, notebook, flask, etc…) – these are the icons that leave an impression on you and make you remember where they are on your springboard.

Once you really dive in to the details you can really come to appreciate the work that went into every single one – the attention to details, the complex texture, simply amazing! For instance, look at the wooden texture on the blackboard icon. Also, look at the amount of detail on the cocktail umbrella:

595923-Casette-Tape 296804-Ice-Cream-Sandwich 926344- 746554-Chalkboard-Icon 49ers Contacts Nestle 59518 745081-Hd-Movies 911442-Pizza-App-Icon 571983-Fruit-Punch-iOS-App-Icon786866-Fc-Barcelona-Fan-Ios-Icon

More available on Pinterest

Quick Tip: UIAlertView with block callbacks

What bothers me

Now more than ever I find myself pondering over the question: “Why delegates? Why???”

Last time I found myself asking this was when I had to display some UIAlertViews in a project (though I really tend to stay away from them for UX reasons).

If you’ve had a little more than 10 days of experience in iOS development, you’ve probably already used these for either feedback to the user or as a way to get input from the user. Using it is pretty simple – you create an instance of the UIAlertView while assigning it a title, a message and titles for it’s button(s) and a delegate. Next, you call “show” and viola, it pops out:

Screen Shot 2013-01-31 at 11.30.17 PM

The problem starts when you want to handle the user’s action – if we’re only talking about a 1-button “OK” alert then this is a non-issue, but if there are multiple buttons here and you wish to handle the user’s action according to their choice – that’s where the spaghetti code is born.

Spaghettelegate (It is too a word!)

Now you’ll find yourself implementing yet another delegate method for when the user dismisses the alert. Won’t it go along nicely with all the other out-of-order methods and code in your class? Something like:

- (IBAction)okPressed:(id)sender {
    UIAlertView *nagAlert = [[UIAlertView alloc] initWithTitle:@"Are you sure" message:@"No, seriously are you sure??" delegate:self cancelButtonTitle:@"Not really" otherButtonTitles:@"Yes!", nil];
    [nagAlert show];
}

#pragma mark - SomeDelegate
...

#pragma mark - SomeOtherDelegate
...

#pragma mark - UIAlertViewDelegate
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
    switch (buttonIndex) {
        case 0:
            [self quitInRage];
            break;
        case 1:
            [self doSomethingNice];
            break;
        case 2:
            [self breakEverything];
            break;
        default:
            break;
    }
}

And what if you need to display more than one UIAlertView? Do you start tagging each instance when you create it and then check the tag when the delegate method is invoked? Pfff… please…

An elegant approach

The way I decided to get rid of this pattern (once and for all) is to create a block-friendly “UIBAlertView” class (B stands for Block or Brilliant or any other B-related word of your choice). So now creating, displaying and handling an alert is an all-in-one deal like so:

- (IBAction)activateAlert:(id)sender {
    UIBAlertView *alert = [[UIBAlertView alloc] initWithTitle:@"Yes, like this" message:@"What are you looking at?" cancelButtonTitle:@"Leave me alone" otherButtonTitles:@"Button 1",@"Button 2",nil];
    [alert showWithDismissHandler:^(NSInteger selectedIndex, BOOL didCancel) {
        if (didCancel) {
            NSLog(@"User cancelled");
            return;
        }
        switch (selectedIndex) {
            case 1:
                NSLog(@"1 selected");
                break;
            case 2:
                NSLog(@"2 selected");
                break;
            default:
                break;
        }
    }];
}

This is elegant because:
A. When an alert is dismissed, you know exactly which one it is.
B. You’re creating, activating and handling the dismissing of your alert in the same section of code.

The code is available on gitHub and featured in CocoaControls check it out.

Any comments, suggestions or different approaches will be hunted down and executed. Just kidding, suggest away :)

Follow

Get every new post delivered to your Inbox.