Saturday, November 3, 2012

Clang Language Extensions: __builtin_unreachable()

Sometimes when programming, we encounter impossible situations. Unfortunately, the compiler isn’t as smart as we are. It can’t identify impossible situations like we can. For these situations, we can help the compiler by instructing it that a code path is impossible. For this we use __builtin_unreachable().

Let’s say we have an enum as follows:

typedef NS_ENUM(NSInteger, MyEnum) {
    MyEnumA,
    MyEnumB,
    MyEnumC,
    MyEnumD,
};

Now let’s say we have a switch statement that uses two of the four enum values, but the other two are impossible in this situation. Not including them in the switch will generate a compiler warning. However, including them doesn’t make sense. We can use __builtin_unreachable() to get rid of the warnings.

MyEnum value = // some value.

switch (value) {

    case MyEnumA:
          // Handle case A
        break;

    case MyEnumB:
          // Handle case B
        break;

    default:
        NSLog(@"Cases C and D are impossible");
        __builtin_unreachable();
}

We can also make a macro that we can use to mark a method as not implemented like the following:

- (NSString *)someMethod {
    NOT_IMPLEMENTED;
}

By using __builtin_unreachable(), this macro also makes it so we don’t need to return a bogus value to suppress the no return value warning. The macro looks like this:

#define NOT_IMPLEMENTED \
NSAssert1(NO, @"Method: %@ not implemented.", NSStringFromSelector(_cmd)); \
__builtin_unreachable();

Another perk of this macro is that it prints an error to the console including the name of the method that is not implemented.

Related to __builtin_unreachable() is the ‘noreturn’ attribute. This allows us to mark any function or method that once called, it will never return. This attribute is great for abort methods. We can mark methods like so:

- (void)gracefulAbort __attribute__((noreturn));

Let’s say our app encounters a terminal Core Data error and we need to gracefully terminate the app. We can encapsulate this into a method. This method obviously will never return since it will cause the app to close. By marking it with the noreturn attribute, we will gain the same benefits as if we did the following:

[self gracefulAbort];
__builtin_unreachable();

The above cases I described are few and far between, but when they come up, __builtin_unreachable() is useful. To learn more about this and other Clang language extensions, check out the clang docs.

Saturday, October 13, 2012

Clang Language Extensions: instancetype

Clang added several features to the Objective-C language. One of these features is the ‘instancetype’ keyword. When creating objects, we frequently use methods in the alloc and init families. They have a return type of id, but the compiler knows that they will return an object that matches the class being used. For example, [[NSString alloc] init] returns an NSString and [[NSMutableString alloc] init] returns an NSMutableString.

There are several methods that will automatically infer the return type from the method name. They must follow standard camel-case naming and start with the following words:

For class methods:
        alloc
        new

For instance methods:
        autorelease
        init
        retain
        self

For any methods that do not follow this naming convention, we use the keyword instancetype. Let’s say we have the following objects:

@interface Animal : NSObject

+ (instancetype)createAnimalWithName:(NSString *)name;

@end

@interface SnowLeopard : Animal

- (void)pounce;

@end

@interface MountainLion : Animal

- (void)roar;

@end

Now let’s say we create an object and call a method on it like so:

[[MountainLion createAnimalWithName:@"Maxwell"] roar];

If +createAnimalWithName: returns id or instancetype, then the compiler accepts this just fine. If it returns an Animal object, then the compiler gives the warning: “Animal may not respond to roar.”

So, when does instancetype have an advantage over id? Let’s say we do this instead:

[[MountainLion createAnimalWithName:@"Maxwell"] lastObject];

A MountainLion is clearly not an NSArray; however, the compiler will happily accept this if +createAnimalWithName: returns an id. It’s not until we run the app that the we find the error when our app crashes due to an unrecognized selector method exception.

In summary, the instancetype keyword helps the compiler help you know when something is wrong. If for any reason you need to know if the compiler supports this feature, you can use the macro __has_feature(objc_instancetype).

