Kitura/iOS: Running a Web Server on your iPhone

Server-side Swift applications, and Kitura applications in particular, can run on macOS and Ubuntu Linux. There are use cases, however, that require running server-side applications on iOS. To run a server-side application on iOS means embedding its code in a plain client-side iOS app. In this blog post, I will describe these use cases and conclude with a Hello-world Kitura/iOS app.

Until now, developers had to produce two different server-side implementations to support running server-side applications on iOS. To enable running the server-side part on a “server OS”, such as Linux, developers used server-side web frameworks for Java, Node.js, PHP, and Ruby, among others. However, it is not simple to run Java or Node.js on an iOS device. Consequently, the developers wrote the iOS server-side part using some Objective-C or iOS Swift web framework. A server-side Swift web framework provides the opportunity to prevent this duplication of effort. Now you can develop a single server-side application that could run both remotely on a “server OS” and locally on an iOS device. In my opinion, this is “the killer argument” in favor of server-side Swift web frameworks.

Use Cases for Running Server-Side Apps on iOS

I found six use cases for running server-side apps on iOS. Let me explain them one by one.

Use Case 1: Running server-side code in Xcode to facilitate iOS app development

This use case is the most obvious one. Once your server-side code can run on iOS, you can run unit tests for your server-side code in Xcode together with your client-side unit tests. Furthermore, it’s quite convenient to debug both the client side and the server side of your application in the same Xcode instance. You can put breakpoints in both parts of your application and then step back and forth between the client and the server parts. In addition, you could create a mockup server for iOS and use the mockup server before the real server is developed. You can also run the mockup server in an Xcode playground.

Use Case 2: Offline mode

According to the title of this Forrester report, supporting offline mode is the “most important and difficult feature” of the mobile apps. Running a web server on iOS can enable offline mode in a simple way – just run your server side code as part of your iOS app.

Note that several databases can run on iOS, for example, SQLite or IBM Cloudant or Couchbase. By using a mobile embedded database, you can have a full-fledged web server inside your iOS app. Your app may opt to fall back on the embedded web server when it is disconnected from the network. The client-side part could communicate with your local server through a localhost URL. The networking code of the iOS app could remain untouched; only the address of the server will be replaced. Alternatively, you can use URLSessionDelegate.

The app can synchronize a local iOS database replica with the remote database automatically, once the device is connected to the Internet. Alternatively, you can synchronize the local data explicitly at some preferred, later point in time. Furthermore, some databases facilitate synchronization between the local and the remote replicas for you.

Naturally, using the network stack for offline mode could have performance overhead, compared to the traditional offline mode implementations. However, the simplicity of the implementation could be a strong factor in favor of a local iOS backend.

Use Case 3: On-device demo mode

The local iOS backend can be used for offline demos. Suppose you want to demonstrate your app to a prospective client at the client’s premises. You are not sure that your device will have network connectivity at the demonstration site (elevator pitch anyone?). Furthermore, your app may encounter firewall issues. When you present at a conference, you cannot know if the wireless network will be reliable and fast enough for your app to function properly. By using a local iOS backend, you can take your server on your device with you, always ready for demonstrations.

Use Case 4: Peer-to-peer

An additional use case involves the peer-to-peer communication of iOS devices on a local network, over protocols on top of TCP, e.g., HTTP, WebSocket, or MQTT. The caveat here is that currently you cannot run arbitrary iOS application code in the background mode. Once your screen is locked, or the user switches to some other app, your server code will stop. You will have to ensure that your peer-to-peer apps run in the foreground. You can do it by enabling Guided Access mode on your iOS device. See the next use case for an explanation about Guided Access mode.

Use Case 5: Ad-hoc server

Another use case is an ad-hoc client-server topology. Suppose you implement an app for employee collaboration. The app has to function at some site with a local Wi-Fi network. The catch is that the local network is not connected to the Internet or has a prohibitively slow connection. Imagine you are at some military location or in a submarine that has Wi-Fi, but the Internet connectivity is restricted.

Alternatively, imagine you have a cellular device that provides a personal hotspot to devices of other app users. Suppose the hotspot device has slow cellular connectivity. The tethered devices will have a prohibitively slow connection to a remote server. However, connectivity between the tethered devices could be fast enough, since the communication will happen on the Wi-Fi network of the personal hotspot.

