@zachwill

Most of my projects are on GitHub. I'm currently with the Portland Trail Blazers.

Tumblr for iOS

Update: Tumblr for iOS is now completely native.

After updating the Tumblr application for iOS (and coming across Peter Vidani’s Dribbble shot), I decided to take a stab at seeing how it worked. I ended up using both Crunch (for resource files) and Charles (for network traffic). There’s no real voodoo involved to see how things fit together — most of what I did is covered in this NSScreencast episode.

The first thing you notice when looking through Tumblr’s source files? 26 different Mustache templates. Yeah, 26 — and all end with a .html extension.

I really hadn’t noticed that the timeline was one big UIWebView when first playing around with the app — in fact, for awhile (even after finding the templates) I thought maybe each post in the timeline was its own UIWebView held inside a native container. Credit where credit is due, it’s an incredibly nice web interface.

Since all the CSS and JavaScript files are included in the source, I thought it was weird that Mustache.js wasn’t included — and both spin.js and Zepto were — but it actually makes a lot of sense. Behind the scenes, the app’s obviously using the Tumblr API, but the API actually has total control over how the timeline looks and feels. So much so, even the highlighted ribbons in staff posts are done via the API:

{
  "highlighted": {
      "message": "Now presenting...",
      "icon": "http://assets.tumblr.com/unicorn.png",
      "color": "#498acc"
  }
}

The API sends 20 resources at a time (posts from users you follow) for the home timeline. Mustache templates are then used (probably with GRMustache) to create the initial HTML document. Based on some of the comments in the app’s JavaScript files (which are incredibly well-commented and easy to read), native code is used to detect how far the user has scrolled.

Once we reach the end of those 20 posts, the API sends 20 more, which go through the Mustache templates to become HTML, and JavaScript is used to append those to the current timeline. The downside to this approach (and I’m sure the engineers are aware of it — the available code is really well-written) is when a user agressively tries scrolling through the timeline. Frame rate drops pretty significantly and scrolling begins to feel sluggish — native containers like UITableView have had hundreds (if not thousands) of man-hours spent on performance and cell reuse for this exact scenario.

While most users won’t ever notice this — I mean, it took me a couple hours to realize it was a single UIWebView even after seeing the static source files — there is another downside. Whenever the user goes back through the timeline a signifcant amount and then exits the application, it can take a really long time to reload. The UIWebView basically has to render the giant HTML timeline again.

But, this post isn’t about a native versus HTML approach, and I’ve got to admit, it’s pretty cool seeing how well the API and Mustache templates mesh together. Basically the entire “logic” behind how the home timeline is displayed exists in the interaction between the two.

Another interesting find was the lack of .nib files (which is actually the opposite of Airbnb’s source). Native UIView files are obviously used when the user begins writing a post (along with a really nice tab bar animation), and are probably written in code. One comment in the Post.cssfile indicates Storyboards are in use or will soon be used sometime in the future. Update: the Storyboard comment actually refers to storyboard.tumblr.com.

For Future Bryan, this is to make Storyboard interviews look right.

Another great takeaway was how the engineers target Retina devices in CSS:

@media only screen and (-webkit-min-device-pixel-ratio: 2) {
    /* Retina CSS */
}

Moving on to other parts in the application, I’m inclined to believe that pull-to-refresh is native (and, as a side note, I really like the use of images throughout the UI rather than text). There are also two Core Data .momd directories included in the source, one of which is named STPersistentCache.

After some light testing, I’m pretty sure STPersistentCache caches images locally after they’ve come across the network twice (at least this was the case with larger images — which no longer came over the wire). It actually makes a lot of sense when you think about it: some of the images in the timeline (or other views) will never be accessed again, but those that have been accessed two or more times must be somewhat pertinent to the user.

The other .momd includes 20 entities that make up the basis for the types of posts a user can make, etc. One of the newer entities is FanMail — which has accompanying images in the source — but I was unable to find it when playing around with the app (although, I haven’t interacted with fans on Tumblr).

Some other notes include Tumblr’s use of Flurry for analytics (sends on applicationDidEnterBackground — standard stuff). Also, the minimum version supported is iOS 5.0 (checked Info.plist).

Lastly, I could be completely wrong about some of these details. Crunch and Charles are great tools for seeing assets and network traffic, but they’re not exactly a view source into Tumblr’s Objective-C code. I can say the CSS and JavaScript are incredibly well-written code — within only a couple hours I was able to see how things fit together.