If you want to learn more about instancetype and other Clang features, check out the Clang docs.

Sunday, June 10, 2012

Linking Storyboards

Summary

Previously I wrote about best practices with UIStoryboards. Since then I have continued to explore the possibilities of storyboards. With today being the eve of WWDC 2012, I wanted to share one of the tricks I have learned.

A key practice of storyboards is to break them down into natural, reusable modules. The downfall to this decomposition is when we need to transition from one storyboard to another. Storyboards were supposed to save us from the tedious transition code, but decomposition brings some of it back. Fortunately, there is a way to remove this pain.

Before I go into any details, I want to start with a disclaimer. I am not going to label this as best practice…at least not yet. All good design patterns need to stand the test of time. I have not used this trick enough to know if it works in all cases. Also, with this week being WWDC, Apple may expand UIStoryboards to include segues between storyboards.

Implementing

You should first download my code from GitHub. Following along will help in understanding how everything works.

The first thing to do is build your storyboards as you normally would. It's best if you break them down into natural modules. The rule of thumb is, if you can give each storyboard a meaningful name, then you have probably decomposed them well.

Now that you have your various storyboards, you can begin connecting them. To do this, you need to identify where you will transition into a different storyboard. At each of these points, create a blank UIViewController. This will represent the view in the other storyboard you will transition to.

Next you need to create a segue to each of these surrogate scenes. You can choose a Push, Modal, or custom segue according to your needs. They should look something like the picture below:



Once you have your surrogate scene in place, we can make the magic happen. We get to use a little-known tool of Interface Builder. It is the User Defined Runtime Attributes tool. This tool is hidden away in the Identity Inspector (3rd tab of the utility view). Up until now, you have probably used this inspector only to set a custom class. It also allows us to set the values of any property of any view we may be using in the storyboard.

First, we need to change the surrogate's class to RBStoryboardLink. Don't forget to include my RBStoryboardLink class in your app. Next, we can add a couple properties instructing the link what to transition to. The first, and required, attribute is "storyboardName" This is the name of the storyboard file you are going to transition to (without the .storyboard extension). The second attribute is optional. It called "sceneIdentifier". This is the UIStoryboard identifier you have given to a scene in the destination storyboard. If you ignore this attribute, then RBStoryboardLink will simply transition to the first scene in the storyboard. This is often what you want. Your Identity Inspector should now look something like the picture below:



Explanation

RBStoryboardLink works much like a symbolic link in your file system. A symbolic link essentially looks and acts like file. Likewise, RBStoryboardLink looks and acts much like a storyboard scene.

The idea behind RBStoryboardLink is simple. At a high level, it does the following:
  1. Creates the destination scene.
  2. Adds the view controller as a child of itself using the iOS 5 container API.
  3. Copies all the destination scene's properties into RBStoryboardLink.
Conclusion

RBStoryboardLink is a clean, easy-to-use technique to link storyboards without leaving Interface Builder. With WWDC this week, Apple may release similar functionality built directly into Interface Builder. If not, expect to use this technique frequently in your projects.

Monday, May 7, 2012

UIStoryboards and UITabBarController

Previously I wrote about decomposing UIStoryboards into modules. One of the most natural modules are the tabs of a tab bar controller. Since segues can't cross storyboards, we can't use a UITabBarController in a storyboard. This means the UITabBarController must be written in code. I have put together an easy pattern for building UITabBarControllers in code.

UIStoryboard Category

The first step is to write a category on UIStoryboard. It will look something like this:

@implementation UIStoryboard (YourExtras)

+ (UIStoryboard *)searchStoryboard {
    return [UIStoryboard storyboardWithName:@"SearchStoryboard" bundle:nil];
}

+ (UIStoryboard *)mapStoryboard {
    return [UIStoryboard storyboardWithName:@"MapStoryboard" bundle:nil];
}

+ (UIStoryboard *)listStoryboard {
    return [UIStoryboard storyboardWithName:@"ListStoryboard" bundle:nil];
}

