Screenflow: an unfinished attempt at a cross-platform server-driven UI at Uber

The problem

Uber has many apps (Rider and driver apps, Uber Eats, Uber Freight) available on iOS, Android, and the web. Since most app features are available on all platforms, they need to be written 3 times and most often by different developers because of different languages and toolchains. That takes time and requires careful coordination. Another problem is the unpredictable wait times in the App Store once the update is submitted. In our case, one example of rapidly changing UI was payment forms. If we couldn’t quickly update the particular form with the new terms and agreements checkbox, then we couldn’t accept payments, and that is business-critical for an app like Uber.

Terminology

To refer to the UI produced in our framework we used the words “screen” or “flow”. This is the data that includes the UI definitions, business logic, and metadata. The word “screen” was not very accurate, since some features did not cover the whole screen, but were embedded in an existing native UI.

Domain-specific language

At Uber, mobile code is done in Swift and Java, web code is written in JavaScript with React. We needed a common way to describe the UI and the associated business logic. Some similar internal projects used JSON, XML, YAML, etc. files to store that data. They all had a downside of the inability to conveniently embed code inside.

An example code in our domain-specific language.
Example code for definitions files.

Server

All screen definitions lived in a shared monorepo where product teams stored their screens. For every commit, the CI pipeline invoked a compiler and stored the compilation artifacts in a database.

Mobile and web runtimes

When a product team wanted to display a Screenflow screen, they would launch the Screenflow Mobile Runtime, passing the flowId as the parameter. Screenflow runtime then loads the associated flow (either from the local cache or from the server) and initializes the native state. When the user interacted with the UI, like pressing a button, the state was serialized and passed to JavaScript. The JS code produced a new version of the state which was serialized back to native. Then runtime did the necessary rerendering.

IDE

At Uber most often iOS developers use Xcode, Android developers use IntelliJ IDEA or Android Studio, and web developers mostly use Visual Studio Code. We had an option to develop plugins for all IDEs, but it was hard to decide which one to do first. These days the Language Server Protocol is available to make it easier to support a new language, however, we wanted a tighter integration.

The web IDE.

Type safety

With server-driven UI you get a very different cadence of releases for different parts. Screens can be updated at any time, but the native API is still a part of the usual app release cycle. It was important to verify that the screen is using only the APIs that are available on that particular version of the app. In our setup, the older runtime could fetch a recently updated screen. That meant that without proper safety, a call to a non-existent API will cause a runtime crash.

Alternatives and inspiration

Why did we need to build our own solution? Let’s consider a few alternatives.

Adoption

We were aiming for a zero-config out-of-the-box framework, so the product engineers could feel as productive as possible.

Future predictions

Solutions like React Native and Flutter seem to work well when building apps from scratch. Integration with existing native apps is usually not so smooth. I think there is a niche for projects that are primarily focused on a side by side usage together with the native UI.

The end

In July 2020 the whole developer platform department in Amsterdam was unexpectedly shut down and our team was a part of it. Some people (including myself) left the company, others went to work in different teams. I don’t know what happened with the project after that.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store