Shared App Functionality via JavaScript

How we created a flexible way to view Square Cash payments offline.

Written by Alan Paulin.

When building version 2.9 of the Square Cash app, we ran across an interesting challenge. The app was fetching payment objects from our server that needed to be displayed to the user. This basically involved choosing a template for each payment and formatting the data. For simplicity, let’s say that the data included a date, an amount, and recipient name, and it needed to produce a displayable string like: “You sent $20 to Erin Hills at 3:00 pm.”

We first considered baking the logic and templates into the app, but that would have resulted in an inflexible system where we’d need to launch a new version of the app to pick up a change. Old versions would be stuck presenting payments incorrectly, and the logic would need to be duplicated in each supported platform (iOS and Android). Our engineering velocity would diminish tremendously.

For a long time, we solved this issue by having the server return fully presented data rather than raw data. This achieved flexibility but at the price of some big drawbacks. The most significant issue was that the app needed a network connection and needed to constantly ask for the presented view of a payment (since the presented data is time sensitive). This resulted in excessive data usage and the lag could introduce staleness. As we worked towards the ability to view payments offline, we knew this approach was no longer viable.

Solution

The solution that we landed on is to use a JavaScript engine in the app to produce the presented strings from the raw objects. This works remarkably well. A JavaScript library that runs in the JavaScript engine effectively contains a series of templates and a translation function that consumes the raw objects, then applies the appropriate template, and finally outputs the presented data.

Here’s a simplified example of a raw object fetched from the server that is passed from the app into the JavaScript library:

{
amount_cents: 2000,
recipient_name: “Erin Hills”
date : “2016-01-19T15:00:00Z”
}

The JavaScript library produces the following object and passes it back to the app:

{
description: “You sent $20 to Erin Hills at 3:00 pm”
}

The presented data is sensitive to the current date and timezone. If the user opens the app a day later, without a network connection, the same JavaScript might produce:

{
description: “You sent $20 to Erin Hills yesterday”
}

Updating the strings or adding support for new types of payments can be done by changing the JavaScript. Here’s a simplified example of when we added support for donations to nonprofits. Raw data:

{
amount_cents: 2000,
recipient_name: “Wikipedia”
date: “2016-01-19T15:00:00Z”,
type: “NON-PROFIT”
}

The JavaScript would convert it into:

{
description: “You donated $20 to Wikipedia yesterday. No goods or services were provided in exchange for this contribution.”
}

Implementation

iOS 7 introduced support for a browserless JavaScript engine with its JavaScriptCore framework. This made for a simple, out-of-the box solution that took very little time to get up and running.

In Android, there was no built-in framework included in the standard APIs. So, we built an open source Java binding to Duktape, an embeddable JavaScript engine. We built in support for timezones, converting JavaScript stack traces to Java stack traces (when the JavaScript throws), and other features.

The JavaScript functions that the apps call into (effectively, the interface between native code and JavaScript) need to use primitive objects. In practice, the functions exposed to the apps by our library all have one or more input string parameters and all return back a string. The JavaScript library is responsible for parsing the inputs into JSON objects. The apps are responsible for serializing the output from stringified JSON to native objects.

The server hosts the JavaScript library as a single file and the apps periodically check for an updated version. Our implementation of this mechanism uses ETags to redownload the JavaScript file only if it changed.

One implementation detail that we found useful was passing the raw object to the app as an opaque string rather than as a strong typed object. This lets the server have complete control of the raw object and the JavaScript that uses it. New fields can be added to the raw objects without the apps knowing or being affected.

Additional Benefits

One of the nice benefits of JavaScript is that it’s not limited to native apps. The same library can also be used in the web. This leads to perhaps one of the best aspects of this approach: the logic lives in a single location (the server), rather than duplicated across iOS, Android, and the web. This helps to ensure a consistent experience, reduces the amount of testing required, and increases our engineering velocity.

Conclusion

The use of a shared JavaScript library has allowed Square Cash payments to be viewable offline while allowing the presentation to be updated independent of a new app release. We’re excited to expand the use of this approach into other areas of the app. Thanks to Michael Druker, Dan Federman, Matt Precious, Alec Strong, Shawn Welch, Jesse Wilson, and Shawn Zurbrigg for their contributions to the effort.


One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.