+ (UIStoryboard *)favoritesStoryboard {
    return [UIStoryboard storyboardWithName:@"FavoritesStoryboard" bundle:nil];
}

+ (UIStoryboard *)contactStoryboard {
    return [UIStoryboard storyboardWithName:@"ContactStoryboard" bundle:nil];
}

+ (UIStoryboard *)homeStoryboard {
    return [UIStoryboard storyboardWithName:@"HomeStoryboard" bundle:nil];
}

@end

The purpose of this category is to abstract away the storyboard file names. Should the storyboard name (or bundle) change, we will only need to change it in one place. Such methods are useful not just tabs but also for any storyboard module. For the purpose of tabs, each storyboard generally should have a UINavigationController as its initial view.

UITabBarController Category

The next step involves a category I wrote. It takes in an array of UIStoryboards, then instantiates each storyboard as a tab. It saves writing a little bit of code between each project.

@implementation UITabBarController (RBExtras)

+ (UITabBarController *)tabBarControllerWithStoryboardTabs:(NSArray *)tabs {

    UITabBarController * tabBarController = [UITabBarController new];
    NSMutableArray * instantiatedTabs = [NSMutableArray arrayWithCapacity:[tabs count]];

    // Instantiates each of the storyboards.
    [tabs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL * stop) {

        NSAssert([obj isKindOfClass:[UIStoryboard class]],
                          @"Expected UIStoryboard, got %@",
                          NSStringFromClass([obj class]));

        [instantiatedTabs addObject:[obj instantiateInitialViewController]];
    }];

    [tabBarController setViewControllers:instantiatedTabs];

    return tabBarController;
}

@end

This category can be found on my Github.

App Delegate Superclass

Next is an app delegate superclass I wrote. This saves a few more lines of code from each tab-based app.

@implementation RBAppDelegate

@synthesize window = _window;

- (void)setUpTabBasedAppWithTabs:(NSArray *)tabs block:(RBTabBarCustomizationBlock)block {

    NSAssert(tabs, @"No tabs given.");
    NSAssert([tabs count] >= 2, @"Insufficient number of tabs.");

    // Sets up the tab bar controller.
    UITabBarController * tabBarController = [UITabBarController tabBarControllerWithStoryboardTabs:tabs];

    // The block is called to allow customization of the tab bar controller.
    if (block) block(tabBarController);

    // Sets up the window.
    UIWindow * window = [UIWindow new];
    [window setScreen:[UIScreen mainScreen]];
    [window setRootViewController:tabBarController];
    [window makeKeyAndVisible];
    [self setWindow:window];
}

@end

This superclass completes the set up of the tab bar controller. It provides a block for any customizations of the tab bar controller. You can also find this on my Github.

Your App Delegate

The final step is to call the set up method. Don't forget to subclass RBAppDelegate first.

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

    // Creates the tabs.
    NSArray * tabs = [NSArray arrayWithObjects:
                                  [UIStoryboard mapStoryboard],
                                  [UIStoryboard listStoryboard],
                                  [UIStoryboard searchStoryboard],
                                  [UIStoryboard favoritesStoryboard],
                                  [UIStoryboard contactStoryboard],
                                  nil];

    [self setUpTabBasedAppWithTabs:tabs
                                                   block:
     ^(UITabBarController * tabBarController) {
         tabBarController.tabBar.selectedImageTintColor = [UIColor greenColor];
     }];

    return YES;
}

The block for customizing the tab bar controller is optional, but I have shown the potential use of such a block. In this case I am setting the color of the selected tab icons.

Conclusion

Within a few lines of code, we can have a tab-based app constructed from several storyboards. Other UIStoryboard patterns can be created using similar techniques.

Sunday, May 6, 2012

Reverse Segues

UIStoryboards make transitioning between views very easy. However, segues work strictly in one direction. I have been asked several times what is the best way to send information back to the previous view controller. Unfortunately, there is no such thing as a reverse segue. In response, I have found four clean, useful patterns to create the needed behavior:
  1. Delegate
  2. Block
  3. Shared memory
  4. NSNotificationCenter
