iOS Dev Notes

iOS Development From The Frontlines

How to Send iOS Push Notifications

without comments

We’ve got some exciting news to share with you today: we’re launching a brand new service, called PushLayer.com, to help make sending iOS push notifications easier! We’re looking for some help beta testing it and wanted to give our iOSDevNotes readers the first shot. If you’re interested in giving it a try, get in touch and we’ll hook you up.

Background

Sending push notifications to iOS devices is an incredibly powerful tool and something almost every app can benefit from. For instance,

  • Games can use them to notify players of a turn their opponent has made.
  • Ticketing apps can notify users of upcoming shows and events they may be interested in.
  • Weather apps can notify users of inclement weather in their area.
  • A financial app can notify the user of a change to their bank account.

Push notifications help drive re-engagement with apps like no other tool. If your app is transaction-based (for instance, a user makes individual purchases within your app), push notifications can drive a significant increase in business.

Unfortunately, push notifications can be tricky to implement on the sending side. Apple makes available the Apple Push Notifications Service (APNS) for developers to send notifications, but it requires quite a bit of development effort to work with. Luckily, there are third party services that make it much easier.

In this tutorial, we’re going to get started with sending iOS push notifications using our brand new PushLayer.com service. We’re going to take a look at how to setup the proper credentials with Apple’s Developer Portal, prompt the user for permission to send push notifications, manage the device token, and send a simple test notification.

With great power…

The first step is to update your app to prompt the user for permission to send push notifications. A notification is a naturally intrusive event for a user, as it might interrupt them when they’re not using your app. You should only consider sending push notifications for a truly important event, though what’s deemend “important” is dependent on the type of app and the expectations of the user.

As a rule, I try to put myself in the shoes of my users to determine if they would actually find the notification I’m sending them useful or annoying. Always err on the side of caution, as too many notifications will lead to uninstalls.

Now then, let’s bring up a prompt. First, decide when you want to bring up the prompt for the first time. Usually, this is when the app is first opened.

    [[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeAlert |
      UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound)];

In this example, we’re registering for three types of pushes: alerts, badges, and sounds:

  • An alert is a blue pop-up message that appears to the user on the home screen, with either one or two buttons: OK, or View and Close. Tapping the View button will open your app. The pop-up can be customized to show a string.
  • A badge is an integer and appears as a red circle on the top-right of your app’s icon on the home screen. This could be used for, say, an email app that would like to display the number of unread emails in your inbox.
  • Lastly, the sound type allows you to specify a custom sound file to play when a push notification comes in. The sound file is located inside your app’s resources directory.

When deciding which of these features to take advantage of, always keep the user in mind. Is a badge actually necessary (for instance, to display the number of unread emails in their inbox), or is it going to be confusing? Remember that you never want to distract or annoy a user or they will uninstall your app.

Next, you should add the following two methods to your app’s delegate:

    - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
      NSLog(@"Device token: %@", deviceToken);
    }
 
    - (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)err {
      NSLog(@"Failed to register for pushes: %@", err);
    }

In the prompt, if the user agrees to give your app permission to send push notifications, you’ll receive the “didRegisterForRemoteNotificationsWithDeviceToken” callback, along with the device token (more on this later). If the user doesn’t grant permission, or another error occurs, your delegate will receive “didFailToRegisterForRemoteNotificationsWithError” with a related error message.

Lastly, note that push notifications (and device tokens) are not available on the iOS Simulator. When you call “registerForRemoteNotificationTypes” while running in the simulator, you’ll notice that nothing appears to happen. Instead, you’ll need to run this on your device, and that requires we setup some credentials.

Setting up credentials

Push notifications only work on physical devices and Apple requires you do a little prep work to properly credential your device. First, you need to configure your App ID in the Apple Developer Portal website to permit sending push notifications:

Configuring an App ID

First, login to your ADC account and go to the iOS Provisioning Portal. Then:

  1. Edit your App ID (or create a new one first, if you don’t have one yet) and make sure you “Enable for Apple Push Notification Service”. Every App ID can allow two types of push notification credentials: production (i.e. App Store), and development, which is what we’re mainly interested in now.
  2. Click Configure next to the “Development Push SSL Certificate”. This will launch the APNs Assistant which will explain how to generate a certificate using the Keychain Access application on your computer. Once you’ve completed generating your certifcate, upload it back to the portal. After a few moments, your App ID will be ready for push notifications.
  3. Click the Download button next to “Development Push SSL Certificate” and take note of where the file is downloaded to.

Setting up your certificate

Once you’ve download your push certificate you next need to:

  1. Double click it to install the certificate into your Keychain.
  2. Within Keychain, click on My Certificates (on the left) and locate an entry for “Apple Development iOS Push Services: com.mycompany.myappid”. Click the arrow to the left to expand the entry to also show your private key.
  3. Select both entries (the parent and the child) and right click. In the pop-up menu, click the “Export 2 items…” entry.
  4. In the File window, save the export to your home directory (or anywhere, just remember where you put it) and give it a name like “apns-dev-cert.p12″.
  5. Finally, we need to conver the .p12 format into a more easily transferred .pem file. Open up Terminal and run the following command:
openssl pkcs12 -in apns-dev-cert.p12 -out apns-dev-cert.pem -nodes -clcerts

Note that if you changed the name or location of the .p12 file you generated earlier, you’ll need to update the command above.

You should now have a shiny .pem file with everything we need to send push notifications. If you’re having difficulties, check out Apple’s in-depth documentation.

Seting up PushLayer

We’re on the home stretch. The next step is to setup an account with PushLayer and upload our certificate. To do this:

  1. Register for an account (if you don’t already have one). When signing up, the first 30 days are free.
  2. Create a new application, giving it a descriptive name.
  3. With a text editor, open the .pem file we created earlier (it’s just a plain text file) and carefully copy the entire contents into the Certificate entry field. Make sure the radio button for Development is selected, and click Save.
  4. Finally, take note of two things: the API Key, which is listed on the application’s page, and the application ID (you can get the ID by clicking on the name and noting the number in the URL). You’ll need both of these later.

Storing the device token

Now that the hard part is out of the way, the next step is to deal with the device token.

The device token is required whenever you want to send a push notification, and you should store this token on your own remote server. Let’s update our “didRegisterForRemoteNotificationsWithDeviceToken” method to convert the deviceToken into an NSString that’s more easily transmitted to the remote server:

    - (void)application:(UIApplication *)app didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
      // see: http://stackoverflow.com/a/9372848/1264925
      const unsigned *tokenBytes = [deviceToken bytes];
      NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x",
                            ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
                            ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
                            ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
      [self saveDeviceToken:hexToken]; // TODO: save the token to our remote server
    }

