Tryswift chris bailey robert dickerson facebook

Swift, Java, Node.js, Ruby? Advantages of Server-side Swift

The addition of support for Swift as a server-side programming language makes it possible to use not just the same language on client and server, but also to reuse APIs and code.

This try!Swift session will introduce you to new models of client and server interaction for application development, and show you how to rapidly build an app with both client and server components written in Swift.


Introduction (0:00)

My name is Chris Bailey. Robert Dickerson and I work for the [email protected] engineering team. In this post, we are going to talk to you about building end-to-end applications in Swift. Now, as part of it, we’re going to try and achieve two things:

  • First of all, explain why Swift is a very good language to be using on the server as well as the client.
  • Secondly, and hopefully more importantly and more interestingly, give you a real example of an application that we built that does that and show you how to achieve that yourself.

Modern Application Design (0:44)

So you have an end user, who is going to be building a client facing application which you’re going to be doing in Swift. Then you’re probably creating some kind of backend application service and doing that using Java or Node.js or Ruby, for example. That, in turn, is probably going to be using some third-party services, whether that’s Elasticsearch, Twitter, Twilio, AWS. or some NoSQL database. You might also be using some legacy database on an on-premise system, i.e., DB2, or Oracle, or MySQL.

What were going to try and convince you, is that rather than just using Swift for your user-facing client apps, you should also be taking Swift and using it on the server. Which gives you an integrated developer experience, so you can use the same IDE and share code between client and server.

Server-side Swift (1:47)

That’s great, but why Swift on the server? What’s the advantage of using Swift on the server? One of the things is it’s really highly performant. On slide 9, we have a set of public benchmarks, run by a group called Alioth that runs the “Benchmarks Game.” These benchmarks are run across a whole number of languages. They run them on Linux on a four-CPU machine.

This particular benchmark is called spectral norm, and it’s just mathematical number crunching. It tries to burn all of the available CPU, and the benchmark, in this case, uses dispatch and runs on all four available CPUs on the machine.

Now Swift completes that task in four seconds. Java does it in just about the same time, in 4.3 seconds. So, Swift and Java’s performance on four-way Linux books for this task is actually almost identical. You then look at something like Node.js. Node.js takes 15.8 seconds. It’s roughly four times slower than running Swift or Java. If you run something like Ruby, then it’s incredibly slow. Ruby is not a language for doing computation, largely because it doesn’t have a form of just-in-time compiler under it like Node.js and Java does.

For performance reasons, Swift’s a really interesting language to be running on the server. But it’s also excellent for memory. Looking at the same benchmark, and at the resident set size (the amount of physical memory that’s needed to run that benchmark), Swift requires 15MB worth of memory. Java, however, requires 32, so you’re running at half the amount of memory but getting the same performance. Node.js needs a little bit less than Java, but it’s still a lot more than Swift. And again, Ruby requires quite a lot.

Swift Cloud Advantages (4:02)

Now this is interesting because if you run applications in a cloud, most clouds charge you by memory usage. So you will buy a 256 MB container or a 512 MB container, and the CPU comes for free there. What this means is Swift is an ideal language to be running in the cloud because it’s both highly performant and has a low memory footprint. It can get more containers for the same amount of money, and therefore run more instances of your application. It’s good for the cloud, and it’s good on the server.

Get more development news like this

It’s also good for developers, because of what’s loosely called “isomorphic development”. The idea here is you are a developer building your Swift project. You can create a project that has both client and server components to it. You can build that in the same project, and you can write it in the same code, and in fact, you can share components. Then you can deploy that application both onto the client and onto the server, and have connectors for whatever databases or data stores it needs. At the same time, you can almost certainly generate a Swagger defined API that sits in the middle because you might want to browse a client or an Android client for it as well. But as your first class citizen, you’ve created a Swift application on both client and server.

This is the side effect that you get to use the same tools; you can use Xcode for everything. You can use the same build systems, the same linting infrastructure. You get to use the same tools and technologies everywhere.

So what does it take to be able to do this?

How Does This Happen? (5:26)

How do we get Swift running on the server? The first thing we have to do is make the language run there. When Swift became open-source in December last year, on Linux it wasn’t complete. We had the Swift runtime, the standard library, a little bit of foundation, and we had a version of dispatch, but it didn’t compile, and it certainly didn’t pass any of the tests.