Each one has its advantages and disadvantages. I will discuss each one in turn.

Delegate

Summary

Cocoa uses delegates everywhere, so why not here too?

Code Sample

Let's say view controller X pushes view controller Y via segue. Y specifies a delegate protocol that it will call to pass the needed information. X conforms to that protocol and sets itself as Y's delegate. When Y is dismissed, it calls the delegate method.

// In view controller X
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    [[segue destinationViewController] setDelegate:self];
}

- (void)objectUpdated:(id)object {
    // Do something with the object.
}

// In view controller Y
@protocol MyDelegateProtocol
- (void)objectUpdated:(id object);
@end

- (void)viewWillDisappear:(bool)animated {
    [super viewWillDisappear:animated];

    [[self delegate] objectUpdated:[self object]];
}

Pros/Cons

Delegation is easy enough to implement. However, in this case, the delegate protocol probably has just one method. This hardly seems worth the effort to create the protocol. Because of this, I do not recommend delegates. I instead favor the next option: blocks. If you need more than one callback, a delegate may be more appropriate.

Blocks

Summary

Blocks are great for making callbacks and can easily replace any delegate.

Code Sample

Now when X pushes Y, it also gives Y a block. When Y finishes, it calls the given block and hands it the information to pass on. The block can then do whatever it needs with that information.

// In view controller X
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    [[segue destinationViewController] setBlock:^(id object) {
        // Do something with the object.
    }];
}

// In view controller Y
typedef void(^MyBlock)(id object);

- (void)viewWillDisappear:(bool)animated {
    [super viewWillDisappear:animated];

    if (self.block)
        self.block(self.object);
}

Pros/Cons

Blocks have two great advantages over delegates. First, they don't require a protocol. For convenience, you may write a one-line block typedef, but that is all. Second, blocks keep code where it is relevant, rather in a different method. For most cases, you should favor blocks.

Shared Memory

Summary

Sometimes you may push a new view so it can edit an object. In this case, you can avoid a callback entirely.

Code Sample

When pushing Y, just pass it a pointer to the object of interest. Any changes Y makes to the shared object will directly affect X.

// In view controller X
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
    [[segue destinationViewController] setObject:[self object]];
}

Pros/Cons

This solution is the simplest of the four. However, it may not be suitable in some situations. One place this pattern doesn't do well is if you want view controller Y to have a Done and Cancel button. You would need to undo all the changes made to your object if the Cancel button is pressed. This could be made easier by using an NSUndoManager, but that may be overkill for what you want.

There is one particular place that this pattern is especially useful. When using Core Data with iOS 5, you may want view controller Y to have an NSManagedObjectContext that is a child of view controller X's NSManagedObjectContext. All you need to do is create the MOC in X, set the MOC's parent, and pass it on to Y.

NSNotificationCenter

Summary

Using NSNotificationCenter to pass around information is the least used option and the most work, but it can have some nice advantages worth mentioning.

Code Sample

Rather than have X tell Y it is interested in its information, X lets NSNotificationCenter know it is interested in an event.

// In view controller X
- (void)viewDidLoad {
    [super viewDidLoad];

    [[NSNotificationCenter defaultCenter] addObserver:self
                                                                          selector:@selector(consumeNotifcation:)
                                                                              name:MyNotification
                                                                             object:nil];
}

- (void)viewDidUnload {
    [[NSNotificationCenter defaultCenter] removeObserver:self
                                                                                    name:MyNotification
                                                                                   object:nil];
    [super viewDidUnload];
}

- (void)consumeNotification:(NSNotification * notification) {
    // Grab the information from the notification.
}

// In view controller Y
NSString * const MyNotification = @"MyNotification";
NSString * const MyObject = @"MyObject";

- (void)viewWillDisappear:(bool)animated {
    [super viewWillDisappear:animated];

    NSDictionary * userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
                                                [self object], MyObject,
                                                nil];

    [[NSNotificationCenter defaultCenter] postNotificationName:MyNotification
                                                                                           object:self
                                                                                       userInfo:userInfo];
}