I’ll leave it to you to handle storing this token.

Sending a notification

Finally, the last step is to actually use the device token we’ve saved and send a push notification to it. Luckily, this is very easy using PushLayer. Here’s a simple example using the UNIX command cURL, which lets you submit a POST message to the PushLayer.com site:

curl -X POST https://www.pushlayer.com/applications/[APP_ID_NUMBER]/notifications.json?api_key=[API_KEY]

Replace APP_ID_NUMBER and API_KEY with the values you got in step 4, above.

If your remote server is a ruby-based one, be sure to check out the pushlayer-ruby bindings, which makes sending much easier. If you’re using PHP, Python, or many other languages, check out the libcurl library for an easy way to connect to remote web services like PushLayer. We’re working on additional language bindings, so let us know what you’d like to see next in the comments.

Best Practices

Lastly, there’s a couple of tips we’ve found related to push notifications that you’ll want to take note of:

  • Consider storing a boolean in NSUserDefaults (or elsewhere) on whether the user has previously granted or denied permission to send push notifications. If the user has granted permission, every time your app opens you should call “registerForRemoteNotificationTypes” again. This is to ensure that you always have the latest device token. You should also update your backend service with this device token every time to ensure that you’re always sending notifications to the right place.
  • If a user grants permission to send push notifications, the user’s device will attempt to retrieve a device token from APNS. In order to get this device token, the device must be connected to the Internet and able to access APNS on port 5223. Consider using Reachability to determine if the device is connected to the Internet properly before prompting for permission to send push notifications. You wouldn’t want them to grant access only to have it fail (and then need to prompt them again later). Instead, prompt when you know you can succeed in getting the device token to reduce confusion and frustration on the part of the user.

We hope you found this helpful, and we really hope you’ll give PushLayer.com a try. We’ve built a lot of iOS apps, many of which need to send push notifications, and we designed PushLayer to make this as easy as possible. We’ve also made the service affordable, and added the features we always wish we had (like better debugging). Let us know what you think!


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

February 26th, 2013 at 9:02 pm

Posted in Networking,Tutorials

Screencast: UIAlertView Styles

without comments

Here’s a screencast on the new UIAlertView styles that have been added to iOS 5.

It makes it really easy to query the user for information, including usernames and passwords. In the past this was a big pain, involving creating your own UIAlertView from scratch.

You can subscribe to the YouTube channel here: iOS Dev Notes on YouTube. If you found the video helpful, I’d appreciate if you could like/favorite it as well.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by cwalcott

October 28th, 2011 at 3:26 pm

Core Location Redux: Adding an NSTimer

with 5 comments

Yesterday, I posted a tutorial on getting started with Core Location. Be sure to check it out if you haven’t already.

One of the comments we received pointed out that it would be a good idea to also have an NSTimer running to stop the Core Location updates should we never receive a location with a good-enough accuracy. This prevents the case where the device keeps Location Services powered up (such as the GPS and radio) and drawing power. Let’s take a look at how to implement this timer:

First, setup an NSTimer class variable in the same class where you’re using the CLLocationManager:

@property (strong, nonatomic) NSTimer *locationTimer;

Be sure to include the corresponding @synthesize. Next, let’s implement a method, called stopUpdatingLocations, to be called when the NSTimer determines a preset interval of time has elapsed:

- (void)stopUpdatingLocations { [locationManager stopUpdatingLocation]; [locationTimer invalidate]; }

This method does two things. It first sends a message to the locationManager to stop updating location information. Note that it’s okay to send multiple stopUpdatingLocation messages to a CLLocationManager, such as the case where we tell it to stop updating after we’ve received a location that’s “good-enough”, and then the timer fires.

Lastly, this method invalidates the NSTimer object as we no longer need it.

One last note: we also want to stop this timer should we unload our view (remember that we’re already stopping Core Location updates in our viewDidUnload method). So, let’s add the following there as well:

[locationTimer invalidate];

Starting the Timer

Great, now let’s kick off the timer. In our viewDidLoad method, we’re going to add the following to the end (just after startUpdatingLocation):

self.locationTimer = [NSTimer scheduledTimerWithTimeInterval:30.0 target:self selector:@selector(stopUpdatingLocations) userInfo:nil repeats:NO];

This sets up a new NSTimer, saves it to the locationTimer class variable, and schedules it with a 30 second timeout (I picked this arbitrarily – you’ll want to decide for yourself how long you’re willing to wait for a location update). When the timer fires, it will call our stopUpdatingLocations method which we already implemented above.

Hope you find this helpful. If you’re going to be implementing Core Location services in your own apps, it’s best to think through every possible edge case to ensure your app is rock-solid. Be sure to post with any other questions or comments you might have.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

October 27th, 2011 at 11:11 am

Core Location in iOS: A Tutorial

with 8 comments

Developing iOS apps that can take advantage of the built-in GPS unit is a great way to add a new dimension to the user experience. You can quickly make your apps location-aware (and able to find the current latitude/longitude coordinates) with minimal effort. This tutorial will walk through the steps involved in finding a device’s current position.

In iOS, you can find the current lat/lng by making a request to the Core Location component to start looking up location information. This works via a series of delegate messages passed to our application when Core Location starts updating. By default, the location hardware in our device is usually turned off (or otherwise in an idle state) so as not to needlessly drain power. In a nutshell we need to:

  1. #import the correct Core Location header file
  2. Link against the Core Location framework
  3. Implement two delegate methods to receive information from Core Location
  4. Tell Core Location to start sending us updates
  5. Properly shut down Core Location when we don’t need it anymore.

Getting Started

Since we’re going to be using Core Location with our iOS app, we need to add the header file (CoreLocation/CoreLocation.h) to our prefix header (usually with the extension .pch). In this prefix header, locate the #ifdef __OBJC__ line:

#ifdef __OBJC__
    #import 
    #import 
    #import  // added for Core Location
#endif

If you have a larger project and are only using Core Location in a few places, you could alternatively just add the #import line to the files where you actually need it. In our simple example, we’ll stick with the prefix file.

Next, we need to add the Core Location framework to our project’s link phase. In Xcode, click on the project’s name at the top of the project navigator view. In the Linked Frameworks and Libraries section, click the “+” icon to add a new framework. Find the Core Location.framework entry, and add it. When you next link your project, the framework will be automatically included.

 

Setting up the Delegate

