Earlier this year we launched a brand new revision of our iPad application. With lots of new features like our show and video discovery panel, mini player, etc, we had to think very hard about speed and stability of both the software we wrote as well as of the development process. With a few months behind us now, we wanted to introduce some of the interesting technical tidbits from our product.
Building the UI
At Hulu, we choose not to use the interface builder for our apps. Traditionally, we’d coded the positions and resizing logic for each view. While this is frequently a lot of labor, we felt it still left us with better control over the results than using a mouse to drag buttons. Using this approach you can keep your views under control when the view is simple, or you’re adding a few small features. With a big redesign, which included many different new pages and components, aligning everything manually with code becomes a lot less fun – not to mention challenging as the design is often tweaked after observing actual on-device user interaction.
There has a be a better approach, and the most interesting one for us was autolayout -- which Apple introduced along with iOS 6. While awesome, we ran into two problems: (1) it works for iOS 6 and above, while we still support iOS 5; (2) it's designed primarily for interface builder users, so it is little harder to use programmatically.
Then we looked at one small interesting part of the autolayout: the ASCII-art-like Visual Formatting Language (VFL). That eventually became our solution: we would use VFL, but not autolayout. Our solution is now released as an open source project called vfl2objc.
How does it work? We built a system to generate Objective-C code based on a VFL-defined layout. This allowed us to retain iOS 5 compatibility while keeping the layout instructions easy to read and easy to modify. The approach is performant because most of computations are done at compile time (except the magic behind the autoresizing mask, but that is relatively simple). It is also very flexible: you can call any sizing or positioning APIs before or after the VFL-generated code block -- unlike autolayout, which ignores the frame you manually set, our VFL code generation tool won't.

For example, you can go the loadView method of a view controller class, and add the following:

// VFL begin
/*
|-30-[label]-30-|
|-50-[button(100)]
V:|-10-[label(20)]-15-[button(50)]
*/
// VFL end

Then, run our vfl2objc script. This will generate the following:<p></p>

// --- VFL GENERATED CODE ---
/*
|-30-[label]-30-|
|-50-[button(100)]
V:|-10-[label(20)]-15-[button(50)]
*/
{
// You need to predefine superview before this.
CGRect frame;
frame = label.frame;
frame.origin.x = 0 + 30;
frame.size.width = superview.bounds.size.width - (0 + 30) - frame.origin.x;
frame.origin.y = 0 + 10;
frame.size.height = 20;
label.frame = frame;
label.autoresizingMask |= UIViewAutoresizingFlexibleWidth;
label.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
[superview addSubview:label];
frame = button.frame;
frame.origin.x = 0 + 50;
frame.size.width = 100;
frame.origin.y = 0 + 10 + 20 + 15;
frame.size.height = 50;
button.frame = frame;
button.autoresizingMask |= UIViewAutoresizingFlexibleRightMargin;
button.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
[superview addSubview:button];
}
// --- END OF CODE GENERATED FROM VFL ---

And triggering the script, native UI alignment code will emerge between VFL begin and VFL end. They are inside curly braces so that you can fold them, and look at just the VFL part. If your design changes you can just edit the VFL definition and run the script again. Of course you can also automatically trigger the generation process with an Xcode pre-build script.<p></p>

To learn more about vfl2objc, please visit http://github.com/hulu/vfl2objc

Defining Events
While NSNotification is something very handy, it is sometimes bug prone because of the nature of weak typing. For example, when you post a notification, the compiler cannot check if the notification name actually matches the name expected by the observers. Also any information passed along the notification is either a weakly typed object, or a dictionary. In order to reduce the incidence of bugs you need document the notification clearly, and always keep this documentation up to date even as your application changes. Decoupling documentation from implementation often leads to people updating one but not the other, thus becoming the source of problems.
To deal with this issue, we introduced a replacement for NSNotification -- the HUTypedEvents. The basic idea is to use a protocol, plus a method signature, to represent an event. This allows the compiler to check both the event name and parameters.
For example, if we have an event PlaybackStart with integer parameters videoID and showID. If we were to do it with NSNotification, the code to define it will be:
const NSString *PlaybackStartNotification = @"PlaybackStart";
const NSString *PlaybackStartVideoIDKey = @"PlaybackStartVideoID";
const NSString *PlaybackStartShowIDKey = @"PlaybackStartShowID";