Pros/Cons

Obviously this is a lot more code than the other options. It comes with two key advantages. First, this pattern makes multiple, unrelated observers very easy. Second, it allows for looser coupling between views. X and Y (and any other views/non-views) essentially don't know anything about each other. They simply send or receive information through NSNotificationCenter. One place I use this pattern is with an app-wide settings view. When the settings are changed, different parts of the app may need to know immediately when the change is made. For example, I may have a setting that can turn off downloading over the cell network. Both the networking code and the UI will need to know about this change. The notification makes sure that they are informed immediately without needing to hard code the interactions.

Conclusion

When passing information to a previous view controller, favor blocks. However, keep the other options in mind. They have their advantages too.

Other Information

For more information on UIStoryboards, check out my best practices.

Monday, April 23, 2012

Unique Email Channels

If you are anything like me, you probably have more than one email account. One account is only for trusted friends and colleagues. The other is for everyone else. I am always paranoid that I will get spammed from some service, like Facebook. Other times I want to get the newsletters or notifications from some site, but I may lose interest in them over time and want to quickly stop the emails.

The Problem

Every service I give my email address to needs to be handled differently. Gmail is really good about creating rules for handling emails. However, I don't know what email addresses I can possibly receive from. I also cannot feasibly create a rule for every email address a service has. For example, one web service alone may have a customer service email, a bug report email, a no reply email, and many others. This just won't work.

Another option is to create a new email account for every web service I use. I could have one email account each for Twitter, Stack Overflow, GitHub, etc. Creating an entirely separate email account is overkill though.

The Solution

Fortunately, there is a way. Many email providers allow for arbitrary tags to be added to an email address. For example, with Gmail, I can create an email account with the address my.email@gmail.com. Then, I can use my.email+twitter@gmail.com, my.email+stackoverflow@gmail.com, and my.email+github@gmail.com. All these email addresses will go to the same account. Most email providers have similar systems.

How it Works

You can think of the tags on an email address like a subdomain of a URL. The base email guarantees uniqueness and the tag allows for routing. These email addresses are known as disposable email addresses. I like to call them unique email channels. By giving every service a different email address, I can control the communication channel. If that email address is ever compromised by a spammer or I get tired of the emails, I just need to sever the channel.

Since all the unique email channels are delivered to the same email account, they are not immediately useful. We need to bring in the rule system. Gmail allows for rules to be based on the To: field. For example, I can have a rule that says, when I get an email sent to my.email+twitter@gmail.com, mark it as read, label it as Twitter, and archive it. I can have all sorts of complex rules that are specific to my needs for each service. I can even have Gmail forward an email to another email account.

Personally, I have one email account that just handles mail sorting. It takes in email from all my services, marks them as read, labels them appropriately, and archives them. If I get an email from a service I am interested in, I forward it on to my real email account. This allows me to build up an archive of newsletters and notifications, but I never see them if I don't want to. When I get tired of seeing a notification, I don't cancel it. Instead, I just change the rule. The rule system keeps the interface to all my notifications consistent. This way I don't need to dig through some website trying to figure out how to cancel my subscription.

Potential Problems

What if some spammer gets smart and decides to remove the tag? This problem is easy to solve. Just create a rule that deletes all emails sent directly to my.email@gmail.com.

What if some spammer gets smart and tries to guess which tags I use? This is much harder to protect against, especially since +twitter or +github would be easy to guess. Using unique email channels has been around for years and is still not in widespread use. I don't foresee this becoming largely popular to the general user anytime soon. So, there is little chance that someone will attempt this.

If that is not comforting enough, you can use tags that are not easily guessable. For example, you could use +apple20934 or +i.love.steve.jobs for your unique Apple email channel. This is admittedly security through obscurity, but it makes guessing channels even less likely. It's a similar security problem as passwords. Again, it is highly unlikely that spammers will be guessing email channels anytime soon.