Where we are now is that we have a working version of foundation and we have a fully working version of dispatch, and those are both in the Swift 3 toolchain. That now gives us consistent platform between client and server. The next thing you need is a web framework, and IBM’s been working on one called Kitura. There’s a whole number of other ones out there.

Then you add a couple of necessary things in order to be able to run the server-side codes. The first thing you need is networking. You also need security if you’re going to do HTTPS, and secure web sockets, and you need some form of HTTP parsing.

We’ve added three additional libraries to do that. We’ve then added our web framework and once you start writing you client application you can then start to take application libraries used by your application and use them on both sides, client and server.

One of the things we’ve been trying to do with Kitura is to use standard components. We use dispatch for concurrency, and the reason we do this is because:

  • It’s the right thing to do because it’s a good library.
  • When we find a performance problem, and we fix it in dispatch, the rest of the community inherits that.

When someone else finds a problem in dispatch, we gain from that as well. So the more users there are of a given component, or a given library, the more sharing there is, the higher quality it becomes and everybody benefits.

As part of that drive to use standard components, we’ve been working with Apple and some other people in the community for the last couple of months to turn those previously mentioned components into standards: Networking, Security, and HTTP parsing. Make them part of the Swift runtime and create a set of server API components.

Swift 3.0 + Kitura (8:12)

Between Swift 3.0 being a solid delivery of Swift on the server, plus web frameworks like Kitura, we now have Swift on the server as a real option for deploying the backend for your iOS applications with all of the benefits of being able to share code.

Now my colleague Robert is going to show you how to do that for an application.

Blitter - Social Networking Backend Example (8:47)

In this example, we’re going to build a social networking backend with Swift 3, and you can see an example of the flow on slide 28. This is a typical backend application, standing up an HTTP server, creating a router which is a way of taking an incoming request, matching some kind of regular expression to it, and routing to the appropriate handler.

We can also plug in various middleware, so for instance, we’ve built a Facebook credential middleware that you can plug in, and it can intercept the request, validate the OAuth token, and get you back a username. And we have various handlers, which are closures that are invoked when the router finds a match. We’re also going to use a Cassandra backend because it’s a very performant database.

I’m going to show you how to:

  • Set up the project and the dependencies
  • Set up the routes
  • Add the Facebook authentication
  • Set up the model of the database
  • Handle the requests

Since many of you probably haven’t played around with Swift 3 on the command line yet, I want to show you an example of what happens when you install Xcode Beta 8 or the Swift toolchain on Linux.

Create the boilerplate

If you create a new directory, you can use the Swift package manager to save Swift package in it, and it creates some boilerplate code for you, so you’re ready to get started:


$ ~/> mkdir Blitter && cd Blitter
$ ~/Blitter/> swift package init

Creating library package: Blitter
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/Blitter.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/BlitterTests/
Creating Tests/BlitterTests/BlitterTests.swift

We can generate both of the sources, which is a Hello World app, and then a test scaffolding so you can add your test cases here. Most of you will probably want to develop using Xcode, and you can transform that project into a Xcode project by using the Swift package manager:


$ ~/Blitter/> swift package generate-xcodeproj
$ ~/Blitter/> open Blitter.xcodeproj

I love developing in Xcode when building backend code because I can step through it, add breakpoints, and I have type checking, code coverage, profiling, and all those tools work on backend Swift.

Add dependencies

Your Package.swift file is your way of specifying dependencies:


// Package.swift
import PackageDescription

let package = Package(
    name: "TwitterClone",
    dependencies: [
        .Package(url: "https://github.com/IBM-Swift/Kassandra", majorVersion: 0, minor: 1)
        .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 0, minor: 28)
        .Package(url: "https://github.com/IBM-Swift/SwiftyJSON.git", majorVersion: 0, minor: 14)
        ]
)

You don’t have to work with Carthage or CocoaPods anymore. You can specify the GitHub URL and the major and minor version that was tagged on those repositories, and the Swift Package Manager will automatically download those dependencies in.

Set up routes (11:44)

Next, how do you set up routes?


router.get("/") { request, response, next throws in

    // Get my Feed here

}

router.get("/:user") { request, response, next throws in

    // Get user bleets here
    let user = request.parameters["user"]
    
}

router.post("/") { request, response, next throws in

    // Add a Bleet here
    
}