In order to post the event, somewhere inside the playback component we need to call:

[NSNotificationCenter defaultCenter]
postNotificationName: PlaybackStartNotification
object: nil
userInfo: @{PlaybackStartVideoIDKey: [NSNumber numberWithFormat:videoID],
PlaybackStartShowIDKey: [NSNumber numberWithFormat:showID]}];

Anyone who needs to handle this event will need to do:

// during init
[[NSNotificationCenter defaultCenter]
registerObservorForNotificationName: PlaybackStartNotification
selector:@selector(handlePlaybackStart:) object:nil];
...
// implement handler
- (void)handlePlaybackStart:(NSNotification *)notification {
int videoID = [[notification.userInfo
objectForKey:PlaybackStartVideoIDKey] intValue];
// some logic based on video ID
...
}

With HUTypedEvents, this is both simpler and safer. Create a singleton class, for example CentralEventHandler. Declare the event at the beginning of CentralEventHandler.h outside the class interface:

HUDeclareEvent(PlaybackStart, videoID:(int)videoID showID:(int)showID)

In the class interface, add:

HUDeclareEventRegistration(PlaybackStart, videoID:(int)videoID showID:(int)showID)

In the implementation for the class, simply add:

HUImplementEvent(PlaybackStart, videoID:(int)videoID showID:(int)showID)

Then, to trigger this event, call:

[[CenterEventHandler instance]
handleEventPlaybackStart__videoID:videoID
showID:showID];

And to handle this event, let the class conform to protocol EventPlaybackStart, and then do:

// during init
[[CentralEventHandler instance] registerEventPlaybackStart__observer:self];
...
// implement handler
- (void)handleEventPlaybackStart__videoID:(int)videoID showID:(int)showID {
// logic based on videoID and showID
}

This approach provides the following advantages:<p></p>

* When you trigger the event, you don't have to look up or remember the event name, parameter names, or paramter types.
* You don't have to convert primitive types into objects.
* Whenever you are triggering or handling your event, Xcode will provide autocomplete on the method and parameter names.
* Whenever you register to receive an event, the compiler will check whether the class conforms to the right protocol, and will ensure that you implemented the right handler method with the right parameters and types.
* If you want to find every use of this event -- both where it's fired, and where it's handled -- you can peform a global code search on "handleEventPlaybackStart". The search results are much cleaner than when you attempt to do the same with NSNotification and search for PlaybackStartNotification.
To learn more about HUTypedEvents, please checkout http://github.com/hulu/HUTypedEvents
Text Rendering
We’ve upgraded GSFancyText, our open-source rich text rendering framework. We moved all the markup text parsing, line breaking, and alignments to a background thread, and only do the final drawing on main thread. Since our new application has a lot of scroll views that contain lots of images and rich text labels, this new approach is much more performant.
The library's usage is similar to the older version, except that you need to explicitly call updateDisplay after a GSFancyTextView object is constructed. In case you need to align some other UI based on the size of a GSFancyTextView obtained from the asynchronous calculation, you can use a new method called updateDisplayWithCompletionHandler.
The vfl2objc system, HUTypedEvents, and the updated GSFancyText are just a few examples of integrating little, fun technical improvements into our big iPad app redesign project. During the process of building the Hulu iOS application, we are constantly looking for better ways to make it easier to build a nice looking, maintainable, and performant application. We're happy to share some of our learnings and code with the wider development community.

Bao Lei is a senior software developer on the mobile team who works on our iOS platform.