If you know what exactly email address(es) you will receive from, you can have rules that only filter if both the To: and From: fields match your expectations. This almost guarantees safety from spammers, but may not be feasible for all situations. This approach could be foiled by worms or email spoofing, but again it would be hard to guess your rules. The key to security is to make spamming you more effort than it's worth. Spammers have plenty of low hanging fruit to spend their time on.

Advantages
  1. Better spam protection than Gmail or any one service alone can provide.
  2. Specific email handling rules for each service.
  3. Don't need to tell your friends that you made a new email address if your email address is compromised by spammers.
  4. Don't need to hunt down the subscription options to cancel email subscriptions.
Disadvantages
  1. If you want to change the types of email notifications you receive from a service, you still need to go to the website.
  2. It takes a little bit of time to set up a rule when creating a channel for a new service.
  3. It takes a lot of time to change all your existing services to use your new, unique email channel.
Conclusion

Though it does take some time up front to set up, unique email channels are worth it for anyone who is paranoid about spammers or wants to automate how their email is handled. I expect to see similar patterns emerge in other communication systems as the event-driven web and personal cloud networks continue to develop.

Wednesday, April 18, 2012

PaintCode App Review

With the new iPad and its Retina Display, iOS apps are getting even larger due to the various sizes of assets. When a user downloads your app from the store, you want them to get it quickly. You especially don't want them to be forced to download from wifi if you can help it. Fortunately, a new app called PaintCode has been released just in time by PixelCut to help remedy this problem.

Summary

PaintCode is a vector image editor. What makes it special is that PaintCode generates Core Graphics code for both Mac OS X and iOS. The idea is that we can draw many of our graphics rather than include a series of rasters in the app's main bundle. PaintCode also serves as a bridge between graphics artists and developers. The graphics artist can generate the assets using PaintCode, then the developer can take the generated code and tweak it as needed.

Test Run

To really see if PaintCode could fill my needs, I stress tested it with a fairly complicated image: the Siri button. The Siri button touches nearly every feature of PaintCode, including arbitrary shapes, linear/radial gradients, and inside/outside shadows.


Within a few hours, I had all the intricate details worked out. I consider myself an amateur graphics artist, but PaintCode made the process very easy. Most of the controls are intuitive and easy to learn. The provided examples and videos also helped.

I then put the code to the test. I simply dropped the code into a custom UIView. To make things more interesting, I applied some affine transformations.


I found that the transformations worked perfectly with everything except, inside shadows. The inside shadows will translate properly, but won't rotate or scale properly. I am not entirely sure why they don't work. I expect this bug will be fixed in a future release.

Pros

Again, PaintCode made generating vector assets very easy. I tried out the trial of a competing app, Opacity, but I found PaintCode to be much more intuitive. I also appreciate the great attention to aesthetic detail, yet minimalistic style.

Everything in PaintCode is drawn using Core Graphics. So, whatever you see on the canvas is exactly what will appear in your own app.

The generated code may not be perfect, but it's pretty good. The advantage of PaintCode is it does the hard, tedious work. Once the image is finished, a developer can easily tweak details as needed to make the drawing more dynamic.

Cons

Though PaintCode generates some good Core Graphics code, it is not truly resolution independent. You will notice this particularly with the radial gradient on the button. I wanted to alternate colors about once every pixel or two. PaintCode only allowed 21 discrete steps for a gradient. Furthermore, since the number of steps is fixed, changing the scale changes the appearance. Fortunately, this can be remedied by tweaking the generated code. A simple for loop can dynamically change the number of steps as the scale changes.

As any graphics artist knows, vector images are not a silver bullet. There are some relationships that are not 1:1 linear transformations. For example, one object may scale twice as fast as another. The scale could even be x2. This scaling problem can be fixed in code though. Also, when the size changes, details often need to be added or removed. The code could be modified to do this automatically as the scale changes, but this is probably outside the scope of PaintCode and may be more trouble than it is worth.