The begin setting up our class as a delegate, the first step is to locate the class where we want to receive Core Location messages. Then, we need to set that class up as a CLLocationManagerDelegate. You can do this by adding to the @interface definition line.

Next, in the implementation file, we need to add two delegate methods:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation

This method will be called when Core Location has new location data for us to process. We receive these messages both when we have better accuracy for a current location, or the device has moved. These messages can also come in quickly one after the other, or as a trickle.

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error

If Core Location needs to report a failure of some kind, this method will be called. We need to properly handle these. Note that this method is technically optional, but it’s best to get into the habit of handling these messages.

Next, we also need a way to store where we currently are located. Since there’s no guarantee of when we’ll be told of our location, we need to store this information for the times when we need to reference it. We also need to keep track of our Core Location manager instance. So, be sure to add the following to your class’ @interface definition:

@property (strong, nonatomic) CLLocationManager *locationManager;
@property (strong, nonatomic) CLLocation *currentLocation;

And, in the implementation file, the corresponding synthesize methods:

@synthesize locationManager, currentLocation;

Receiving Messages

Now that the setup is out of the way, it’s on to the fun stuff. First, let’s implement the didUpdateToLocation method. I’ll paste the entirety here and then we’ll step through it:

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
    self.currentLocation = newLocation;
 
    if(newLocation.horizontalAccuracy <= 100.0f) { [locationManager stopUpdatingLocation]; }
}

First, note that we’re saving the currentLocation. Again, since we don’t know when we’re going to be given an update to our location, we need to keep this around for whenever we might need it in the future.

Next, we check the included accuracy against a pre-set expectation. Each time we’re given a newLocation, we’re given an estimation on how accurate it is. This float is in meters, and usually starts out as pretty inaccurate (remember that when we first start requesting location updates, Core Location needs to determine the device’s location, which can take time).

You’ll notice in the Maps app on your iPhone that a blue circle is initially drawn around the point marking your location. That blue circle’s radius is determined by this horizontalAccuracy number. If you’re going to be showing a point on a map for the user’s current location, you may want to consider adding a similar “accuracy circle”.

The reason why we’re checking the provided accuracy against a pre-set expectation is that we don’t want to leave Core Location running for any longer than we absolutely need it to. It drains the battery life of the device when it has to communicate with GPS satellites or power up the radio for location triangulation, so we want to turn it back off again as soon as we have a good-enough position. In the example above, a position +/- 100m is plenty accurate for us. To stop Core Location from sending us updates (and thus allowing it to go back into an idle state), we send our locationManager the stopUpdatingLocation message.

Lastly, we need to implement the didFailWithError method. Here it is:

- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error {
    if(error.code == kCLErrorDenied) {
        [locationManager stopUpdatingLocation];
    } else if(error.code == kCLErrorLocationUnknown) {
        // retry
    } else {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"Error retrieving location"
                              message:[error description]
                              delegate:nil
                              cancelButtonTitle:@"OK"
                              otherButtonTitles:nil];
        [alert show];
    }
}

When handling errors from Core Location, we need to worry about two special cases. The first is when the user specifically denies our app access to finding its position. If that occurs, we want to immediately stop trying to update by sending the stopUpdatingLocation message.

The second special case is when we’re given a kCLErrorLocationUnknown error. This can occur when Core Location is first starting up and isn’t able to immediately determine its position, or even make a guess as to where it is. If we get this message, its best to ignore it and to keep trying to update our location.

In other cases, we may want to alert the user to a problem with getting a location. According to the Core Location docs, this might occur when the device is near a particularly strong magnetic field. In this example, we’re showing the user a problem but still retrying. You will need to determine for your own app whether you want to stop trying to get location updates at this point or to keep retrying.

Requesting Updates

After the delegate has been implemented, it’s time to actually setup Core Location and request location updates. To setup Core Location, you’ll want to do the following:

locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startUpdatingLocation];

This sets up a new CLLocationManager instance and saves a pointer to it in a class variable. We next set our current class up as the delegate to receive CLLocationManagerDelegate messages. And finally, we tell Core Location to start figuring out our position.

In my example app, I’ve placed all of my Core Location-related code in a single view controller’s class as I only need this information in one section of my app. Thus, I placed the above three lines in my viewDidLoad method, which is called after the view has been loaded, but before it’s being shown to the user. This could occur right when the app opens up (if loaded from the XIB file), but it could also occur at another point in the app, such as after a low-memory condition and the view had been previously unloaded.

Since I put this setup code in the viewDidLoad method, I also need to remember to tell Core Location to stop updating should my view get unloaded. Thus, I placed the following line in my viewDidUnload method:

[locationManager stopUpdatingLocation];

It’s important to remember to tell Core Location to stop sending messages to the delegate before the delegate could be freed, which could cause Core Location to attempt to send messages to a freed/nil object (which would cause a crash).

And that’s all there is to it! Implementing Core Location is an easy thing to implement and can add an interesting new dimension to your iOS apps. Be sure to post in the comments if you found this interesting or have questions.

Reverse Geocoding

One final note. Now that we’ve successfully found the current lat/lng of our device, the next step is to do something interesting with it. Many times you’re looking to turn the coordinates into a street address.

Reverse geocoding is the process by which you find the closest street address given a latitude/longitude coordinate pair. To do this, you must use a reverse geocoder, which is a third-party service (though there is a built-in geocoder in iOS that uses Google). Be sure to check out the article on using SimpleGeo to reverse geocode a pair of lat/lng coordinates into a street address.

Update: Adding an NSTimer

A suggestion was made that we also start an NSTimer to ensure against the case where we never get a “good-enough” location, which would thus leave Core Location running indefinitely. Check out the follow-up post which shows how to implement an NSTimer to stop Core Location after a pre-set time interval.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

October 26th, 2011 at 12:34 pm

Screencast: UIStepper

with 6 comments

I’ve recorded a screencast on UIStepper, a new UIKit control that’s been added to iOS 5.

Let me know what you think! We hope to record more screencasts on all sorts of topics in iOS development.

You can subscribe to the YouTube channel here: iOS Dev Notes on YouTube. If you found the video helpful, I’d appreciate if you could like/favorite it as well.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by cwalcott

October 20th, 2011 at 3:50 pm

Posted in iOS 5

Tagged with , , ,

UITableView Tutorial

with 17 comments

UITableView is one of the most common types of views used in iOS applications. Let’s go through a tutorial of the most regularly used features of table views.

There are two main types of UITableView. The first is a “plain” table view, which looks like this:

The second is a “grouped” table view, which looks like this:

Both are commonly seen in iOS apps. Grouped table views are often seen in “settings” sections of apps, or on screens where users are inputting or editing information.

In this post we’ll be going through a sample app that uses UITableView. The code is available here.

Data

There are lots of different types of data you might be representing in your table. Maybe it’s a list of countries. Or you might be using the table more for navigation, by giving the user a list of screens they can choose to go to.

iOS keeps it pretty open-ended when it comes to how your data is represented. A UITableView is setup to support a list of rows, optionally split into sections with header names. If you don’t want to show the rows in sections, you would have one unnamed section.

This can be a bit confusing at first, as you might expect to be able to just give the table view an array of rows, and be done with it. Instead, as is common with iOS development, the delegate design pattern is used.

It may be annoying at first, but it actually provides a lot of flexibility for how you choose to represent your underlying data model. You simply define specific methods to use in order to get table information, and iOS can call them when it needs to know something like the number of rows in a section, or the content of a particular row. We’ll get into specifics below.

UITableViewDataSource vs UITableViewDelegate

One thing that makes UITableView unique is that it has two different types of delegates: UITableViewDataSource and UITableViewDelegate. This is fairly unusual in iOS. Usually an object will only have one delegate at most. This can be confusing at first (I know it was for me was I was first learning iOS).

The way to think about these two delegates is that they’re split based on functionality: for the most part, UITableViewDataSource relates to the actual data being shown in your table, whereas UITableViewDelegate deals more with the appearance and UI of the table.

To help differentiate them, here are the types of things each delegate looks for:

UITableViewDataSource:

  • Number of sections in the table
  • Number of rows in a particular section
  • Header and footer titles
  • Being told when rows are reordered, inserted and deleted
  • The particular cell at a given location in the table

UITableViewDelegate:

  • The height of a row at a given location in the table
  • Handling of selection of a row
  • Custom header and footer views for sections in the table
  • Responding to row editing

There’s some overlap between the two. For example, if you allow users to insert a new row, you may want to be notified of that action occurring (through UITableViewDelegate) and also will need to update your underlying data model (through UITableViewData Source).

There are certain delegate methods that are used over and over when working with table views, and over time you’ll probably start to forget whether they live in UITableViewDataSource or UITableViewDelegate. When you find yourself needing one of the less common methods, it can be hard to remember which delegate they belong to, so it’s a good idea to check the documentation for both if you’re not sure.

UIViewController vs UITableViewController

The class that implements the delegate methods is almost always the view controller that owns the table view. What should that class be? Most view controllers are usually subclasses of UIViewController, but iOS also provides a UITableViewController.

UITableViewController only provides a few features on top of UIViewController:

  • UITableViewController has a tableView property built-in that points to its table view.
  • UITableViewController will automatically make itself the data source and delegate, unless you specifically change it.
  • The UITableViewController will reload the table view’s data the first time it’s loaded. It will also clear any selected rows whenever the table view is displayed.
  • After the table view appears, it will flash the table view’s scroll indicators. This is a hint to the user that there’s more data than they currently see on the screen.
  • If there’s a navigation bar with an Edit/Done button, the UITableViewController will hook it up to toggle the edit mode of the table view.

Basically, it saves a little bit of time by automatically implementing some common and expected code. If you don’t want any of this behavior, you can always do it yourself within a UIViewController. Just remember to manually implement the steps listed above yourself, if you still want them. You might have seen apps where you select a row from a table, go to a new screen, and when you come back the row is still highlighted. This is usually a sign that someone used a UIViewController with their table view and forgot to clear the selection :).

The most common times when I don’t use a UITableViewController in my apps is usually when the view controller needs extra functionality beyond just a table view. Perhaps I want the table view nested inside another view, for example. Usually though, you can use a UITableViewController, as we do in the example below.

A helpful hint: if you’re creating a new view controller though Xcode (like under File -> New -> New File…), you can select “UIViewController subclass”, and then on the next screen, choose “UITableViewController” from the “Subclass of” drop down.

UITableViewDataSource

Say we have an app that displays a list of countries in a table view. (A reminder that I’ve made an example project that does this available here: UITableView-Tutorial).

We have a UITableViewController subclass called CountriesTableViewController. Countries are stored in an NSDictionary called “countries”. The keys in the dictionary are the continents in the world, and each value is an NSArray with the countries in that continent.

The first method we’ll define is “- numberOfSectionsInTableView:”. This is the number of keys in the countries dictionary:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return [self.countries count];
}

Now we add a way to get the title of a given section:

- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section
{
    return [[self.countries allKeys] objectAtIndex:section];
}

Next, we provide a way to return the number of rows for a given section. We can use the method we just defined to get the name of the continent:

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    NSString *continent = [self tableView:tableView titleForHeaderInSection:section];
    return [[self.countries valueForKey:continent] count];
}

The one required method left is to return the cell for a particular row.

cellForRowAtIndexPath and UITableViewCells

The method “- tableView:cellForRowAtIndexPath:” is an important one. We’re given an index path. An index path is an object that contains both a section and a row. Given this information we can determine which row we’re looking for and configure a UITableViewCell to match it.

The class UITableViewCell represents a single cell (or row) in the table view. If a table has thousands of rows (say, in a list of contacts), then for iOS to have to create a thousand UITableViewCell objects would be a large resource hog. Also, when a user swipes down a table view list, iOS has to setup these rows very quickly, and creating separate UITableViewCell objects as you scroll takes a lot of time.

This is why UITableViewCell uses the idea of a “cell identifier”. When you create a UITableViewCell, you can give it a cell identifier string. After that, calling “dequeueReusableCellWithIdentifier” will attempt to return a pre-existing UITableViewCell, saving iOS from having to create a new one. It can cycle through 10 or so table view cells that are visible at any one time.

You usually only need different cell identifiers for each separate cell layout in your table. In many cases, there is only one, if each cell is laid out the same way. That is also the case for our example.

UITableViewCell is a very versatile class. It has a number of styles pre-defined, including a single line of text, a line of text with a subtitle, and image views. You can also setup the accessory view, which is the symbol you sometimes see on the right side of the cell (often as a “>”). If you need to further customize UITableViewCell, you can get access to its contentView, or even subclass UITableViewCell for complete control.

Let’s setup a simple cell with the country name on the left, and an accessory indicator on the right:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"CountryCell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
    }
 
    // Configure the cell...
    NSString *continent = [self tableView:tableView titleForHeaderInSection:indexPath.section];
    NSString *country = [[self.countries valueForKey:continent] objectAtIndex:indexPath.row];
 
    cell.textLabel.text = country;
 
    cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
 
    return cell;
}