Using server side code on iOS, you can set up one of your iOS devices to act as a server. Then you can configure other client devices to use it as their backend.

You may want to set your server-side iOS app to run in Guided Access mode. You can set Guided Access mode to be protected by a passcode. Disabling hardware buttons is one of the options of Guided Access mode. In addition, configure your iOS app not to report its idle time by setting UIApplication.shared.isIdleTimerDisabled to true. All these settings will ensure that neither you nor the OS will accidentally deactivate your app and thereby shut down your server. This is because with these settings neither you nor the OS can lock the screen or switch off your device. In this mode, your server app will continue to run forever. Well, OK, not forever, but either until the battery is depleted, or until you let the app exit Guided Access mode explicitly, by providing the passcode.

Use Case 6: Gateway for IoT

Yet another use case for a server on iOS is Internet-of-Things. You could have your iOS device act as a gateway to smaller IoT devices. To facilitate IoT data collection and processing, the gateway device can store the data collected from the sensors locally. Once in a while the iOS device will send the data to the remote server. Furthermore, your iOS device can perform some processing of data before sending, for example to filter or to aggregate it.

Bonus Use Case: Just for the fun of it

I hope you are convinced now regarding the use cases. The last use case is just for the fun of it. Isn’t it cool to use your iPhone as a web server?

Kitura iOS Hello World app

Now it’s time for some action. Let’s run a simple Kitura application on iOS. My colleague Roded Zats and I implemented the Kitura/iOS Hello World app. The app demonstrates Use Case 5 – providing an ad-hoc server to devices on a local network. The server returns a “Hello World” message with a timestamp using REST API.

The Kitura server-side application is embedded in a client-side app that facilitates running a server. To provide a smooth iOS server experience, the app displays the URL of the server and the QR code of the URL. In addition, it allows the user to start and stop the server and shows the server logs.

To build the app, clone the project and run make. make generates an Xcode workspace that will contain the client side and the server side parts as projects. Once finished, make will open the workspace. I will explain the details of what make actually performs in a future blog post.

Build and run the client side part on an iOS device or in the simulator. To test the app, start the server and open the displayed URL in a browser of any device on the same Wi-Fi network. The “Hello World” message with a timestamp is displayed in the browser. See log messages running on the main screen of the Kitura iOS app. In addition, you can browse the log in a separate screen.

Note that you can run the server-side code of the project as a plain server-side Swift executable. The server-side code is in the ServerSide folder of the repository. Just build it with Swift Package Manager (swift build) on macOS or Linux. This is the “killer” advantage of server-side Swift web frameworks – the same code can run on iOS, macOS, and Linux.

Conclusion

In this blog post, I describe several use cases for running Kitura applications on iOS. In future posts, we will explain the technical issues involved in developing Kitura/iOS applications.

I work as a senior software developer in IBM Research - Haifa. In my 16 years in IBM Research, I have participated in a variety of projects in different areas, from binary executable optimization to microservice platforms. I've worked in various roles - as a developer, an architect and a project lead, and programmed in C, C++, Java, Swift, Assembly languages, Perl, Ruby, JavaScript, Go and Bash. My interests include Software Engineering and Architecture, Programming Languages, Software Design/Design Patterns, Application Environments/Application Frameworks/Middleware, Semantic Web technologies, Web and Mobile applications, Server-Side Swift, Microservices and Service Meshes.
I have an M.Sc. from the Technion - Israel Institute of Technology.

Database synchronization for offline mode is very hard to get right, for reasons. Services like CloudKit and Firebase handle some of this, but it still requires a significant amount of paperwork to get set up and working correctly. As a lead I usually recommend a tiered approach to offline mode, the selection being dependent on available time, money and resources: 1. No Offline – must have a connection, 2. Offline – read only from local database, 3. Limited Offline – read with limited writes (create only with caveats), 4. Full Offline – full read and write (CRUD) with synchronization and error handling.

Does this still work? I’ve tried following your steps on github but when I try running “$ make Builder/Makefile” I get the following error: *** [setDeploymentVersionOfSharedServerClient] Error 1
ruby Builder/Scripts/set_deployment_version.rb SharedServerClient/SharedServerClient.xcodeproj 11.2
ruby: No such file or directory — Builder/Scripts/set_deployment_version.rb (LoadError)