My Wishlist

If there is anything I could have added to this app, it would be import SVG. Every graphics artist has his or her favorite vector editor. It would be amazing if PaintCode could import an SVG file (or AI file) and convert it to Core Graphics code. I have no doubt such a feature would be a large undertaking, but if PaintCode could do this, it would be worth so much more than $80.

Recommendation

If you have an app that is getting large, you should really look into getting PaintCode and converting some of your assets to Core Graphics. Or if you are trying to draw something in Core Graphics and find debugging too hard, PaintCode will make save you many hours.

Code

If you want a copy of my PaintCode file or Xcode project, you can find them on my GitHub account.

Disclaimer

I received a copy of PaintCode free for the purposes of reviewing. However, I do not receive any compensation if you purchase PaintCode.

Tuesday, March 6, 2012

Hidden iOS Simulator Hotkeys

Apple's iOS Simulator app is a helpful tool in building iOS apps. However, there is more than what appears on the surface. Recently a hack has been discovered to allow iPad Retina Display testing. I have also found a couple hidden features. As far as I know, these hidden hotkeys only work on the iOS 5 simulator.

The first, and least interesting, is the "Toggle Slow Animations" feature. This feature slows down animations making it easier to see the fine details. I've found this useful when "debugging" animations. This feature is normally toggled through the drop-down menu: Debug > Toggle Slow Animations. However, it can also be toggled by double tapping Cmd+Shift. If you ever have slow animations mysteriously turn on, then you may have accidentally activated this hotkey.

The second hotkey is more peculiar. I often use the hotkey Cmd+Shift+4+Space to take screenshots of the simulator. I noticed that sometimes a popup would show up that displays the keyboard localization languages I have turned on. It took a while, but I can almost consistently reproduce it. I first disabled the screenshot hotkeys:  (Apple) > System Preferences > Keyboard > Keyboard Shortcuts > Screen Shots > Uncheck all hotkeys. Then I opened the iOS simulator and used the hotkey Cmd+Shift+4+Space. Sometimes it takes a few tries. Other times I need to open/close an app and try again. Again, this hotkey only appears to work on the iOS 5 Simulator. You may also need to have more than one keyboard language activated.


When the keyboard language popup shows up, you can click on of the languages to change your keyboard language. The effect is not immediately apparent unless you have the keyboard visible while changing languages.

I'm not entirely sure why this hidden feature exists in the simulator. If the multiple keyboards are setup, they can be easily changed using the language key on the keyboard. What is more interesting is Apple likely has many other hidden features.

Friday, January 13, 2012

UIStoryboard Best Practices

UIStoryboard is a hot topic right now in iOS. However, there have been many misconceptions on the topic. The first reaction to storyboards (what I have seen anyway) is that they are a panacea for all situations. Following that reaction I saw many people claim that storyboards are awful and broken. The truth is, both these ideas are wrong. During WWDC 2011 Apple may have over-promoted storyboards, thus giving the wrong impression and leading to unmet expectations. Let me correct this now: UIStoryboard is not an all-purpose solution, it is another tool for your toolbox.

My intent here is to provide the best practices for UIStoryboard. I will list my lessons learned, but there are probably more yet to learn. I expect over time that this list will expand to include other best practices for UIStoryboard.

Break your storyboard into modules

The first mistake developers have been making with storyboarding is producing large, monolithic storyboards. A single storyboard may be suitable for small apps but certainly not for large apps. Don't forget one of the key principles of programming and abstraction: decomposition. We decompose our code into modules to make them more reusable and reduce maintenance costs. The same is true for user interfaces, and it applies whether you are using UIStoryboards, XIBs, or anything else. There is an even deeper principle wanting to emerge: when code and user interfaces are decomposed together, then you have truly useful and reusable code, user interfaces, and products (you can quote me on this).

