The technology behind EatIn: Android apps in Scala, iOS apps, and Play Framework web services
This blog post is the story of how 6 University of Waterloo "Winterns" managed to build the EatIn Suite - which consists of 3 web services, 2 web apps, and 2 mobile apps - in just 3 weeks. EatIn is an internal food-rating platform that allows company employees to rate and comment on the food provided by LinkedIn. We originally created it as a hackday project and then got approval from the executive team to work on it full time as an Incubator project.
Although we learned along the way that it takes a lot more than code to ship a product, making the right technology choices was also an integral part of our success. In this article, I am going to give an overview of the approach that allowed us to launch a product with so many moving parts in a short time period, including using a modular architecture, the Play Framework, the LinkedIn APIs, and writing Android apps in Scala.
Our Incubator project consists of two major components: OrderIn, a portal where the food services team enters the menus for each day, and EatIn, a set of apps (web, Android, iOS) employees can use to see the menus and rate food items.
To maximize throughput from 6 team members, we designed a modular architecture to ensure we could work as independently as possible:
To reduce conflicts, we used 5 Git repositories to manage the code for the backend, OrderIn frontend, EatIn frontend, Android, and iOS; consequently, we did not have a single merge conflict during development. Our team of 6 split into 3 teams of 2:
- Database and backend
- EatIn frontend
- OrderIn frontend
Beyond productivity, there were several other benefits to this modular approach:
- Separation of concerns: OrderIn and EatIn are intended for different customers (food service team and employees, respectively) and don't share many API calls, so separating the servers allowed us to make changes to one without disrupting service to the other server. We were able to swap out the entire backend server from ActiveWeb to the Play Framework without modifying any code on the OrderIn or EatIn servers.
- Flexibility: Maintaining a generic RESTful backend increases flexibility for future features, metrics, and open APIs. Having well-defined entry points to the database enables easier debugging and logging, since we knew exactly which controllers were accessing the data.
- Maintainability: A multi-server configuration allowed the frontend servers to have more specialized, consistent, and simplified APIs. This is applicable to EatIn frontend, where all 3 apps require the same behavior for rating dishes. If the frontend server did not exist and the apps had to communicate directly with the REST backend, the 3 apps would have had duplicate code for calling the backend with the same parameters. Maintaining consistency between the 3 apps would have been a nightmare.
We decided to use the Play Framework for all 3 backend servers. Given our limited timeframe, we needed a framework that got us maximum productivity, and Play fit the bill perfectly:
- Hot-reload: modify the code, refresh the page, see the change almost instantly.
- Easy setup: just a single command in the terminal to create and run projects, 2 commands to deploy a production server.
- IDE support: run
play gen-ideaand import your project from your IDE choice.
- Supported: LinkedIn is moving much of its Java and Scala development to Play, so we had plenty of in-house expertise.
- Maintainable: Scala is an amazing language for writing robust and maintainable code, even while in a very high speed and highly iterative environment.
We leveraged the LinkedIn APIs to authenticate users and get their profile info. Therefore, we got most of the authentication and security process for free. On our Play servers, we used SecureSocial, a Play 2.0 plugin, to handle OAuth and API calls to LinkedIn.
We used cookies to maintain user sessions across our mobile applications. When the user logs in on the mobile app, a session cookie is stored on that device, and is used for all subsequent requests. This allows us to reuse the authentication mechanism for both the web and the mobile apps.
Scala on Android
Did he just say Scala on Android? Madness! In truth, using Scala on Android not only made development more enjoyable, but considerably faster as well.
Setting up the Project
Using Scala with Android 4.2 is a simple affair. I recommend using Scala 2.10 so you can take advantage of Futures and Promises. You will need to install the following Eclipse plugins (sorry IntelliJ users):
AndroidProguardScala is necessary mainly because Proguard, an Android post-processor that strips out unused Java classes and obfuscates code to reduce APK size, runs after every Scala code compile, and it runs slowly, super slowly. AndroidProguardScala ensures that Proguard only runs after a significant code change, and using it should provide a better development experience.
After installing those plugins, create or import your Android project, then right click your project in
Package Explorer (not
Project Explorer), and select
Configure > Add Scala Nature.
After that, right click your project again and select
Voila, your project is good to go.
Using Scala with Android
Beyond Scala’s awesome collections functionality (
for comprehensions), Scala also brings many other useful tools to Android.
Events in Android-land are handled through event listeners, classes that implement a
Listener interface with the appropriate handler method. One such example is the ubiquitous
OnClickListener interface. The most common method of using this interface in Java is through an anonymous inner class:
In Scala, you can reduce the amount of boilerplate code by defining this implicit method somewhere in a utility class (e.g.
Now, anytime you need to work with an
OnClickListener, just import
Helpers.scala and you'll be able to use this much more concise and readable callback code:
Another powerful Scala tool is theScala 2.10 Futures library, which provides a more flexible and powerful alternative to Android’s
AsyncTask. I'll only give a brief overview of Futures here, so check out Play Framework: async I/O without the thread pool and callback hell for more info.
If we're using Java on Android, here is how we'd work with
AsyncTask to add up a large number of integers:
Here it is using Scala Futures:
Much shorter, right? Futures not only make asynchronous code more concise, but more flexible as well. Here is an example of how you could show a progress dialog while performing 3 asynchronous tasks in parallel in the background:
The equivalent code in Java would be significantly longer and more convoluted. Try it at home!
Of course, this is only the tip of the iceberg, and I could fill several more blog posts on how using Scala on Android can greatly improve your development experience. That said, there are a few gotchas you should keep in mind:
Gotcha #1: UI modifications can only be done on the main thread. Android will throw an error if you try to change the UI on another thread, such as after an asynchronous computation. Be sure to wrap your UI code in a
Runnable object, and pass it to the method
runOnUiThread on your Activity class. For example:
Gotcha #2: Try to avoid using the Scala reflection libraries, such as Manifests and Scala JSON libraries like Lift-JSON. Using the libraries can increase your exported APK size by 1-2MB, and AndroidProguardScala automatically strips those libraries out on post-processing. This doesn’t mean you cannot use Scala reflection, just be aware that using those libraries will require tuning Proguard and incur a noticeable increase in APK size.
Gotcha #3: You may run into an error trying to export your Android project as an APK. In that case, turn off auto-build, and remove the following line in
-injars "\path\to\your\project\bin\classes", and re-export your application.
We had a hectic 3 weeks going from concept to alpha testing to beta release, but we could not have been happier with our results. EatIn is making it easier for employees to find the food they love and give instant feedback to the food services team. Moreover, EatIn is a great source of data, and at LinkedIn, we love data. For example, we learned the other day that everyone loves burgers:
Unfortunately, people don't like grits very much:
Terry and the Winterns