So if you are used to Express.js or Sinatra, we use a very similar syntax that you can register update requests on GETs, POSTs, PUTSs, and DELETEs. You get back a request-response object, and also a function you can call to hand it off to the next set of middleware. If you want to pass in URL parameters, you can parameterize that using /:user for example, and then I’m fetching the user later in the parameters.

Usually I like to encapsulate all of that logic into a controller, so here I’ve produced a BlitterControler class:


public class BlitterController {
    
    let kassandra = Kassandra()
    public let router = Router()

    public init() {

        router.get("/", handler: getMyFeed)
        router.get("/:user", handler: getUserFeed)
        router.post("/", handler: bleet)
        router.put("/:user", handler: followAuthor)
    }
}

I created the database driver, the router, and the register. All those routes are inside of my constructor.

Add Facebook authentication (12:35)

How do we add Facebook authentication to this? Here we have the Credentials module:


import Credentials
import CredentialsFacebook

let credentials = Credentials()
credentials.register(CredentialsFacebook())

We create the Credentials() and then go ahead and plug in the Facebook credentials into our middleware. We also have support for GitHub and some other authentication sources.

If you want the middleware to intercept the traffic, you register the credential’s middleware on that route. For instance, when I want to post as an author a new bleet, I will POST at the route path, and then the middleware will check to see what my username is, and then that will be my user that’s used:


router.post("/", middleware: credentials)

router.post("/") { request, response, next in}
    /// ...
    let profile = request.userProfile
    let userId = profile.id // "robert dickerson"
    let userName = profile.displayName // "Robert F. Dickerson"
    /// ...
}

Set up the model and database (13:22)

A Bleet has the following properties, and for every time I write one, all of my followers get a copy:


struct Bleet {
    
    var id             : UUID?
    let author         : String
    let subscriber     : String
    let message     : String
    let postDate     : Date

}

That’s how we’re going to structure the data here. The message of the Bleet and then the postDate. The key is that we’re using Foundation here, which is really nice. Notice that you don’t have to do NS anymore, that’s been renamed. Using the date and UUID is fine here on Linux as well.

When you’re writing backend code you often have to structure things as JSON quite a bit, so it’s useful to have some notion of converting a structure into a string value pair. Here we define a protocol for making sure that that conversion is possible:


typealias StringValuePair = [String : Any]

protocol StringValuePairConvertible {
    var stringValuePairs: StringValuePair {get}
}

It also makes sense that if an object is string value convertible, then a collection of those should return a collection of string value pair convertibles as well:


extension Array where Element : StringValuePairConvertible {
    var stringValuePairs: [StringValuePair] {
        return self.map { $0.stringValuePairs }
    }
}

We can define a Bleet with this kind of string value pair conversion method so that we can easily turn it into JSON:


extension Bleet: StringValuePairConvertible {
    var strongValuePairs: StringValuePair {
        var result = StringValuePair()

        result["id"]            = "\(self.id!)"
        result["author"]         = self.author
        result["subscriber"]     = self.subscriber
        result["message"]         = self.message
        result["postdate"]         = "\(self.postDate)"

        return result
    }
}

How do we take that existing structure and then give it the ability to be persisted or saved into the database? By using the Cassandra driver that we developed, and by extending the capability of the Bleet with the Model behavior. Then we can specify the name of the table where bleets are stored and you have a lot of persistence functions for free:


import Kassandra

extension Bleet : Model {
    static let tableName = "bleet"

    // other mapping goes here
}

For instance, when I create a new Bleet, I can go ahead and connect to the database and call .save() on the object. It’s very simple for persisting the data:


let bleet = Bleet(    id             : UUID(),
                    author         : "Robert",
                    subscriber     : "Chris",
                    message     : "I love Swift!",
                    postDate     : Date()

                )

try kassandra.connect(with: "blitter") { _ in bleet.save()
}

Let’s say that you have a list of strings of subscribers and you want to create a new bleet for each of them and then save it to the database. You can map the subscribers list onto the constructor of the bleets, and now you get a new list back, and then you can call save on it:


// Get the subscribers ["Chris", "Ashley", "Emily"]
let newbleets = [Bleet] = subscribers.map {
    return Bleet(    id             : UUID(),
                    author         : userID,
                    subscriber     : $0,
                    message     : message,
                    postDate     : Date())
}