The first question to ask is "how do I identify the natural modules of my app?" Let's start with something easy. Many apps are based on a tab bar controller. Each tab is a natural module and you hence you can put them in their own storyboard. Next, if you have two tabs (or any two views for that matter) with a segue to a common view, then that common view (and its following hierarchy) is almost certainly another natural module. Once you have pruned your view hierarchy, you will probably find that each module can be given a name. For example, you may have your login storyboard, settings storyboard, about storyboard, main storyboard, etc. If you can give a fitting name to each module, then you have decomposed your hierarchy well.

Keep in mind that having a single view in a storyboard is not a bad thing. This is particularly true with table views. The power of static and prototype table view cells is very useful.

You should also note that breaking storyboards into modules makes them more friendly to version control and sharing among a team.

You don't need to convert everything at once

When you first saw storyboards, you may have had an urge to convert everything over to UIStoryboard. I know I did. Be aware that this does take time and you may not gain any immediate benefits from switching everything. Fortunately, you don't need to do it all at once. You can switch over the parts of your app that will benefit the most now. Then over time you can slowly switch out others.

Make custom UITableViewCell subclasses for prototype cells

Probably the best feature of UIStoryboard is prototype cells. They make custom table view cells easy. Prototype cells can be easily customized; however, the UITableViewCell class may not specify the IBOutlets you need. A custom subclass also gives you another advantage. You can create a -setupWith<#Object#>: method. This takes your setup code out of your table view controller and into the cell itself. This is where you can gain some great reuse.

Not only can you create custom UITableViewCell subclasses, but you can have one subclass service many prototype cells. For example, you may have a cell that takes a Person object. In one table view your cell may have main label on the left and a detail label on the right. A different table view may use the same cell but have a main label above and a detail label below. Your cell subclass handles the content, but the prototype cell handles the look and feel. Again we gain great reuse and flexibility by decomposing code and views together.

Don't forget about IBAction, IBOutlet, and IBOutletCollection

Segues are nice and can do a lot, but don't forget about the other IB fundamentals. Segues can't cross storyboards, but IBActions can. IBActions can also clean up an unnecessary spaghetti segue mess and reduce the need for custom segues. Everything has their place and their use; they are all tools at your disposal.

While on the topic of IBActions and family, I should cover encapsulation. Most, if not all, your actions and outlets do not need to be public. So, rather than put them in your public headers, put them in a class extension. See here for more details. Xcode/Interface Builder didn't handle this well in the past but now there are no problems. This keeps your interfaces much cleaner and easier for others to understand.

Don't forget about XIBs

Even though storyboards are very similar to XIBs, they still have a place. Simple, one-view modules may be better represented as XIBs. Also, XIBs can hold custom views without an associated view controller. UIStoryboard requires that view controllers be the basic unit.

You don't need to have a main storyboard

You may have noticed your app settings now has a "Main Storyboard" setting. To use UIStoryboard you are not required to use this. In fact, my latest project has neither a main storyboard nor a main XIB. It uses a programmatic UITabBarController. From there I load all my tab storyboards. The beauty of it all is that it doesn't matter whether I use UIStoryboards, XIBs, or code. All three options are flexible enough to work with each other in any combination.

Specify the name of your app delegate in main.m

As I mentioned the other day, you may need to change your main.m file if it doesn't specify your app delegate's name. It should look something like this:

UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]))

Put your storyboards in a category

To get one of your storyboards, you need to call +storyboardWithName:bundle:. Be wary any time you need to hard code a string for a key, especially one that can change frequently. Should you ever need to rename your storyboard, you will have to change all references to that storyboard. My favorite way to handle this is with a category. It looks something like this:

@implementation UIStoryboard (MyApp)

+ (UIStoryboard *)mainStoryboard {
    return [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
}

@end

The benefit of the category approach is that if you ever rename a storyboard, you only need to change the hard coded string in one place. It also makes your code more readable.

Summary

If you take anything from this post you should remember these:
  1. Decompose your code and your views.
  2. UIStoryboard is a great tool, but it's not the only tool.
I hope these tips and best practices help promote proper use of UIStoryboard. I invite all my readers to let me know of other best practices they encounter.