Skip to main content

Swift: It isn't just for iPhones anymore

Swift is becoming a first-class server-side programming language, with the aid of tools such as Kitura.

There are no lack of choices when it comes to server-side software development these days, be it for something as complex as full-blown responsive web development or as humble as simple microservices. Java and C# have long held prominent spots in the language popularity rankings, along with scripting languages such as Python, PHP, and Ruby. There are also relative newcomers such as Go and JavaScript using Node.js.

For back-end developers in the Apple ecosystem, life hasn’t been as rosy. Client development for iOS devices and Macs was traditionally done in Objective-C and, more recently, Swift. In this article, we look at how that’s changed and what that means for server-side Swift development. If you’re already coding in Swift, you may appreciate an introduction to Kitura, a framework for model–view–controller (MVC) web development using Swift. If you have not played with Swift yet, you’ll get a taste of the language as we go along.

Swift is a language designed to allow sandbox-style playgrounds with the feel of interpreted languages and the efficiency of a compiled language. Swift tries to embody the best practices of “modern” languages, such as easy map-style coding. It pushes developers to think hard about things such as optional values, and it enforces defensive programming where practical.

As an example, consider trying to use a method called getFavoriteSport(). Usually, this method returns a string, but if the person dislikes sports entirely, it returns null.

In Java, you’d write code that looks like this:

A Java programmer has to protect the equals comparison from getting a null pointer exception by checking for null first.

Contrast this with the same method written in Swift:

The odd-looking if let construct basically says that if person.getFavoriteSport() returns a null, the if-statement fails and the inner code does not run. Also, if it fails, the second part of the if-clause (checking if the sport is equal to chess) is never run.

In Swift, the if let is called unwrapping the value of an optional. The getFavoriteSport() method would have been defined with a return type of String?, meaning it can return either a String or nil. If you try to return nil from a method and haven’t declared the return type to be optional, you get a compiler error. If you try to use person.getFavoriteSport() without unwrapping it first, you get either a compiler error or a warning, depending on the context. This is just one example of how Swift keeps developers from shooting themselves (or other developers consuming their code) in the foot.

Innovation in your inbox. Sign up for the daily newsletter.

But, as mentioned above, Swift until recently was an Apple-only club. You needed Xcode to develop in Swift, and the code would run only on Apple hardware. This was especially unfortunate because it prevented anyone from sharing business logic between mobile or Mac applications and web-based applications. If you implemented a module in Swift to compute sales tax on iPhones, for example, you needed to re-implement it in a different language to create a web version.

That’s all changing now, due to a concerted effort to port both Swift and supporting libraries and frameworks to Linux. In fact, using an open source IBM-contributed framework called Kitura, it’s now possible to deploy full-blown MVC web services written in Swift. In the remainder of this article, I walk through how you can develop Swift code in Linux and deploy a Kitura-based application using Docker containers.

Set up the environment optimally

To begin with, even though it’s possible to develop on Linux, it’s not the best way to do it. Xcode is still available only on Macs, and if you code using another editor, you miss all the nice IDE features such as code completion. The recommended strategy is to develop under Xcode and deploy to Linux once everything is working properly.

On the other hand, there’s one big advantage of developing purely on Linux: You won’t accidentally use libraries or frameworks that aren’t supported outside of the Apple ecosystem, since they won’t compile. If you do use Xcode, be sure to test frequently on Linux to make sure you don’t fall into that trap.

At the moment, the best distribution for Swift is Ubuntu 6.04. It is an LTS distribution, so it won’t drop out of support any time soon, and there are prebuilt Swift packages from it available at swift.org.

In addition to following the instructions for how to install Swift, I recommend you edit /etc/profile to add swift to the search path globally rather than just for your user.

Once you install it correctly, you should be able to type swift at the command line. We can try it out with a simple program:

This works using Swift’s read-eval-print loop (REPL). While it’s a good way to play around with the language, and is pretty much what the Swift playground environment gets you in Xcode and on iPads, REPL is not all that useful to develop production Swift code.

Instead, you should set up a proper Swift project using the Swift Package Manager (SPM). To do this, let’s create a directory called article and change directory to it, then run the command swift package init:

Among other things, this creates a .gitignore file set up for Swift, which makes it easier to later check code into a git repository. It also creates the directory structure for a Swift project and a sample Swift source file (which we get rid of in a moment).

Before we go any further, we need to install prerequisites for Kitura. It’s easy enough to do in Ubuntu:

The next step is to set up our project to include the Kitura libraries, which is done in the Package.swift file in the base directory. If you’re familiar with Maven or Ant, this file serves the same purpose, defining the project build dependencies. We add a package into the dependencies clause and into the target clause; the added text is shown in bold:

This defines a dependency to the Kitura git repository and then tells the main target for the build to depend on Kitura. This makes the Kitura library available in the finished binary.

Now, with all the prep work done, we’re finally ready to go to town.

The sample project

For this example, we create a simple JSON-based web microservice to convert between different temperature units. It takes as an input the source unit, value, and target unit, and returns the temperature in the target unit. If you give it inappropriate JSON, it gives you a failure JSON structure with the reason for the failure.

By default, a Swift package created with the Swift Package Manager for execution looks for a file called main.Swift and runs the contents. Here’s the one I whipped up, broken up with commentary:

We begin by importing the Kitura library so that we can make use of it in our code.