newbleets.forEach { $0.save() { _ in } }

You can also chain the forEach at the end of the map function and turn this into one line of code.

For asynchronous error handling, often you’ll see APIs give you back either the value or the error, and there’s this sort of mutual exclusion between the two of them:


func doSomething(oncompletion: (Stuff?, Error?) -> Void) {
    
}

I recommend that you go ahead and exploit some of the properties of the language in order to ensure that mutual exclusion, and do something like this:


enum Result<T> {
    case success(T)
    case error(Error)

    var value: T? {
        switch self {
        case .success (let value): return value
        case .error: return nil
        }
    }

    // Do same for error
}

Where you have an enumeration of the result which is generic, and it could be a success with the object that you’re interested in or an error. And if you want to get the value back, then it either returns nil if there was an error, or the value back.

How do we get a list of bleets back? We can connect to the database, fetch all the bleets, and if there’s an error, then we call the onComplete with the error case. If you are able to get some results back, then you go ahead and create bleets out of them and then call the success handler:


func getBleets(oncomplete: Result<[Bleet]>) -> Void) {
    try kassandra.connect(with: "blitter") { _ in
        Bleet.fetch() { bleets, error in

            if let error = error {
                oncomplete( .error(error))
            }

            let result = bleets.flatMap() { Bleet.init(withValuePair:)}
            oncomplete( .success(result) )
        }

    }
}

We can also handle predicates too. If you want to get bleets back that match a certain author name, we can exploit some of the operator overloading properties that Swift has, where that == now gets evaluated as a predicate clause that can be applied in our query:


Bleet.fetch(predicate: "author" == user,
            limit: 50) { bleets. error in

        ///
}

Handle the requests (17:53)

Finally, I want to show you how to handle the requests:


getBleets { result in
    
    guard let bleets = result.value else {
        response.status(.badRequest).send()
        response.next()
        return
    }

    response.status(.OK)
        .send(json: JSON(bleets.stringValuePairs))
        response.next()
    }
}

When we get the bleets back, we have a result. We checked to see if that result was a success, if not then we can go ahead and append a bad request to the response type. That’s basically a 500 error. If it was successful, then it’s a 200 status code. That’s how we set those status codes using the .status() method.

Then when you’re ready to serialize the JSON you can do a .send() with the JSON. We already set the MIME type for you for the JSON type, and we use that string-value pairs to get us back that dictionary. Using SwiftyJSON, that dictionary will be turned into a JSON object, and serialized as a string.

Often times when a person will post a message up to the web server it will be in the form of JSON and we want to be able to extract some information from it.

In this case, we can extend the router request object, checking to see if there is a body in the document, whether there is JSON, and then going ahead and returning that JSON:


extension RouterRequest {
    
    var json: Result>JSON> {

        guard let body = self.body else {
            return .error(requestError("No body in the message"))
        }

        guard case let .json(json) = body else {
            return .error(requestError("Body was not formed as JSON"))
        }

        return json
    }
}

When we’re ready to save the bleet, then we can go ahead and get the message from the JSON and save it:


let userID = authenticate(request: request)

let jsonResult = request.json
guard let json = jsonResult.result else {
    response.status(.badRequest)
    next()
    return
}

let message = json["message"].stringValue

// Save the Bleets with author matching userID

Conclusion (19:30)

If you’re interested in other examples of using Kitura, we have built a to-do list app in multiple databases, such as MongoDB and Redis. And we also have a corresponding iOS app.

If you’re also interested in a more extended example that you can use with AngularJS, we have a BluePic web example that uses Kitura, CouchDB, object storage, and Watson. And we have both an iOS front-end and an AngularJS front-end.

Thank you very much. I hope you download Xcode 8 Beta, and the latest toolchain, and play around with Kitura.

Resources

About the content

This talk was delivered live in September 2016 at try! Swift NYC. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

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.

Robert Dickerson

Robert F. Dickerson is a lead software engineer in [email protected] at Austin, TX. He is focused on enriching the “Swift on the server” community by being a developer for the web framework “Kitura”, Swift server libraries and SDKs, and also sample applications. He has taught computer science courses at the University of Texas (Austin) and the College of William and Mary and has written numerous research papers about mobile computing, Internet of Things, and virtual reality. When not busy writing code, he is busy swing dancing at nights.

4 design patterns for a RESTless mobile integration »

close