UITableViewDelegate

The code we’ve written above is actually enough to show the list of countries within sections of continents. However, it doesn’t provide a way to respond to any actions, such as the user tapping on a row of the table. That’s where UITableViewDelegate comes in.

Let’s add in some code that presents the country in a UIAlertView when its tapped. For this we define a method called “– tableView:didDeselectRowAtIndexPath:”.

- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSString *continent = [self tableView:tableView titleForHeaderInSection:indexPath.section];
    NSString *country = [[self.countries valueForKey:continent] objectAtIndex:indexPath.row];
 
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:[NSString stringWithFormat:@"You selected %@!", country] delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil];
    [alert show];
    [alert release];
 
    [tableView deselectRowAtIndexPath:indexPath animated:YES];
}

We’re given an indexPath, which we can use to find the country that was selected. Then we display that in an alert. Lastly, we deselect the row. Without this last part, the row would stay highlighted after being tapped.

Conclusion

I hope you’ve found this UITableView tutorial helpful. UITableView and its related classes are used in almost every iOS app. There were some more complex things that I didn’t talk about here, including inserting, reordering and deleting rows, and coding up custom UITableViewCells. If you’d like to know more about those or any other topics, let me know in the comments! It might be our next tutorial!


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by cwalcott

October 5th, 2011 at 10:53 am

Top 5 Developer Features in iOS 5

without comments

There’s been a lot of discussion over new features in iOS 5 from a user perspective, but I’d like to cover how iOS 5 will affect iOS developers.

iCloud

iCloud is certainly one of the most visible items being added in iOS 5. I also think it has the possibility to affect a lot of iOS apps.

iCloud allows apps to use cloud-based storage of documents and key/value data. Changes are wirelessly pushed to iCloud and automatically synced on all devices (including computers as well as iOS devices).

I think iCloud will be great for iOS apps that have iPhone and iPad versions. Someone can be using your photo editing app on their iPhone and then switch to the iPad version and continue right where they left off.

iCloud can also increase the possibilities of sharing content. Any iCloud document can be given a public URL, so apps can provide users with URLs that let them share documents online, which also has the advantage of providing some viral marketing possibilities for apps.

I also think the key/value aspect to iCloud will allow for some interesting possibilities. Most apps don’t bother to sync user preferences because it’s just too much work to bother. However, iCloud makes it so simple that I think a lot of apps will start to take advantage of it. An example of this might be Twitter syncing your associated accounts across the various versions of their app.

iCloud also works directly with Core Data, so almost any app that already uses Core Data ought to be able to link it with iCloud, and have anything in Core Data be synced across devices as well.

I think one of the biggest things that iCloud will provide to developers is that for many types of apps, developers will no longer be required to build and maintain an external API. Any apps that need APIs for backups, storage, or syncing can just use iCloud instead.

Automatic Reference Counting (ARC)

Since this is a feature that only devs care about, it hasn’t gotten much press. However, I think it’s going to be a great boon to us developers.

ARC means we no longer have to worry about calls to retain and release, or retain counts! ARC takes care of this for us.

It’s not a garbage collector, which means it’s not adding any overhead to apps. Instead, the memory management is being added at compile time. Since the rules of memory management are pretty well defined in Objective-C, the compiler can worry about when an object is done being used, and free it.

For us, this means fewer bugs, since memory issues are one of the biggest causes of application crashes. It also means less code. For example, you don’t have to worry about always defining a dealloc method that releases all the properties of an object.

We’ll still have to think about memory, but mostly in terms of ownership. Methods that start with “alloc”, “copy”, “init”, ”mutableCopy”, and “new” transfer ownership, and anything else does not. So it’s important to think about what kind of method you need to call, depending on whether the object can go away when the current scope ends, or if it needs to stay around. If it’s staying around, it’ll need to be set to nil at some point.

Overall, I think this will be a big help to developers, and really helps fix one of the biggest complaints developers have when moving to Objective-C from another language.

Storyboarding

Storyboarding is another nice feature that will really save developers some time. In iOS development, Interface Builder has always provided a nice way to graphically represent the layout of individual view controllers, rather than doing it programatically.

But when it comes to the flow between view controllers, there’s been no easy way to do this. If you try to do it in Interface Builder, you end up with a lot of proxy objects cluttering up your XIB files. It’s usually easier to do it programatically. In both cases, a lot of care has to be placed on memory, or else performance takes a hit.

Storyboarding greatly simplifies the setup between view controllers. It allows for a graphical representation of the flow between screens, much like Interface Builder can already do for individual view controllers. Basically, you add various view controllers and hook them together with “segues”. The segue is the transition between view controllers.

An example would be a navigation controller that has a table view at the root, and then a detail screen that is shown when a cell is tapped. The segue here is the animation between the list and the detail screens. They’re customizable, and you can define your own. You can also define the data that gets passed as part of the segue.

Storyboarding looks like it will be especially nice for table views. The table view cells can be laid out directly in the storyboard, and hooked into the table view controller. Even better, storyboards allow for the design of static table views. These are especially common for “forms”: the kind of screen you’d see when adding or editing a piece of data. Usually each cell has a label on the left, and on the right some kind of entry field. Coding this up has always been a bit ugly, especially if different kinds of cells are used. With static table views, you can visually lay out the entire table. This greatly reduces the amount of coding. You don’t even need to define a data source for the view controller!

AirPlay

AirPlay was introduced in iOS 4.2. It allows apps to stream audio and video to an HDTV via an Apple TV. In addition, the iPad 2 could be mirrored on a display using an adapter.

AirPlay is being expanded in iOS 5, and I think there’s the possibility of adding additional value for a lot of apps.

First, iOS 5 makes it much easier to stream audio or video. Any app that deals in media should take advantage of this to allow users the ability to stream onto their TVs.

In addition, iOS 5 allows iPad apps to display different content on the display and the iPad. Here’s where I think a lot of opportunities will be opened.

This can be great for games. A racing game could show the track and controls on the device, and the actual race on the large display.

Also, any app that can benefit from sharing content with multiple people (like photo and presentation apps) can take advantage of this for sharing content on a big display.

There’s some question as to how many users will take advantage of AirPlay. It does depend on adoption of Apple TV and other devices that can work with AirPlay. However, I think there’s a real potential for large numbers of users to regularly connect their iPhones and iPads to displays and expect their apps to provide functionality over AirPlay. If you think about the number of apps that in some way involve video, photos, or audio, it’s a pretty high number, and including AirPlay support might be a good way to stand apart from the competition.

