Kitura

Intro to Kitura

Kitura is a lightweight web framework that allows you to easily build web services with complex routes. Much of its design was inspired by Express.js based on the success of its overall design in particular URL routing and pluggable middleware. Kitura takes these principles and adds the advantages of Swift, which include:

  • Compiled native-code
  • Type-safety
  • Optionals
  • Multi-programming based on Grand Central Dispatch

The combination of these features provides the foundation for an extendable and robust web framework.

This enables both mobile frontend and backend portions of an application to be written in the same language.

In addition, web developers who are used to other routing frameworks such Express, Sinatra, or Flask will be able to easily create or port web apps over to the Swift language.

Creating a to-do list app

For this tutorial, we follow the API created for web-based to-do lists TodoMVC. You can view many framework and language implementations of this same API on TodoBackend. We will implement our own in Swift. If you want to grab the source of the finished project:

Kitura-TodoList

Along the way, you will learn to:

  1. Create GET, POST, PUT, DELETE routes with URL parameters
  2. Parse JSON messages
  3. Handle concurrency on shared resources
  4. Create custom middleware that allows cross-origin requests

1) Set up your Mac or Linux environment to run Kitura

Kitura applications support both Mac OS X and Linux. Because of the differences in the implementations of Foundation and libDispatch on each platform, the instructions are a bit different if running on Linux.

2) We will create a directory structure:

  +-- Package.swift
  +-- Sources
  |   +-- TodoList
  |       +-- main.swift
  |       +-- Controllers
  |           +-- App.swift
  |       +-- Models
  |           +-- TodoCollection.swift
  |           +-- TodoCollectionArray.swift
  |           +-- TodoItem.swift
  

The Swift Package Manager expects the files to be laid out in a particular manner. Every Swift project must have a directory called Sources. For every subdirectory inside of Sources, the package manager will either build an executable file or a Swift module. For example, since our subdirectory is called TodoList with a main.swift file inside, the build will produce a TodoList executable. Our choice to put the TodoCollection and TodoItem inside of Models and App.swift inside of Controllers is only a matter of organizing our code, and not enforced by Kitura or the Swift Package Manager.

3) Create a Package.swift file:

import PackageDescription

let package = Package(
    name: "TodoList",
    dependencies: [
      .Package(url: "https://github.com/IBM-Swift/Kitura.git", majorVersion: 1, minor: 0),
      .Package(url: "https://github.com/IBM-Swift/HeliumLogger.git",  majorVersion: 0, minor: 16)
    ]
)

4) Create a main.swift file:

In order to use Kitura in your application, you must add the Kitura module to your application:

import Kitura

In the main.swift file, we will set up the Kitura router as well as our collection of to-do items, then instruct the HTTPServer to listen to port 8090 for new connections.

///
/// The Kitura router
///
public let router = Router()

We will store the to-do items in a shared resource DAO (Data Access Object) called ‘todos’. The DAO will be a protocol for which more powerful databases could be supported in the future. In future blog posts, we will leverage Cloudant/CouchDB using this TodoCollection DAO.

///
/// Setup the database
///
let todos: TodoCollection = TodoCollectionArray()

Create a file called Controllers/App.swift. Here we will define function called setupRoutes that our main.swift file will call to set up our routes. We will pass the reference to the router and the data access object with this function:

///
/// Call a helper function to create routes in App.swift
///
setupRoutes( router, todos: todos )

Back on your main.swift file at the end, set up an HTTP server that listens to port 8090:

///
/// Listen to port 8090
///
Kitura.addHTTPServer(onPort: 8090, with: router)
Kitura.run()

5) Set up middleware to parse incoming JSON in the body of client requests in your App.swift file in setupRoutes:

router.use("/*", middleware: BodyParser())

BodyParser gives routers the ability to parse the JSON message describing the title of the item and its position in the list.

6) Create a route to return a JSON containing all the to-do list items:

Kitura supports multiple HTTP method types such GET, POST, PUT, and DELETE. We match incoming requests on the regular expression `/`, and when this match is made, the supplied closure is invoked. The arguments entering the closure are the request, response, and next element. The request contains the header information and the body. The response object is used to serialize data, JSON, or plaintext out to the client. Content types can be specified here as well. The method ‘next’ is a closure that should be invoked at the end of your handler block. By invoking ‘next,’ the router will proceed to process the next element that matches the regular expression. If you want to ensure that no other matching routes are handled after the current route, omit the next() call.

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

    let json = JSON(TodoCollectionArray.serialize(todos.getAll()))

    response.status(HttpStatusCode.OK).sendJson(json)

    next()
}

