Full-Stack Swift in 30 Minutes

The introduction of Swift on the server gave the promise of being able to easily build, deliver and own the whole user experience and the solution, not just the iOS app. Building a backend however introduces many new technologies and terms, from server, cloud and Swagger definitions, to Docker and Kubernetes. This session will show you how easy it can be, demonstrating how to build a Swift Server application and connect to it from an iOS app in under 30 minutes.


Introduction

Over the next 30 minutes we’re gong to spend five minutes talking about Swift on the server and why it’s an interesting thing. The final 20 minutes will look at how to actually build a real server component that builds into a client-side application.

The State of Server-Side Swift (0:30)

December 3rd 2015, Swift became available as an Apache 2.0 license project, so fully open source. Alot has happened since then: We’ve gone from having an open source library which only really worked on Darwin, to the point that we’ve had Swift Evolution with 129 implemented proposals, another 15 which are accepted, and work is underway to bring the Foundation library to Linux. It recently reached a thousand pull requests, so a huge amount of work being done by the community to make Swift real on Linux.

We’ve also managed to take Swift from just Darwin devices to Linux and Linux on the mainframe, huge mainframe boxes provided by IBM, Hitachi etc.

It’s now running at the other scale on Raspberry Pi and there’s some nascent work to get it running on Android as well. In 18 months we’ve gone from zero off-Darwin devices to multiple platforms and a huge amount of community involvement.

What this means is between Darwin and Linux we now have a consistent foundation layer. We have the Swift language, we have the Standard Library, we have Foundation, and we have Dispatch for concurrency.

This has allowed us to start to build server frameworks, so on top of that you now have Kitura from IBM, Vapor from Q theory, Perfect from PerfectlySoft, and there are about 18 other server frameworks which have been created in the last 12 months. This is starting to build a real ecosystem where Server-side Swift is actually becoming real.

Why Swift on the Server? (2:15)

We can do it, but should we?

The first data point is around performance. When Apple released Swift they talked about it being safe, expressive, and fast.

Performance

This is a set of public benchmarks that run on Linux on a full CPU machine, running mathematical algorithms and crunching numbers:

  • Swift completes that task in four seconds.
  • Java takes 4.3 seconds – roughly comparable.
  • Node.js is four times slower.
  • Ruby is significantly slower.

server-side-swift-performance

Memory

Performance in terms of the ability to run tasks quickly is one thing, but memory usage is important as well:

Get more development news like this

  • Swift runs the test using 15 MB of memory
  • Java uses 32 MB – so twice as much
  • Node.js is somewhere between the two
  • Ruby uses quite a lot of memory.

server-side-swift-memory

Memory = 💰

You get good performance and a low memory footprint for Swift on the server. That makes it ideal for the cloud, and one of the reasons for that is in the cloud you pay according to the amount of memory that it uses. You’re charged for a one gigabyte memory instance, for example, not by the number of CPUs – it’s a purely memory-based calculation.

Getting good performance at low memory footprint means it’s cheap. This is something that we saw at IBM, and we thought was actually quite valuable and interesting.

But is it Necessarily Worth it?

There was a presentation at Swiftconf in Cologne towards the end of last year from Jens Ravens. He works for a relatively small company and 87% of their cost is employee cost, 11% of their cost is running the office, and just 2% of their cost per month was hosting applications on the server.

The argument was “Well, you’re only gonna help me with that 2%.”

If you host something on IBM’s cloud, we charge you seven cents a gigabyte hour, which equates to just over $600 a year for a gigabyte’s worth of application. If you run a node instance, typically in a 256 MB image, with two for resilience, that means running a single node application for a year costs about $300. Swift uses half the memory, with two again for resilience, and you’re running $150 a year.

So running a server Swift application is really cheap and will half your costs moving from node, but if you’re only spending 2% of your money on hosting, that’s not the most important factor for a lot of people and companies.

Productivity = 💰💰💰

Jens’ argument was “Swift is fast enough, but what we care about is developer productivity.”

If you make it easier for developers to do their job quicker, those developers can do more. Faster development means ultimately you make more money because you can write better apps quicker, and you can deliver faster than your competitors can.

Taking it to the Full Stack (6:10)

An emphasis on productivity makes full stack Swift development much more interesting.

You can reuse client-side code on the server to provide similar functionality without having to do another implementation of it. Sharing code, reusing the same tools, reusing the same workspace, having the same developers being able to work on both components, helps to give us that productivity.

This is something we actually see from web development.

In the Stack Overflow survey from this year, 11.9% of web developers describe themselves as frontend developers, 24% self-describe as backend developers, and 63.7% say they are full stack developers and they do both the client and the server. Given that web developers in general are full stack developers, why aren’t more mobile developers full stack developers as well?

The Full Stack Big Picture (7:50)

There are a lot of different pieces to make up a stack. You have an iOS app, hosted capabilities like cognitive machine learning capabilities, data persistence, social media integration, etc.

Your hosted services are separated from your app, so you add a Swift server in the middle, and an API called Swagger between the client and the server.