Notification Center

With Notification Center, local and push notifications get a much better user experience. Notifications no longer pop up and interrupt whatever the user is doing, and users have a single place to go to view notifications.

This could be big for apps. In the past, notifications had to be limited to really important events, because showing them had the potential to really annoy users as they were suddenly pulled away from whatever they were doing. Now, users can see the notification, and choose to deal with them immediately or come back later.

Because the App Store is so big, we have a problem with not only getting users to download our apps, but to also continue using them. I have lots of apps that I download, use once, and then forget about.

Now that we have Notification Center, I think notifications will become even more important as a way to encourage users to return to our apps. Obviously we have to be careful about spamming users, but I do think we can take advantage of notifications more often. An event that might previously have not warranted a notification may now be appropriate.

There really isn’t anything new to do in order to take advantage of the Notification Center as developers. Local and push notifications continue to work as they have before. However, provisioning of push notifications will now be built in directly to Xcode, which should further simplify the process.

Conclusion

Looking back over these features, one of the common themes from a development perspective seems to be simplification and reduction of code. That’s actually pretty surprising considering a new version of an OS means new features to think about. Other features that I didn’t mention, including Twitter Integration, Newsstand, and
iMessage, also offer additional functionality that may have been possible but more difficult before iOS 5.

Overall, it’s a good time to be an iOS developer! I’m personally looking forward to the chance to play with some of these new features in the apps I build. And of course I’ll continue to share my thoughts and suggestions with you as well.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by cwalcott

October 3rd, 2011 at 1:48 pm

Custom UINavigationBar: Two Techniques

with 5 comments

A key component of almost any iPhone or iPad app is the UINavigationBar.  It makes navigating through an app effortless, is intuitive to users, and can even be customized to match almost any style you need.  In this article, we’re going to look at two easy techniques to customize your UINavigationBar.

I’ve created a simple demo app to show the two techniques.  Feel free to follow along with the source code.

Getting Started

First off, let’s talk a bit about how a UINavigationBar is constructed.  Normally you have an XIB (perhaps in your MainWindow.xib file) that contains your Navigation Controller.  Often the default behavior will suffice (though you can always subclass UINavigationController and implement your own behavior if you need to).

In the following example, I have a tab-based layout with three separate Navigation Controllers.  The first one, called “Default”, has its own UINavigationBar and the associated view controller (which, in turn, contains a UINavigationItem).

Once you have the basic setup, build and run your app to make sure things look about right.  You’ll have a plain vanilla blue navigation bar with a text string for a title.  This will form the basis of the next two techniques.

 

Technique 1: Embedded View

In many cases, leaving the actual UINavigationBar alone and instead customizing the UINavigationItem (the title part) is what we’re looking for.  This can be done by embedding a UIView and adding components to that as we see fit.  For instance, in this example, we have a single UIView positioned in the center of the title area, which contains a UILabel and a UIImageView.

This technique is fairly straightforward and can be done entirely within the XIB.  You don’t need to subclass anything, and you can change the appearance of these custom titles easily in the future.  Let’s walk through how to do it.

First, make sure you have a working Navigation Controller setup with a default UINavigationBar as discussed in the Getting Started section.  Once that’s working properly, start by dragging a new UIView onto the Navigation Bar item in your XIB.  As you drag the UIView over the Navigation Bar, it should give you a drop area to place it.  Drop it there.

Next, add elements and customize this view as you like.  I added a UILabel and a UIImageView, carefully aligning them to fit properly.  When you like how it looks, be sure to click on the View and set it’s background color to Clear Color, and (under Drawing) make sure the Opaque checkbox is unchecked.

 

Note that there was a bug in Interface Builder (prior to Xcode 4) which wouldn’t redraw the UINavigationBar with the clear background color in the title UIView until you re-opened the XIB.  If you set the background color to Clear Color and turn off the Opaque checkbox, then try rebuilding your app.  Chances are it will work properly, even if it doesn’t quite look right in IB.

One catch: when you’re using an embedded view as a title, you need to be careful to setup the Back Button property for the Navigation Item the view belongs to.  In this case, I set it to “Simple is good!”.  If you don’t set this, then when you call pushViewController, the next view won’t have a back button.  Normally, the UINavigationController can determine what the back button text should be based on the previous view controller’s title.  But since our title isn’t a UILabel, we need to be explicit about this text.

 

Technique 2: Subclass & Override

The other way of customizing the UINavigationBar is to subclass the UINavigationBar class and override the drawRect: method.  It’s very easy to do, and gives you the most control over how your UINavigationBar will appear.  However, it requires a bit of code.

First, again, make sure you have a basic Navigation Controller with a default UINavigationBar setup in your XIB (as shown in the Getting Started section).  When that’s working, you next want to setup your UINavigationBar subclass.

In Xcode, create a new Objective-C class, and make it inherit from UINavigationBar:

#import <UIKit/UIKit.h>
 
@interface MyCustomNavigationBar : UINavigationBar
 
@end

Next, in the implementation file, override drawRect: (and nothing else!) with whatever you’d like.  In this example, I’m just loading an image  an image as a new background, but you could do just about anything here.

@implementation MyCustomNavigationBar
 
