Under the hood: Rebuilding Facebook for iOS
Today we released a new version of Facebook for iOS that's faster, more reliable, and easier to use than ever before. The development of this new app signals a shift in how Facebook is building mobile products, with a focus on digging deep into individual platforms. To understand how we approached this shift, let's take a look at how Facebook has evolved on mobile.
Early versions of Facebook for iPhone were built when iOS was in its infancy. There was no iPad and iOS wasn't even called iOS. Out of those early versions of the app, we built an open source framework called Three20: a library of handy components that didn't exist in iPhone OS at the time. Over the years Three20 became one of the most popular open source projects for the iOS community and solved many problems for us, but it also started to feel long in the tooth. Feature creep had begun to set in and new developers using the framework were confronted with an ever-steeper learning curve; as the core of iOS quickly improved, certain parts of Three20 were less relevant. In light of this, the new Facebook for iOS marks our first release in years without the Three20 framework.
Scaling up with HTML5
As mobile usage exploded over the last few years, our priority was to ensure that regardless of device, platform, network, or region, Facebook users had a good experience on their mobile devices. To support thousands of devices and multiple mobile platforms, we leveraged HTML5 to build and distribute Facebook mobile experiences across iOS and other platforms.
By allowing us to write once and ship across multiple platforms, HTML5 has historically allowed us to keep the Facebook mobile experience current and widely available, and has been instrumental in getting us to where we are today. We chose to use HTML5 because not only did it let us leverage much of the same code for iOS, Android, and the mobile web, but it also allowed us to iterate on experiences quickly by launching and testing new features without having to release new versions of our apps.
So while utilizing web technology has allowed us to support more than 500 million people using Facebook on more than 7000 supported devices, we realized that when it comes to platforms like iOS, people expect a fast, reliable experience and our iOS app was falling short. Now that our mobile services had breadth, we wanted depth. So, we rewrote Facebook for iOS from the ground up (I really did open up Xcode and click "New Project") with a focus on quality and leveraging the advances that have been made in iOS development.
(Re-)Building for speed
One of the biggest advantages we've gained from building on native iOS has been the ability to make the app fast. Now, when you scroll through your news feed on the new Facebook for iOS, you'll notice that it feels much faster than before. One way we have achieved this is by re-balancing where we perform certain tasks. For example, in iOS, the main thread drives the UI and handles touch events, so the more work we do on the main thread, the slower the app feels. Instead, we take care to perform computationally expensive tasks in the background. This means all our networking activity, JSON parsing, NSManagedObject creation, and saving to disk never touches the main thread.
To give another example, we use Core Text to lay out many of our strings, but layout calculations can quickly become a bottleneck. With our new iOS app, when we download new content, we asynchronously calculate the sizes for all these strings, cache our CTFramesetters (which can be expensive to create), and then use all these calculations later when we present the story into our UITableView.
Finally, when you start Facebook for iOS, you want to see your news feed, not a loading spinner. To provide the best experience possible, we now show previously-cached content immediately. But this introduces a new problem: If you have a lot of stories in your news feed, UITableView throws a small spanner in the works by calling the delegate method -tableView:heightForRowAtIndexPath: for each story in your news feed in order to work out how tall to make its scrollbar. This would result in the app loading all the story data from disk and calculating the entire story layout solely to return the height of the story, meaning startup would get progressively slower as you accumulate more stories.
The solution to this particular problem has two main parts. Firstly, when we do our initial asynchronous layout calculations, we also store the height of the story in Core Data. In doing so, we completely avoid layout calculation in -tableView:heightForRowAtIndexPath:. Secondly, we've split up our "story" model object. We only fetch the story heights (and a few other things) from disk on startup. Later, we fetch the rest of the story data, and any more layout calculations we have to do are all performed asynchronously.
All this and more leads to high frame rates while scrolling and an app that remains responsive.
New Foundations: Messenger and beyond
Developers always have to deal with constraints. Some are technical, some design, and some are driven by product requirements. We began rebuilding Facebook for iOS while a new native app - Facebook Messenger - was starting to gain traction in the wild. Our challenge was to completely incorporate both the foundations and UI of Messenger, and in doing so take advantage of all the heavily-tested code the messages team works so hard on. When you tap "Messages" in Facebook for iOS, you're seeing the same code that runs as a standalone app in Facebook Messenger.
To accomplish this we built a system of modules. Modules provide view controllers that are presented when you tap a bookmark in the left navigation menu. News Feed, Messages, Friends—they're all modules. Modules also specify their dependencies. For example, we use MQTT to update notifications, messages, and bookmarks. At application startup, we walk the dependency graph and ensure that our MQTT service has started before we start listening for new notifications. Even as we add new features, our modular system ensures that our application setup happens in the right place, at the right time.
While modules get us part of the way there, Messenger couldn't simply move all existing code over to a new core overnight. The new authentication system in Facebook for iOS and the separate implementation that still exists in Messenger use objects that share a common interface (they "conform to the same protocol," in Objective-C parlance). In conjunction with the Messenger team, we built a dependency injection system that provides Messenger code with the objects it uses to authenticate at runtime. When running as a standalone app, the Messenger code is provided with the Messenger implementations of these objects; when running as a module under the new Facebook for iOS, it's provided with a different implementation. The code that uses these objects is none the wiser.
Planning for the future
At the moment, stories in news feed consist of headers with profile pictures and timestamps, messages, photos, videos, the "Like" and "Comment" buttons, and a whole lot more. HTML5 allowed us to add these these features and update our designs quickly, such as when we recently updated news feed to make photos bigger. But news feed is constantly evolving, and building more in Objective-C creates new challenges when we add in new features.
To solve this, we came up with a different plan to let you use the newest features without requiring you to update the entire app: a "fallback" renderer. When the news feed team designs a new type of story, Facebook for iOS downloads something it doesn't quite understand. When we detect this, we use a "fallback" renderer to show the pertinent information in the new story in a format the app already understands. In the meantime, we create a new custom renderer and have it ready for our next app update. For areas within the app where we anticipate making changes more often, we will continue to utilize HTML5 code, as we can push updates server side without requiring people to download a new version of the app.
The best Facebook experience, in your pocket
Building on native iOS gives us a major opportunity to keep making the app faster, more reliable and feature-rich. Whether you have 30 seconds or an entire train ride to use Facebook we want you to have a fast and satisfying experience. We truly believe mobile is the best platform for Facebook, and the new Facebook for iOS is just one of our steps to ensure you have best Facebook experience anytime, anywhere.