You’ll want to add authentication, push notifications, and mobile analytics. You also need to have DevOps, CICD pipelines, availability, scaling according to load, and monitoring. You can also serve the web experience from that same server.

Screen Shot 2017-06-28 at 3.15.59 PM

Now this looks like more complexity than just having an iOS app, but what I’m about to show you is how you can actually start to build all of that backend very easily quickly. Let’s try and build that in the 20 minutes that we have left.

Full-Stack Swift Demo in 20 Minutes (9:15)

FoodTracker Demo App

For this demo we’ll work with the FoodTracker app. It’s available as part of Apple’s getting started guide to programming in Swift, and is a simple app where you can save meals along with a photo, give it a rating, and it saves that data and persists it to your local device using NSCoder. The model is very simple:


    var name: String
    var photo: UIImage?
    var rating: Int
    

Creating a server

Next we’re going to create a server that’ll actually store this data on the server.

I start with an empty directory that we provide a utility called yo swiftserver. yo is a utility call Yeoman, which is a generator that helps you get accelerated building apps, and we’ve created a plug-in for Yeoman called swiftserver. So yo swiftserver helps you start to build an application. I’m gonna call mine FoodTracker and build it in the FoodTracker directory. I’m doing persistence so I’m going to create a CRUD application: create, reuse, update, delete.

I’m going to ask it to build in a bunch of capabilities: I want a monitoring dashboard, I want some Docker files to deploy to server, I want to be able to deploy into IBM’s Bluemix cloud, I want to store the data in a Cloudant database, I would like to have auto-scaling, and I’m happy with the default configuration.

It’s now starting to build out the shell of an application for me. It’s creating a whole load of files which helps me build and deploy that application.

We give you a Sources directory in which you have the application which is called FoodTracker, and we’ve generated a whole bunch of stuff for you: an adapter, some factories, and your Package.swift file or Package.pins file.

Adding the Model

Now at the moment I’ve told it to create an app, but I haven’t actually told it anything about the data I want to be able to persist. So my next task is to ask swiftserver to add a model to it through a prompt in command line.

Next I’m going create a server meal, which is the server equivalent of the meal object I have in my client. It had a couple of properties:

  • name which is a string and I will make a required property, not an optional.
  • photo was a UI image, but JSON doesn’t have an equivalent to a UI image so we convert it to a string, and also make it required.
  • rating was a number so I can select that, again required.

Voila! (14:25)

And now my server meal model is created and it’s filling out the rest of the application and the APIs that I’ve just asked it to create for my application. I now have a fully working application. If I run this locally, we can see my Kitura application up and running.

Inside my application I’ve added a couple of things by default. The first thing is Swift Metrics Dash, which is an inbuilt monitoring capability that tells me CPU usage, memory usage, and the performance of calls against the HTTP server. It tracks any REST call.

The second thing I’ve added is an explorer, which allows me to go through and look at the API that I’ve put in and run and test the API directly.

For example, if I wanted to run this and use get and find all of the server meals that have been stored, I can hit “Try It Out” and it gives me a response from whatever’s stored in my database at that point in time. Back in the monitoring area we can see the performance of the requests that have been made against the server.

Running it in the Cloud (16:00)

So far this is running locally, but we wanna be able to run it in the cloud so we’ll create a Git project next.

The application is already set up with a Git ignore file, so it’s not going to send all of my build directories Git.

I’ve created a Git project, now I’ll add all the files to it, and commit. The last thing to do is push it to a real Git server, for this demo we’re pushing it up to my GitHub at seabaylea/FoodTracker.

The project is now Git and from Git IBM offers a “Create toolchain” button that will link my Git repository to IBM’s Bluemix CICD pipelines.

Next we will publish it into an organization, which I’ve already setup with an account, tell it to use I want to use the repository we just created, and check the box to track deployment of code changes, so as I make pull requests, it will refresh the deploy in Bluemix.

We now have a full CICD pipeline from my code to a deployed application that will automatically deploy the application. But at this point we’ve built a client, we’ve set up a server, but we don’t have a link between the two.

Building a Client SDK (18:00)

The next step is to build a client SDK to put into our iOS app to let it talk to the server. Bluemix has a command line utility called bx sdk generate that will create a client SDK based on a Swagger definition.

Once my app’s been deployed to the cloud I can select the deployed application, but it’s still deploying so for now we’ll point it to a local Swagger definition.

Now that was created as part of my application so I can type ./Cloud/FoodTracker/definitions/FoodTracker.yaml --ios to build an SDK for iOS. If we wanted to we could build SDKs for Android and a number of other languages as well.

bx sdk generate creates an SDK for me and downloads it to my local server, which is this FoodTracker.zip file with a bunch of docs, a pods file, etc.

There’s some information about how to get started in Docs/README.html:

  • In my project, we need to add a pod file with a couple of entries
  • We need to update a plist
  • It gives us some APIs - a server meal API and info to plug it into my application.
  • It gives us a model which represents the information that we gave to the server earlier:
    • id
    • name
    • photo
    • rating

Plugging it in (20:20)

Now we’ll plug it into our iOS application. In our table view controller we previously were loading meals and saving meals. The first thing we do is to add this code into saving meals:

for meal in meals {
    saveToServer(meal: meal)
}

As part of saving the meal I’m going to iterate over the meals that I’ve got and call saveToServer.

private func saveToServer(meal: Meal) {
        ServerMealAPI.serverMealCreate(data: meal.asServerMeal()) { (returnedData, response, error) in
            guard error == nil else {
                print(error!)
                return
            }
            if let result = returnedData {
                print(result)
            }
            if let status = response?.statusCode {
                print("ServerMealAPI.serverMealCreate() finished with status code: \(status)")
            }
        }
    }

This is the code taken from the docs that came down as part of my client package. We’re making a call with ServerMealAPI.serverMealCreate, and what I have to do is convert my client meal into a server meal, so I can convert that UIImage to a string by taking the photo, creating a JPEG representation of it, and making a base 64 encoded string - that’s what will go over the wire.

extension Meal {
    func asServerMeal() -> ServerMeal {
        let serverMeal = ServerMeal()
        serverMeal.name = self.name
        serverMeal.photo = UIImageJPEGRepresentation(self.photo!, 0)?.base64EncodedString()
        serverMeal.rating = Double(self.rating)
        return serverMeal
    }
}

My client is now set up, the next thing we will do is tell it where to send the data - this should all be working now but it just doesn’t know where the server is yet.

My Bluemix deploy is still running, so we’ll use use my local version.

Back in Xcode we’ll tell the app where the server is in a plist file, at http://localhost:8080/api. Since we’re on the local host we’ll refresh Swift Metrics Dash and delete everything that’s in the Swagger definition. Now Swagger sends me back a response body when I try and get everything that says there’s no data in my database at the moment.

In our application running in simulator, I’m going to add a new meal, select a photo, and name my meal “waterfall” even though it’s a meal. If I save that, what I can see from Xcode is it’s telling me ServerMealAPI.serverMealCreate() finished with status code: 200.

In my server app we’ll fetch the data from it again, and now we have all of my meals: the Caprese Salad, Chicken and Potatoes, Pasta with Meatballs, and I have “waterfall”.

So we can see that it’s storing all of that data to the database and my monitoring tells me about the performance of those calls. So I’ve now plugged a server into my client pretty quickly (only doing save of something I was doing locally without setting up fetch yet, but this is all work that you can do).

Building the Web App (25:15)

Now that I’ve got the information in the server, I might want to be able to build some kind of web app for it.

In my generated application I have a number of resources. You get is a whole bunch of roots which are the APIs which we’ve implemented on API server meals and we do get, post, delete, put, patch and delete. These are the REST APIs for persisting data.

Next we’ll ask it to persist some data where we have the handleCreate function. We need to add code to do the reverse of my conversion of the image to a string - I need to convert it back to data. In my handleCreate we’ll add this code:

let photoData = Data(base64Encoded: model.photo)
let fileManager = FileManager.default
let publicDirectory = fileManager.currentDirectoryPath + "/public/"
fileManager.createFile(atPath: publicDirectory + model.name + ".jpg", contents: photoData)

What this is doing is it’s saying:

  • I’ve got a meal that’s been created
  • From that I’m going to take the photo value
  • I’m going to create data from it from a base 64-encoded string
  • Then I’m going to copy it into the public directory of my app.

It’s set up to serve the contents of the public directory’s static webpages, so it will save the photo into the public directory. The last thing to do is to add some HTML. With that added, let’s go to my public directory in command line and add something called jpeg.html. And finally we just need to add an image.

At the moment there’s nothing in this directory except jpeg.html that we just created. If I run the server, go into the app again, and create another random entry and save it, as part of saving it to the server it will copy those files into the public directory. When I go to my app at localhost and look at jpeg.html it should show me the same photo that I saved, but it’s not there.

We missed the critical swift build line - the silver lining when a demo goes wrong is that it proves that this is actually happening and I’m not making it up as I go along. 😄

Let’s try building that again, and this time just edit and delete something for a quick way of getting it to re-save. Just to be on the safe side let’s add something in as well.

Voila, Part II (30:25)

And this time it works! What we’re doing is taking stuff that’s in the app, copying it to the server, from the server it’s persisting in the database, and also taking that data and making it available as a hosted web app.

Conclusion (40:40)

In about 15 minutes of demo we’ve gone from having a standalone iOS app that persists data to the local device to having a remote persist and then being able to re-share that data in a web page.

Hopefully, this shows how easy it is to get started. Everything in this whole stack is Swift code. I needed to convert my UI image to a base 64-encoded string, but I could use exactly the same code to do the reverse in the server to store it as data in the local file. Likewise, if I wanted to fetch something from the data to the local application, I’m just doing the same code in reverse.

Resources

Next Up: Using Realm Seamlessly in an RxSwift App

General link arrow white

Chris Bailey

Chris Bailey is a developer and technical leader in the Runtime Technologies team at IBM. Chris has spent over 15 years working on runtimes, working with the open source communities for Java, Node.js and most recently, Swift. He has contributed to the Swift Language, Foundation and Dispatch projects, and is currently working on making more “server” focused APIs available to the community.