- (void)drawRect:(CGRect)rect {
	UIImage *image = [UIImage imageNamed:@"Custom-Nav-Bar-BG.png"];
	[image drawInRect:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
}
 
@end

Once your custom UINavigationBar is ready to go, you next need to update your XIB to use it instead of the default UINavigationBar class.  Click on the Navigation Bar item in your XIB and set Class to your custom class (in this case, MyCustomNavigationBar).

Rebuild your app and, if everything was done correctly, you should now have a custom UINavigationBar as you set it up in the drawRect: code!  If not, be sure to double check that your custom UINavigationBar is being called and that only the drawRect: method is being overridden.

 

Other Resources

There are a number of additional resources to help you customize your UINavigationBars.  First, be sure to check out the companion source code for this article.

Second, there’s a good discussion at Stack Overflow on the “correct” way to achieve these customizations.  There are other techniques, for instance you could create an Objective-C category and override drawRect: there.  I don’t recommend this approach for several reasons, though the most practical is that you need to be more careful that you are only overriding the UINavigationBars in your app that you actually intend to.  Note this is also known as “method swizzling” which can be quite dangerous if you’re not careful.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

September 29th, 2011 at 3:50 pm

SimpleGeo on the iOS: Getting Started Tutorial

with 6 comments

Mobile apps that are geographically-aware are all the rage these days, and there’s a number of ways to jump into this new geo-world we live in.  One of the more interesting ones is SimpleGeo.  They’re a startup in San Francisco that provides a number of APIs for doing geo-related things (like geofencing, and reverse geocoding which we’ll take a stab at today).

Although they have a pretty decent iOS framework, there is next to zero documentation on how to use it.  So I thought I’d write a simple app demonstrating how to do a reverse geocode (and retrieve the current weather) using SimpleGeo.framework.  Here’s the process:

  1. Get the current lat/lng using the built-in iOS Core Location framework.
  2. Create an SGPoint using the lat/lng.
  3. Setup SimpleGeo with your API keys
  4. Pass the SGPoint to getContextForPoint, with ourselves as a delegate

An SGContext will give us a whole slew of information, including the reverse geocoded address for our lat/lng (among the data is also weather information at this point and more).

A Little Background First

Why would we want to use SimpleGeo to do a reverse geocode in the first place?  What the heck is a reverse geocode?  And why does everything have the word “geo” in it all of a sudden?

Well, first of all, a “geocode” is the process of taking a postal address and converting it to latitude and longitude coordinates.  When we’re dealing with a phone that has a built-in GPS, however, we’re going to be getting only lat/lng points from our device.  It will then be up to us to figure out what the address is.  Hence, a “reverse geocode”.

As for why we’d want to use SimpleGeo to do this reverse geocode?  Built into the iOS is a reverse geocoder that uses Google Maps to achieve similar results.  For many people this is sufficient; however for others, it creates problems.  For one thing, you must be willing to agree to Google’s Maps TOS to use the service and be restricted by their rate limits (at the time of this writing they allow 2,500 requests per day).  SimpleGeo has a very handy API access monitoring and rate limiting system, and you can optionally pay for additional access as you need it.  Their free plan is also quite generous: our simple app will be able to do 10k requests per day.

Getting Started

To get started, create a new iOS project in Xcode.  You’ll need to add the YAJL and SimpleGeo frameworks along with:

  • Foundation
  • UIKit
  • CFNetwork
  • SystemConfiguration
  • MobileCoreServices
  • CoreGraphics
  • libz
  • MapKit
  • CoreLocation

Note that MapKit and CoreLocation are not listed as dependencies in the SimpleGeo.framework README file, but they are required (I submitted a pull request for the documentation – hopefully it will be updated).

Once you’ve got an app that builds using the framework, you next need to setup your developer access at www.simplegeo.com.  Create an account, login, and grab your consumer and secret keys.  We’ll need those in a moment.

Where Am I?

If you’re not already familiar with Core Location, it’s fairly easy to get started with.  I won’t rehash all the details here, but the general idea is that you need to register to receive location events and then act on them.  One tip that many people forget: be sure to turn off location updating to conserve power when you don’t need it.

First up, let’s setup our CLLocationManager and set ourselves as the delegate.  For my demo app, I’m doing this as part of the viewDidLoad, but you should really consider moving this (say, into its own singleton) if you plan to use it throughout your app:

self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;

Next, since we’ll be the delegate, we’ll want to implement the didUpdateToLocation method (as well as the error handler):

- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation {
	// wait until we are within a certain level of accuracy
	if(newLocation.horizontalAccuracy &lt;= 70.0f) {
		[locationManager stopUpdatingLocation];
	}
 
	// newLocation.coordinate.latitude
	// newLocation.coordinate.longitude
}

This method is called in response to the CLLocationManager getting a new location.  We first check that the accuracy is acceptable to us (remember that the accuracy can change depending on which method the device is using to determine location, as well as how many GPS satellites it can acquire, etc), then turn off updates.  I chose 70 (as in 70 meters) as that is what the simulator returns, but hopefully we can get more accurate than that.

Of course, we’ll want to tell the CLLocationManager to start updating.  I added this in the viewWillAppear method:

[locationManager startUpdatingLocation];

To be safe, I also added a stopUpdatingLocation to the viewWillDisappear (for the case where we can never get a good-enough accuracy before the user navigates to another screen).

Setting Up SimpleGeo

The first step to using SimpleGeo is to initialize an SGClient with your API credentials (you did set that up already, right?).

self.sgClient = [SimpleGeo clientWithDelegate:self
consumerKey:@"CONSUMER_KEY"
consumerSecret:@"SECRET"];

You can now use sgClient to subsequently get the SGContext information for the lat/lng we received from CoreLocation.  First, however, we need an SGPoint:

SGPoint* myPoint = [SGPoint pointWithLatitude:newLocation.coordinate.latitude longitude:newLocation.coordinate.longitude];

Retrieving SGContext

Now that we have our lat/lng and an SGPoint, the next step is to ask SimpleGeo for details about it.  This can be done by querying for an SGContext, which will return all sorts of interesting details, including:

  • Street address (if known), province, country, etc
  • Street intersections
  • School district information
  • Voting precinct and government type
  • Time zone
  • And weather

Each response is slightly different depending on what SimpleGeo knows about the location so be sure you structure your results code to handle situations where expected information isn’t available.

To get context information, we need to do two things: first, implement a delegate to handle the asynchronous callback from SimpleGeo.framework, and then to actually request information.  When a context has been retrieved, SimpleGeo.framework will attempt to call the didLoadContext method:

- (void)didLoadContext:(NSDictionary *)context forQuery:(NSDictionary *)query {
}

Here’s how to kick off the request:

[sgClient getContextForPoint:myPoint];

When didLoadContext get’s called, the context dictionary contains all the goodies SimpleGeo has on your location, and this is where you’ll find things like your street address and weather.

Hope you found this helpful.  I’ve also created a demo iOS app with this code – just drop in your API credentials and check it out.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

August 1st, 2011 at 11:27 am

Posted in Tutorials,UI

Creating Great iPhone and iPad Mockups

with 5 comments

Building great iOS apps starts with a set of great mockups. For anything but the most trivial of apps, having a good set of blueprints can be enormously helpful, allowing you to refine the user experience before jumping into the code. Too many people either skip this step or don’t know how to do it effectively. Although going through this process requires some investment up front, the dividends often justify it.

In my first post for iOS Dev Notes, I’m going to walk you through the key points of building an iOS application’s wireframes. First, we’ll go through some background info on wireframes and mockups, then we’ll start creating our own.

Workflow

Process to create iPhone/iPad wireframesThe power of wireframes is speed: when you have the right setup and tools, you can often mockup an entire iPhone or iPad app in an afternoon or so. Then you can iterate on your mockups until everyone is satisfied. Adding screens, tweaking graphics and layout, and anything else is trivial compared to building the app.

When building wireframes, my workflow consists of the following steps:

  • Create wireframes, screen by screen, for the entire app using OmniGraffle. Each screen is a canvas, and as I go through I try to tell a story (more on this later).
  • Copy wireframes into Keynote and add annotations.
  • Solicit feedback and refine the wireframes.
  • Create comps

Ultimately the goal is to diagram every step in the app. Almost nothing is too small, including loading screens, alert messages, etc. I may even include a screen showing the iPhone Home screen with the app’s icon.

In my experience I’ve found that even the trivial screens (like alerts or loading screens) can be important to clients who are cognizant of the overall user experience. It’s also a great way to see where you may be overusing certain visual elements, like UIAlertViews.

Telling a Story

Good wireframes tell a story. If you’re thinking like a user – and not a programmer – when creating your wireframes I can promise that you’ll end up with a much better app. The easiest way to do this is to think of the path you’re likely to take through your app as if you were seeing it for the first time.

Let’s take the iPhone’s Mail application as an example. When you first open the app, should you be brought to the list of folders or to your Inbox? Where is the Compose button located? Do you include a button to retrieve messages from the server (if so, and you’re using a Compose button and a UINavigation bar, where are you going to put your refresh button)? Questions like these force you to think of the app’s layout and usability, which is crucial to creating good mockups.

Each wireframe should flow into the next. To accomplish this, I like to include a small icon showing a finger tapping the screen (or some other relevant gesture). See the graphic below:

iOS mockup progression

By the end of this process, you’ll probably have numerous nearly duplicate wireframe screens showing seemingly trivial things, like loading screens. But starting at the first Canvas and flipping through one-by-one, you should be able to tell exactly what the user is doing in the app, tap-for-tap.

Getting Started with OmniGraffle

The first tool we’re going to discuss is OmniGraffle (about $99) for the Mac. There are a number of other tools out there, but I’ve found this one to be the most intuitive while allowing me to create both wireframes and comps. The power of OmniGraffle is really in the speed with which you can create these documents: just drag-and-drop graphical elements onto your canvas to build your screens.

You can get started with OmniGraffle with a free trial at their website.

Before we do anything, we need to get the right stencils for OmniGraffle. Stencils are like graphics libraries with common elements to build your iOS interfaces. There are a number out there, but here are the ones I recommend you get started with:

To install a template, just download it from the link above, uncompress it, and double-click the file. Then, within OmniGraffle, click the Install button (on the right, in the Stencils window).

OmniGraffle canvas settings for iPhone wireframesOnce we’ve installed our templates, we now need to setup OmniGraffle to have the right canvas size. As you’ll see in later steps, I don’t use OmniGraffle to print out wireframes, so I’m going to tweak the canvas size without worrying about fitting it to a printer page.

Open the Inspector to edit the Canvas Size. With the stencils we just installed, I’ve found that a canvas size of 576pt by 764pt seems to work well. If you want to keep everything in one document, rather than using Keynote to add annotations, then you may want to consider setting up Landscape for the orientation and leaving room to the side of your wireframe. One tip: I usually set the Page Margins to 0 when doing my wireframes.

I’ll leave it up to you to decide what flow works best. As you do more of these wireframes, you’ll find a style that works well for you.

Creating a Wireframe

Use OmniGraffle layers for iOS mockupsLet’s get started with the first wireframe. Use the iOS Wireframe iPhone stencil and drag the iPhone stencil to your canvas. Center it on the canvas by dragging it left and right until it snaps to the center.

Next, drag the iPhone status bar (with the time, battery, and carrier) to the canvas and align it to the top of the iPhone’s screen.

In the left pane (called Canvases), click the Lock icon. You’ll notice that you can no longer select or edit elements on this layer. Add a new layer by right-clicking on the canvas image, or click the Add Layer button at the bottom of the Canvases window. You’ll see a new layer is added above Layer 1 (our background). This is where we’ll add most of our screen’s elements.

Next, drag a few elements over. For instance, I added a Navigation Bar to the top, a UITableView Grouped element to the center, and a Tab Bar to the bottom. It’s starting to look like a real wireframe!

An iPhone mockup created in OmniGraffle

Annotations

Now that you’ve got the hang of creating your individual screens, the next step is to put them together into a complete set of mockups. For this, I like to use Keynote (part of iWork on the Mac).

The idea here is to bring each screen into Keynote as it’s own slide. Using a landscape design, I then attach a set of bulleted points for each screen, explaining what the user sees and other important notes (especially things that may be going on behind the scenes and not visible to the user).

Completed iPhone wireframe with annotations

While in Keynote, I’ve found that keeping each screen simple and to-the-point works best. I don’t use any themes or other styling, and my mockups are almost entirely devoid of color. The goal is to force the viewer to use their imagination a bit to picture how this screen is going to look on the iPhone or iPad.

As I said earlier, there’s no requirement to use Keynote for this step. I like using it because I can easily re-order slides (and add title slides and other non-wireframe info) without cluttering up my OmniGraffle document. Find a workflow that works for you.

Iteration

The last step is to put it all together. Create your wireframes, package them into a story that walks a user through every corner of your iPhone or iPad app, and present it to them with usage notes. Let your mockups speak for themselves. I like it when they tell the entire story so I don’t need to say a word for my client to understand exactly how the app will work. If you’re doing client work, your wireframes can also double as a great Statement of Work/Scope supplement.

As you go through the initial diagramming process you’ll no doubt pick up on a number of rough areas that could use some improvement. Refine your wireframes (or at least point out the problem to your client and work with them to refine the experience). Redoing a few screens at this stage shouldn’t take more than a few minutes to an hour, whereas reworking screens once they’ve been coded can take days or longer.

Finally, keep getting feedback. Wireframes tell the user how the app is going to work, while the next step (comps) show the user how the app is going to look. Since you’ll be spending a lot more time building comps than wireframing, I suggest you focus on getting these screens as close to the finished product as reasonably possible.

In a later article, I’ll get into some more advanced wireframing topics, including tips on creating tricky iOS-elements like pager controls and custom cells, along with comps. Hopefully this is enough to get you started.


Looking to add push notifications to your iOS app? Check out PushLayer, a service from the authors of iOSDevNotes.

Bookmark and Share

Subscribe for updates

Fill out the form below to join my newsletter. I'll also send you a bonus copy of my ebook, "5 Steps To Becoming a Better iOS Developer".

Written by rtwomey

March 28th, 2011 at 3:15 pm

Posted in Tutorials,UI

Tagged with , , ,