In this code segment, the library SwiftyJSON is used to convert the Dictionary representation of the to-do list to a JSON String. Calling sendJson() will send the String and set the response’s header ‘Content-Type’ to ‘application/json’.

7) Create a route to add a to-do list item:

When the client needs to add a to-do list item, they add a JSON string to the body of the request, which describes the title of the item, the order (position) in the list where it needs to be inserted, and whether the task has been completed or not. Therefore, in the handler we parse the JSON message and get each of the fields:

///
/// Add a to-do list item
///
router.post("/") {
    request, response, next in

    if let body = request.body {

            if let json = body.asJson() {

                let title = json["title"].stringValue
                let order = json["order"].intValue
                let completed = json["completed"].boolValue

                let newItem = todos.add(title, order: order, completed: completed)

                let result = JSON(newItem.serialize())

                response.status(.OK).sendJSON(result)

            }
        } else {
            Log.warning("No body")
            response.status(.badRequest)
        }

}

In the above code, request.body returns the body of the request as an Optional. If the Optional is nil, we return a Bad Request response. If the body exists, we attempt to parse the JSON; if successful, we use the DAO to write the item to the database and return the TodoItem of the newly created item back to the client.

8) Create a DAO protocol in Models/TodoCollection.swift:

protocol TodoCollection {
    var count: Int { get }
    func clear()
    func getAll() -> [TodoItem]
    static func serialize(items: [TodoItem]) -> [JSONDictionary]
    func add(title: String, order: Int, completed: Bool) -> TodoItem
    func update(id: String, title: String?, order: Int?, completed: Bool?) -> TodoItem?
    func delete(id: String)
}

9) Create the TodoItem model in Models/TodoItem.swift:

For the model object, we need the ability to easily serialize the struct to a Swift Dictionary. We create a serialize function that handles this conversion for us:

struct TodoItem {
    var id: String = ""
    var order: Int = 0
    var title: String = ""
    var completed: Bool = false
    var url: String = ""

    func serialize() -> JSONDictionary {
        var result = JSONDictionary()
        result["id"] = id
        result["order"] = order
        result["title"] = title
        result["completed"] = completed
        result["url"] = url
        return result
    }
}

10) Implement the TodoCollection DAO in Models/TodoCollectionArray.swift

For our implementation, we will not be using a database or in-memory database. We have libraries for integration with Redis and CouchDB.

Instead, we will store the items in a Dictionary in memory.

private var _collection = [String: TodoItem]()

Because Kitura is multithreaded, we need to be careful with writes to the shared object TodoCollection. We will handle writes synchronously using a Queue and a semaphore. You can create a queue using:

let writingQueue = DispatchQueue(label: "writingQueue", qos: .userInitiated, attributed: .concurrent)
let semaphore = DispatchSemaphore(value: 1)

We directly make calls to the Grand Central “Dispatch” library for handling synchronous and asynchronous operations. At this point, Kitura’s threading model is simple. There is one thread continually listening for new connections; when a connection has been established, a thread is spawned to handle the ClientRequest. In the future, we expect to move to thread pooling.

In the following code, we define a closure that modifies the collection and pass it to the synchronous queue:

func add(title: String, order: Int, completed: Bool) -> TodoItem {

        var original: String
        original = String(self.idCounter)
        
        let newItem = TodoItem(id: original,
            order: order,
            title: title,
            completed: false,
            url: self.baseURL + "/" + original
        )

        writingQueue.sync() {

            semaphore.wait()
            self.idCounter+=1
            self._collection[original] = newItem
            semaphore.signal()

        }

        Log.info("Added \(title)")

        return newItem

    }

After executing that code, we return the TodoItem of the post that was created so that it can be serialized as JSON and sent back to the client.

To delete objects that match an id:

func delete(id: String) {
        writingQueue.sync() {
            semaphore.wait()
            self._collection.removeValueForKey(id)
            semaphore.signal()
        }
}

The serialize function iterates through the List and converts the list of TodoItems to a list of Dictionaries:

static func serialize(items: [TodoItem]) -> [JSONDictionary] {
  return items.map { $0.serialize() }
}

Enable cross-origin requests to your backend

