Vapor vs. Kitura: Choosing a Server-Side Swift Framework

If you’re coming from iOS development and considering server-side Swift, one of the first questions you’ll likely ask is “Which framework should I choose, and why?” In this post we’ll compare the two most popular Server-side Swift frameworks: Vapor and Kitura. By Brian Schick.

Leave a rating/review
Save for later
Share
You are currently viewing page 2 of 3 of this article. Click here to view the first page.

Differences

Both Vapor and Kitura implement and extend Swift’s intrinsic benefits in their own specific ways. Each framework has a “flavor” with several aspects that make each framework unique.

Heritage

Kitura has been sponsored and controlled by IBM since its inception. Kitura is fully open sourced under the Apache license, but IBM’s corporate sponsorship and resources are clearly evident throughout the framework. By contrast, Vapor came to life as an independent framework without corporate affiliation or sponsorship. While it picked up its first corporate sponsors shortly afterwards, Vapor’s independence and developer focus clearly show its own approach and personality.

Priorities and Philosophy

The effect of these different lineages can be seen in the differing approaches and priorities of the two frameworks as they’ve evolved and responded to Swift’s rapid evolution. Kitura consistently favors predictability and backwards compatibility. Its API has remained remarkably stable given Swift’s own aggressive evolution during the last few years, and it’s a point of pride for team members that code written in Kitura 1.0 still builds and runs to this day.

Kitura’s emphasis on continuity over newness gives it a somewhat conservative feeling. While this may disappoint some developers, it brings continuity and helpful tools essential in the enterprise, such as on by default health check route that makes it straightforward to monitor Kitura web app uptime and performance under load.

On the other hand, Vapor’s developer-centric roots mean that it tends to move more aggressively whenever new Swift features and technologies are released. Overall, the Vapor API has shown significantly more volatility between each version than Kitura, and both Vapor 2 and 3 (the current version) have featured deep reworkings of both core components and the exposed API.

This has meant that apps developed in one version have typically required significant rewrites to run in the next. Whether this is a pattern or an anti-pattern depends on one’s priorities. In the large and highly active Vapor developer community, these sweeping changes have been generally accepted as the cost of rapid evolution and improvement.

The same difference is reflected in how the two frameworks responded to the release of the highly regarded low-level SwiftNIO framework in early 2018. The Vapor core team integrated it into the then-upcoming version 3 right away! In a 2018 interview with Paul Hudson, Vapor founder and lead developer Tanner Nelson revealed that the framework had anticipated and prepared for this rapid adoption.

Conversely, the Kitura team took a more cautious approach and spent several months stress testing NIO and ensuring that performance edge cases were fully tested and resolved before formally adopting it later that year.

Approach to APIs

A similar difference in approach plays out in the way the two platforms write and expose APIs. Vapor has been “full stack Swift” from the very start. Whether you’re writing routing code, interacting with your data sources, or templating your web pages, you’ll work in Swift.

Vapor APIs are built from the ground up to reflect the best practices of writing Swift code. This makes for a consistent Swift-y experience throughout the platform that’s likely to appeal strongly to iOS developers wanting to extend their Swift expertise to the server. In many cases — such as with Vapor’s native Swift database drivers — this eliminates any need for non-Swift dependencies.

Kitura approaches APIs from a wider industry perspective, in which JavaScript platforms like Node, Express and even Java tools are widely used. The Kitura team has chosen to base many APIs not primarily on Swift standards, but rather on standards from this broader web community. For example, its template language Stencil takes its syntax from the popular Django and Mustache tools, and its overall syntax was modeled on Express, which is used in a wide array of Node.js web frameworks.

This same perspective shows through once more in Kitura’s tendency to use the best available tools for a given task, regardless of their language. This would be an anti-pattern in the Vapor community, where all core stack elements are implemented in Swift. But there’s wisdom in Kitura’s approach that becomes apparent from an enterprise perspective. And experienced web developers will likely appreciate an approach that lets them take advantage of Swift’s best features while also being able to leverage conventions and tools they’ve already mastered elsewhere.

Take a look at the implementation style of both frameworks.

Programming Style

Once more, Vapor is more aggressively Swift-y and leading-edge than Kitura, here.

In its current version 3, Vapor syntax prominently features heavy use of promise-like Futures as a core of the version’s async performance optimizations and its inclusion of SwiftNIO. This, in turn, makes Vapor code more overtly functional in style, with extensive use of nested closures and functionally oriented methods like map() and flatMap() in evidence everywhere.

This tends to give Vapor a distinctively abstract flavor, as code almost universally deals with manipulating, awaiting and reasoning about async futures that may or may not be fulfilled in real time. For example, here’s a simple Vapor route:

func getAllHandler(_ req: Request) throws -> Future<[User]> {
  return User.query(on: req).all()
}

Here’s a more complex route that combines async and functional traits:

func userHandler(_ req: Request) throws -> Future<View> {
  return try req.parameters.next(User.self)
    .flatMap(to: View.self) { user in
      return try user.acronyms
        .query(on: req)
        .all()
        .flatMap(to: View.self) { acronyms in
          let context = UserContext(title: user.name, user: user, acronyms: acronyms)
          return try req.view().render("user", context)
      }
  }
}

In contrast, even though Kitura also leverages NIO and async, it exposes this less in its API. As a result, Kitura coding — and routing in particular — tends to look more “concrete” than Vapor’s.

For example, here’s a sample route in Kitura:

func getEntries(user: UserAuth,
                query: JournalEntryParams?,
                completion: @escaping ([JournalEntry]?, RequestError?) -> Void) {
  JournalEntry.findAll(matching: query, for: user, completion)
}

Performance

How do the two frameworks compare in performance? Unfortunately, as we’ve already seen, this question can’t be objectively answered at this time, as there’s not yet a standardized set of benchmarks we can apply to the two frameworks, and the best benchmarks we currently have come from the early days of both platforms.

Both frameworks have evolved heavily since these early days — investing heavily in core performance optimizations — and community consensus is that the earlier documented performance advantages have only increased with time.

When we compare Kitura and Vapor head to head, we know that both frameworks take heavy advantage of Swift’s most performant aspects, and that both have made excellent use of more modern tools like SwiftNIO. But beyond this, there’s little, if any, objective empirical evidence to prefer one over the other: Both have been extensively battle tested, with very positive subjective reports from the field. But keep your eye on this space… there’s a good chance that you’ll see new sets of benchmarks in the near future!

What’s it like to implement an app with these frameworks?