Next, we instantiate a Router object. A Router is what Kitura uses as a controller in its MVC architecture. It is responsible for determining how to process incoming requests.

We declare that the endpoint called “temperatureConverter” will parse the incoming body depending on the content-type header of the request. Later, we use a switch statement to determine what type of data was passed in and deal with it appropriately.

This bit is a Swiftism that calls the Router.post method; it defines a POST endpoint for the router to handle and defines a block of code inline, which is called to run when a request is received. The request variable is bound to the incoming request. The response variable is bound to the response object that will be used to send back a response to the client.

We define a value variable to contain the reason that a request has failed (if any does). Then we wrap everything in a do clause with a catch down at the end. Do/catch creates an error-trapping wrapper around a block of code. We then use the guard statement to check if there is in fact a parseable body. If there isn’t, call next() to continue the normal processing pipeline in Kitura and just return.

Next, we do a switch statement on the parsedBody result. This value is a Swift enumeration with an associated value.

What this means is that if the parsedBody is of type JSON, there will be an associated value with the actual JSON dictionary (which we bind to jsonBody).

Now that we know we have a legitimate JSON body, we look to make sure that there are values for the input and output units as well as for the temperature. We also want to ensure that the temperature is a legitimate float value. If not, we fall out of this if-statement and hit the failure case lower down.

If all the input values are available, we switch on the lowercase value of the inputUnit, and use it to convert the value to a normalized Celsius value. If we can’t get a match, we declare a failure.

The same thing is done with the output value, turning it back from Celsius to the designated target unit type.

Assuming we have both a legitimate input and output unit, we create a dictionary with the appropriate response values and send it to the response object.

Note that all we have to do is call response.send, specifying that we’re sending JSON data. The response does all the work of marshaling up the dictionary into legal JSON.

The try keyword is required on any call that is marked as possibly returning an exception. It comes in three flavors. The plain try version requires that it’s inside a do clause with a catch that matches the error type that the method throws. If you say try? instead, the evaluation silently fails and returns a nil. Finally, try! requires the call to succeed and causes a crash if it fails.

If we fell through either of the if clauses, set the appropriate failure message.

If we didn’t get a JSON body, that’s the failure reason to return.

And if we threw an error on the try above, that is the reason for the failure.

If we fell through for any reason, we have a failure message in the string. We try to send a response with try?, essentially doing our best to send a failure status but not caring if it fails itself.

With this code in place, we can build a binary and try out our web service:

$ swift build
$ ./.build/x86_64-unknown-linux/debug/article

Using curl, we can poke at the service and see if it’s working.

$ curl -X POST -H "Content-Type: application/json" -d \
'{"inputUnit" : "C","outputUnit" : "F", "temperature" : 100}' \
http://localhost:8080/temperatureConverter
{   "inputUnit": "Celsius",
 "outputUnit": "Fahrenheit",
  "outputValue": 212.0,
  "inputValue": 100.0,
 "status": "ok"
}

$ curl -X POST -H "Content-Type: application/json" -d \
'{"inutUnit" : "K", "outputUnit" : "F", "temperature" : 0}' \
http://localhost:8080/temperatureConverter
{
  "inputUnit": "Kelvin",
  "outputUnit": "Fahrenheit",
  "outputValue": -459.67,
  "inputValue": 0.0,
  "status": "ok"
}

$ curl -X POST -H "Content-Type: application/json" -d \
'{"inputUnit" : "K", "outputUnit" : "R", "temperature" : 0}' \
http://localhost:8080/temperatureConverter
{
  "status": "failure",
 "reason": "Invalid Values in JSON, inputUnit = K, outputUnit = R"
}

Success! It even successfully fails (as it were…) if you try to convert to degrees Rankine (degrees Fahrenheit above absolute zero), which this coder refuses to support on moral and aesthetic grounds.

Let’s bring in Docker

The last step is to bundle up the code so that we can run it in a containerized environment such as Docker. The good news is that a prepackaged Docker image is available for Kitura on Ubuntu, so there’s not much involved in getting your application into the cloud.

Here’s a Dockerfile for our new application:

FROM ibmcom/swift-ubuntu-xenial:latest
LABEL Example Docker packaging for article
 
EXPOSE 8080<
 
RUN mkdir /root/article
ADD Sources /root/article/Sources
ADD Package.swift /root/article
 
RUN ls -R /root/article
RUN cd /root/article && swift build
 
USER root
 
CMD ["/root/article/.build/debug/article"]

We install the appropriate Swift image, expose the Kitura port (8080), create a directory for the sources and SPM file, then build it.

Docker build .

That assumes you put your Dockerfile in the same directory as the root of your Swift project. Now you can fire it up with:

$ docker run --privileged -d -p 127.0.0.1:8080:8080 dbe2c852a5a6

Assuming that dbe2c852a5a6 is the new image you created.

That’s the nickel tour

This has been, at best, a very brief introduction to Kitura and Swift on the back end. There’s lots more to explore, such as the packages that are available under Linux (for example, Core Data: no, SQLite: yes). There are also tools you can use under macOS, such as the command-line Kitura commands, which permit you to use the generated Xcodeproj to do your development under Xcode. Plus, folks can help out, as everything (including Swift) is open source.

It’s still somewhat early days for Swift on the back end, but it looks like it’s got a bright future!

This article/content was written by the individual writer identified and does not necessarily reflect the view of Hewlett Packard Enterprise Company.