We will be using an existing front-end JavaScript app that is hosted at http://todobackend.com. Since that app needs to make requests to your backend that’s running on a different domain, we need to enable cross origin requests. To do this, we add to the response headers for every route in our application to return the header Access-Control-Allow-Origin: *. Routing middleware serves this purpose well since we can intercept a request, add these headers, then hand it over to a handler that’s registered for a particular route and method. Note the next() invocation. This is important so that other routing elements can be matched next.

Add to your Controllers/App.swift file the following class definition:

class AllRemoteOriginMiddleware: RouterMiddleware {
    func handle(request: RouterRequest, response: RouterResponse, next: () -> Void) {
        response.setHeader("Access-Control-Allow-Origin", value: "*")
        response.setHeader("Access-Control-Allow-Headers", value: "accept, content-type")
        response.setHeader("Access-Control-Allow-Methods", value: "GET,HEAD,POST,DELETE,OPTIONS,PUT")
        next()
    }
}

router.use("/*", middleware: AllRemoteOriginMiddleware())

11) Run your server:

You are set to compile your application. You should use the SwiftPM to fetch the dependencies and build your project. First, run:

   swift build
   

SwiftPM will place your the dependencies in your Packages directory and next build your project in the .build/debug directory. Once the project is built, run the executable:

   ./.build/debug/TodoList
   

After the executable is running and listening for connections on localhost:8090, you can test out the application by using the TodoBackend web page. This web page can use any backend that implements the API by supplying the address to your backend. View your to-do list at the following address:

http://todobackend.com/client/index.html?http://localhost:8090

When this is done, you should have a to-do list that looks like this:

todo-website

Next steps…

This concludes this tutorial. Keep in mind that Kitura is under active development. If you want to continue with this application, the next steps are to:

  1. Add support for editing existing items
  2. Add support for deleting items
  3. Deploy to Bluemix

