wordbotch.com

Blog


Demonstration Android/iOS app with embedded server

Background

I've been meaning for a while now to write a quick post about a work-related side project I did over much of last year. At work, we have an Android app and an iOS app, both native. I work on the Android app. We have test servers that we use in development, but sometimes it's useful to demonstrate the app without having to worry about network connectivity and setting up data. I made a demonstration version of the app for both platforms by embedding a server in the app.

I wanted to create a very simple demo that I could open source to illustrate the concept. I started one for both Android and iOS that would retrieve and display jokes (for want of anything better). Then I got distracted by other stuff. I might yet return to it, though - maybe I can use Kotlin native so learn that at the same time. Anyway, for the moment I'll just try to describe what it is and how it works.

Version One - Proxy

The first version was a standalone Android app which acted as a proxy between our app and the test environment. Our app would send a request to the proxy, which would pass it through to the test environment, then record the result to a SQLite database on the way back. The proxy app could then 'play back' the recorded data. This worked ok - but only as long as actions are repeated in the same order that they were recorded. If server requests were repeated out of order, behaviour was unrealistic. The proxy app just matched a particular request to a response; it had no concept of state. For the server, I used NanoHttpd, and for the client I used OkHttp.

Version Two - Stateful

This evolved into a second Android version, where the server was embedded in the main Android app. Imported into the Android project as a jar, the server starts when the app does. Now instead of replaying canned data, the server actually maintains state. It's all in-memory though - no database. When the app is stopped and restarted everything goes back to the default. The NanoHttpd server part is loosely based on this example.

Configuration page

The server can be configured via a web form. The form gives very coarse-grained control, mostly via checkboxes. The data itself is all randomly-generated. On a side note, to generate vaguely realistic-looking names of companies/people I used the random adjective/surname pairing system that Docker uses to name containers.

One issue with configuring the embedded server via a web interface is that the server has to remain running long enough in the background for you to access it in a browser. For Android this isn't really a problem - the server runs in a background service. For iOS it was a bit more complicated, more on that below.

iOS

I got the embedded server to work for our iOS app using j2ObjC, a Google project for transpiling Java to Objective C. All the business logic code is written once in Java, then it all gets turned into Objective C for the iOS app. It's still all Java 7, though I think j2ObjC actually supports Java 8 now.

Other than that, the principal remains the same for the iOS app - the server starts when the app starts and takes the place of the 'real' back end. In order to allow the server to run in the background long enough to close the main app and access the configuration page, I had to add the following hack, borrowed from Ray Wenderlich.

func registerBackgroundTask() {
  backgroundTask = UIApplication.shared.beginBackgroundTask { [weak self] in
    self?.endBackgroundTask()
  }
  assert(backgroundTask != UIBackgroundTaskInvalid)
}

func endBackgroundTask() {
  UIApplication.shared.endBackgroundTask(backgroundTask)
  backgroundTask = UIBackgroundTaskInvalid
}

func applicationDidBecomeActive(_ application: UIApplication) {
 endBackgroundTask()
 registerBackgroundTask()
 ...
}

Standalone Server

There are actually two ways of running the app - as an embedded server in the main app as described, but also as a standalone server. In this standalone mode, the server is started as a runnable jar. The main app then just needs a minor properties change to send requests to this server instead of the test environment. The main app then doesn't need to run the demo server internally, so doesn't need to be bundled with that jar.

Outcome

In any case I thought this was worth a post even though I've put my example demo project on hold. This remains one of the biggest projects I've actually completed. I would like to think it's more or less original too - I haven't heard of it being done anywhere else, especially the cross-platform part.