Re-architecting Pinterest's iOS app
A small team of Pinterest iOS engineers was recently given the opportunity every engineer dreams of - completely rethinking and rebuilding our app. We’ve grown so much that it’s created a high demand on our platform. In order to reach our next 100 million Pinners around the world, we re-architected our platform from the ground up with a framework that’s faster to build in, easier to scale and up to 3x faster for Pinners in many different scenarios. Here’s a look at some of the larger pieces of the project, with more to be published the coming weeks. Off we go!
Reaching more people outside the U.S. requires the app to be as performant as possible, especially on older devices. That means previous issues that may have come up - such as frames dropping when scrolling home feed - needed to be resolved. The fastest and easiest way to reduce energy usage and increase performance was to take advantage of the fact that most iPhones have multiple processors.
Our new system relies heavily on multithreaded code; UI rendering, image downloading, GIF decoding and model processing are all examples of systems we’ve designed to be multithreaded. This allows us to better maintain smoothness in scrolling and responsiveness when a Pinner taps. In testing, we’ve measured 10x improvements in touch responsiveness and scrolling smoothness. And what performance project would be complete without a bunch of minor tweaks? We built at least three different ways to round the corners of a view (the cost of using the cornerRadius property on CALayer adds up surprisingly quick).
Most of the new framework was built on top of AsyncDisplayKit (ASDK), an open-source iOS framework originally authored by Pinterest engineer Scott Goodson, which keeps UIs smooth and responsive. ASDK seemed like a silver bullet to our problem because it provided asynchronous rendering of the UI, so we could do the expensive work of layout and rendering off the main thread.
While asynchronous display is the main promise of ASDK, it also provides a layout system similar to CSS Box Model, easy use of CALayers instead of UIViews (when you don’t need to register touches) and a myriad of other performance improvements. One especially impactful optimization is the calculation of different ranges for scroll views. ASDK will calculate a range of cells in advance of the Pinner’s scroll position, which need to have their data fetched from the network, and a smaller range of cells for pre-rendering. This automatically allows just-in-time fetching, rendering and clearing out of ranges that the user has passed, saving memory and time spent on individual run loops to speed up scrolling.
Of course, in rewriting nearly the entire app to take advantage of ASDK, we found bugs and submitted a number of improvements back to the project. In fact, five of the ten biggest contributors to ASDK are Pinterest employees.
Immutable data models
Another significant pain point has historically been caused by our model system. The system we had was intermittently thread safe, and while creating the models could be done on a background thread, it required any modifications to models to occur on the main thread. This was bad for performance and introduced a complexity that wasn’t always obvious to developers, leading to many crashes.
What we needed was a system that was thread safe and performant. The easiest way to guarantee thread safety is to make everything immutable, however it was a challenge to change nearly 100 models and keep them updated as new features were developed. To solve for this, we generated them ourselves. One of our brilliant engineers (Rahul Malik) went off, learned some Swift and built a system that turns JSON files into immutable models. We’re really excited about our generator and will be open-sourcing it and blogging about it soon!
Now I’m sure you’re asking yourself, “But Garrett, how do you get your architecture Pins on your boards if your models are immutable?”. I’m glad you asked! Our new immutable models allow for creating modified copies with a builder system. We have a robust framework (built by the gifted Wendy Lu) for quickly and safely notifying UI and any other interested observers of the changes. We’ll soon share more about our models, the generator and the system for keeping our UI in sync.
A new design language and system
In addition to a near complete rewrite of the iOS app, we were also tasked with overhauling the visual design. We worked closely with the product design team (including field trips to our remote office in Portland) to implement a new design language and framework that simplifies building apps for multiple device sizes. With the designers, we created a portmanteau of BRIO (the internal design code name) and points (i.e.“boints") to specify device agnostic layout. Once it was spoken aloud, it wouldn't die.
The new framework also automatically scales fonts, margins and kerning depending on your screen size and language. We found Chinese, Japanese and Korean languages appear too large to native readers without a bit of scaling, so our framework automatically handles that.
Overall, this is a system any engineer can get behind: one implementation for any screen size (ok, there are still a few #if IPADs scattered about but at least an order of magnitude fewer).
And what visual refresh would be complete without some new interaction design? (This is exactly what we asked ourselves while working on the project well past our bedtimes.) So we took on the challenge of building new animations and transitions in a performant way. How do you make a button that can expand outside of its bounds no matter where in the UI it shows up? How do you calculate the transform to simulate an image growing to become full screen and a different aspect ratio? Tough questions we’ll be answering in a follow-up post.
Logs, logs, logs
Most importantly, with all of the updates, we wanted to be sure Pinners would continue to enjoy the experience of discovering and saving ideas. The only way to do this is accurately measure usage! But it would be terrible if for every button in the app we had to write code to send a log to the server. Why not build a smart system that can ask the button, “Anything special about you, button? What view are you in?”. That’s exactly why one of our talented engineers (Chris Danford) built a system that takes care of much of the logging headache without the developer having to do much. It walks up hierarchies, gathers context and generates detailed logging and sends them to the backend.
We’ve been on a long journey to ship this project, and we’re all incredibly proud of what we’ve achieved - a shiny new app that performs admirably on low end devices while delivering a premium experience. We can’t wait to share with you in greater detail how we did it.
Android and web are next on our list to overhaul. If you’re excited to help us build those frameworks from scratch, join our team!
Acknowledgements: This update was a massive undertaking by members across the company including Allen Williams, Arla Rosenzweig, Andreas Pihlström, Austin Louden, Bill Kunz, Brendan Ryan, Bin Liu, Chris Danford, Connor Montgomery, Evan Sharp, Huy Nguyen, Garrett Moon, Gordon Chen, Jay Marsh, Jenny Liu, Kim Fellman, Leo Liu, Levi McCallum, Long Cheng, Lukas Blakk, Luke Zhao, Martin Jiang, Max Gu, Michael Schneider, Nicole Hedley, Patrik Goethe, Rahul Malik, Ricky Cancro, Rocir Santiago, Romi Phadte, Samuel Hsiung, Scott Goodson, Steven Ramkumar, Tom Watson, Vincent Tian, Vivian Qu, Wendy Lu, Yunnan Wu, Adam Barton and countless folks from our data and biz analytics team