39 comments on"Build End-to-End Cloud Apps using Swift with Kitura"

  1. That’s a dream come true… I was waiting for this.

    Is it possible to specify more than one closure to handle a route?

    A series of trailing closures actually, for instance in the following snippet I could go on adding as many callback handlers as I like. If not please consider adding as it will provide an amazing pattern to work with.

    app.get(‘/example/b’, function (req, res, next) {
    console.log(‘the response will be sent by the next function …’);
    next();
    }, function (req, res) {
    res.send(‘Hello from B!’);
    });

    source: http://expressjs.com/en/guide/routing.html

    • We did a competitive analysis with ExpressJS and discovered exactly what you brought up. We don’t currently support that, however we are adding support for that soon.

      • Charles Dowd July 04, 2016

        Have you tried to use Bolts Swift framework — open source from Facebook? This is promises framework for Swift. Very handy for those complicated async callback nests of hell you can get into.

        • Yeah, I am familiar with the Bolts framework- but only that it’s a dependency for the Facebook SDK. I will have to check it out. I agree that we should be using some promises here and that can improve our callback nesting. I am not familiar with a Swift 3 compatible Promises library out there yet.

  2. Do you plan to add annotations on methods Spring MVC way ? It would be an great addition to the framework.Thank you !

    • The Swift language does not really support something like decorator annotations like Spring uses. Good idea, though. Any thoughts on how we might be able to add something like that?

  3. Bishal Ghimire February 27, 2016

    Github for Kitura does not provided any step by step for Mac with XCode 7.2 to use and install.
    I am stopped at exactly mentioned in this blog –
    https://marc.ttias.be/swift-users/2016-02/msg00085.php

  4. I’m getting this error when I try to compile:
    ../Kitura-TodoList/Packages/Kitura-HttpParserHelper-0.3.1/utils.h:22:10: error: ‘http_parser.h’ file not found
    #include “http_parser.h”
    I’m using the swift-DEVELOPMENT-SNAPSHOT-2016-02-25-a on my Mac(OS X El Capitan 10.11.2)
    Any Ideas?
    Thanks

    • Hi Jay, install with Homebrew http_parser, then build with ‘swift build -Xcc -fblocks -Xswiftc -I/usr/local/include -Xlinker -L/usr/local/lib`

    • padalingam March 31, 2016

      Jay can you please help me on this i am also face the same issue which u faced… some http_parser.h file is missing and some /Users/padalingam.a/Desktop/TodoList/Packages/Kitura-net-0.6.0/Sources/KituraNet/HttpParser/HttpParser.swift:18:8: error: could not build Objective-C module ‘CHttpParser’
      import CHttpParser

      and

      /Users/padalingam.a/Desktop/TodoList/Packages/CHttpParser-0.0.2/shim.h:22:10: error: ‘http_parser.h’ file not found
      #include

      I have installed home brew and ran this command also

      its giving me a error as
      EXIMR-IM-335:TodoList padalingam.a$ swift build -Xcc -fblocks -Xswiftc -I/usr/local/include -Xlinker -L/usr/local/lib
      error: invalid usage: unknown argument: -X
      Enter `swift build –help’ for usage information.

  5. Thanks. Worked great in docker. Having trouble installing on linux with Ubuntu 15.10 image and swift-DEVELOPMENT-SNAPSHOT-2016-03-01-a-ubuntu15.10

    Getting error error: no such module ‘Dispatch’ import Dispatch

    I followed the linux installation instructions here https://github.com/IBM-Swift/Kitura#swift-version

    Any help would be appreciated.

  6. Ignore my last comment. Got it working. Thanks.

  7. Helge Heß March 16, 2016

    You synchronize write access to the objects using your `writingQueue`. I’m a little confused why you don’t synchronize read access. Are Swift data structure operations atomic? I wouldn’t think so?

    P.S.: Also:

    var count: Int {
    return _collection.keys.count
    }

    Shouldn’t that be a simple

    var count : Int { return _collection.count }

    ? Why generate the keys array wrapper?

    • Yes, absolutely, the reads should be synchronized as well. The latest version of this article I am writing now uses external backends (CouchDB and Redis) so this will no longer be as relevant.

  8. Padalingam March 31, 2016

    When i tried to compile this sample i got this piece of error..

    Compiling Swift Module ‘KituraNet’ (12 sources)
    :1:9: note: in file included from :1:
    #import “shim.h”
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/CHttpParser-0.0.2/shim.h:22:10: error: ‘http_parser.h’ file not found
    #include
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/Kitura-net-0.6.0/Sources/KituraNet/HttpParser/HttpParser.swift:18:8: error: could not build Objective-C module ‘CHttpParser’
    import CHttpParser
    ^
    :1:9: note: in file included from :1:
    #import “shim.h”
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/CHttpParser-0.0.2/shim.h:22:10: error: ‘http_parser.h’ file not found
    #include
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/Kitura-net-0.6.0/Sources/KituraNet/HttpParser/HttpParser.swift:18:8: error: could not build Objective-C module ‘CHttpParser’
    import CHttpParser
    ^
    :1:9: note: in file included from :1:
    #import “shim.h”
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/CHttpParser-0.0.2/shim.h:22:10: error: ‘http_parser.h’ file not found
    #include
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/Kitura-net-0.6.0/Sources/KituraNet/HttpParser/HttpParser.swift:18:8: error: could not build Objective-C module ‘CHttpParser’
    import CHttpParser
    ^
    :1:9: note: in file included from :1:
    #import “shim.h”
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/CHttpParser-0.0.2/shim.h:22:10: error: ‘http_parser.h’ file not found
    #include
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/Kitura-net-0.6.0/Sources/KituraNet/HttpParser/HttpParser.swift:18:8: error: could not build Objective-C module ‘CHttpParser’
    import CHttpParser
    ^
    :1:9: note: in file included from :1:
    #import “shim.h”
    ^
    /Users/padalingam.a/Desktop/TodoList/Packages/CHttpParser-0.0.2/shim.h:22:10: error: ‘http_parser.h’ file not found
    #include

  9. padalingam March 31, 2016

    I have already installed Homebrew.. i run this command
    swift build -Xcc -fblocks -Xswiftc -I/usr/local/include -Xlinker -L/usr/local/lib

    Its giving me this error

    error: invalid usage: unknown argument: -X
    Enter `swift build –help’ for usage information.

    I don’t know what mistake i made here.. can u please give me a solution?

  10. Hi,

    nice start. I have a question. If I compare this with a decent Java framework, in Java I am able to specify a Java class and the framework does the serialization/deserialization automatically quickly and easily

    1. Java class conference doesn’t need serialization method
    2. Json is automatically generated based on @Produces annotation

    Example

    @Path(“demo”)
    class MyResource {
    @GET
    @Produces(“application/json”)
    @Path(“{id}”)
    public Conference getById(@PathParam(“id”) long conferenceId) throws Exception {
    System.out.println(“Conference ID = ” + conferenceId);
    return null;
    }
    }

    Are you planning on adding something similar?
    Best
    Frank

    • I don’t think we have plans currently to use something like decorators to automatically serialize and deserialize JSON. Thanks for feature request, though.

  11. What about development process?
    How do I do stuff likes : auto restart server when code changed, kinds of “nodemon” or Django runserver ?

    • We currently do not support automatically deploying changed code to a running server. The application logic and HTTP server gets compiled into the executable itself.

  12. Hi,
    I’ve encountered this error when running this command to compile the application.
    swift build -Xcc -fblocks -Xlinker -rpath -Xlinker .build/debug

    fatal: repository ‘https://github.com/IBM-Swift/Kitura-router.git/’ not found
    Any thoughts?
    Thanks

    • I’ve found that this post is out of date.
      At this link https://github.com/IBM-Swift/Kitura-TodoList/blob/master/Package.swift
      I saw that Package.swift now looks like this.

      import PackageDescription

      let package = Package(
      name: “TodoList”,
      dependencies: [
      .Package(url: “https://github.com/IBM-Swift/Kitura.git”, majorVersion: 0, minor: 13),
      .Package(url: “https://github.com/IBM-Swift/HeliumLogger.git”, majorVersion: 0, minor: 7),
      .Package(url: “https://github.com/IBM-Swift/Swift-cfenv”, majorVersion: 1)
      ]
      )

      So there is a need to update the post with correct information.

    • We have changed Kitura-Router to now be Kitura. I am updating the article to reflect this change.

  13. León Yannik López Rojas June 20, 2016

    Hello Robert, Sorry I’m not pro at all, could you please please make a video tutorial??? that will be much appreciated !!!!!! please please

  14. This is amazing – especially debug integration within Xcode -> Thank you for initiating this exciting project!

    Q: Won’t having a thread per client explode your server / wallet (cloud services) if you anticipate traffic? (Is there a facility on the IBM cloud to mitigate this somewhat – automatically: auto-load balancing etc)

    Q: Is there support for ‘real-time’ protocols such as (wish) reliable web-sockets at scale ?

    • Thanks! Yeah, I agree that debug to be one of the best things for developing web servers in Swift, it’s so much better than my experience with developing for Ruby, Python, and NodeJS.

      Having a thread per client is what actually what a lot of other web servers require, too. It’s somewhat unavoidable. However, we can do a better job with using lower-level event handling signals to be aware when a socket has data on it for reading. We have used Bluemix’s load balancing on multiple instances with a lot of success. However, the version of this application in this app does not have a shared database backend, so each instance will store a different version of a todolist depending on which instance was selected by the load balancer. We are releasing a new tutorial soon that will demonstrate MongoDB, Redis, CouchDB, and other databases as examples.

      Agreed, we should have web socket support in Kitura. Either officially, or through a library. It is on our list for features we would like to support.

  15. Great hammer of Thor, that is polwufrely helpful!

  16. Hi,

    I have encountered below error while compiling a simple “Hello World” project.

    1. /myFirstProject/Packages/SwiftyJSON-10.0.2/Source/SwiftyJSON.swift:1155:57: error: cannot call value of non-function type ‘NSDecimalNumber’
    if decimal == NSDecimalNumber.notANumber() { // indicates parse error

    2. /myFirstProject/Packages/SwiftyJSON-10.0.2/Source/SwiftyJSON.swift:1156:48: error: cannot call value of non-function type ‘NSDecimalNumber’
    return NSDecimalNumber.zero()

    3. /myFirstProject/Packages/Kitura-net-0.24.0/Sources/KituraNet/SPIUtils.swift:82:44: error: cannot call value of non-function type ‘Calendar’
    let calendar = Calendar.current()
    :0: error: build had 2 command failures
    error: exit(1):

    I am running this code on MacOS 10.11.6 Xcode 8 Beta3 with Swift “swift-DEVELOPMENT-SNAPSHOT-2016-06-20-a-osx”

    There are other latest snapshots are also available but as per this documentation “https://github.com/IBM-Swift/Kitura?cm_mc_uid=56044340393514697255223&cm_mc_sid_50200000=1470114617#getting-started”

    —-
    This branch of Kitura requires the DEVELOPMENT-SNAPSHOT-2016-06-20-a version of Swift 3 trunk (master). You can download this version at swift.org. Kitura is unlikely to compile with any other version of Swift.
    ——

    Any help would be appreciated.

  17. Is this application valid currently. I am trying to build this on an Ubuntu 14.04 box and I am getting “Failed to clone https:// … Kitura-router.git. Looks like that package is not available anymore. Any insights?

Leave a Reply