Swift on the Server in 2023
Three years in the past I began to concentrate on Vapor, the preferred web-framework written in Swift, which served me very properly through the years, however now it’s time to begin a brand new chapter in my life.
As I discovered increasingly about how servers work I spotted that Vapor has it is personal execs and cons. The neighborhood modified so much in the course of the previous 3 years, some core members left and new individuals began to take care of the framework. I additionally had some struggles with the default template engine (Leaf) and lately I began to show away from the summary database layer (Fluent) too. One other ache level for me is the rising variety of dependencies, I barely use websockets & multipart-kit, however Vapor has these dependencies by default and you’ll’t eliminate them. 😢
Vapor has some very nice issues to supply, and for most people it is nonetheless going to be a fantastic selection for constructing backends for frontends (BFFs). For me, Vapor reached its limits and I needed to make use of one thing that feels a bit lighter. Somethings that’s modular, one thing that may be simply prolonged and suits my actual wants with out extra (unused) package deal dependencies.
This shiny new factor known as Hummingbird and it appears very promising. It was created by Adam Fowler who’s a member of the SSWG and in addition the primary writer of the Soto library (AWS Swift).
Hummingbird has a complete documentation obtainable on-line and a pleasant instance repository containing numerous demo apps written utilizing the Hummingbird Swift server framework. I imagine that the very best a part of the the framework is modularity & extensibility. By the way in which, Hummingbird works with out Basis, nevertheless it has extensions for Basis objects, it is a large plus for me, however possibly that is simply my private desire these days. Hummingbird may be prolonged simply, you could find some very helpful extensions below the Hummingbird undertaking web page, lengthy story quick it really works with Fluent and it is comparatively simple to get together with it when you’ve got some Vapor data… 🤔
Getting began with Hummingbird
To start with, there isn’t a toolbox or command line utility to assist the kickoff course of, however you’ll be able to all the time obtain the examples repository and use one of many initiatives as a place to begin. Alternatively you’ll be able to set every thing up by hand, that is what we’ll do now. 🔨
So as to construct a brand new software utilizing the Hummingbird framework it’s best to create a brand new listing and initialize a brand new Swift package deal utilizing the next instructions:
$ mkdir server && cd $_
$ swift package deal init --type executable
¢ open Bundle.swift
This can create a brand new Swift package deal and open the Bundle.swift
file in Xcode. You need to use your personal editor in case you don’t love Xcode, however both manner you may have so as to add Hummingbird to your package deal manifest file as a dependency. We will setup an App goal for the appliance itself, and a Server goal for the primary executable, which can use the appliance and configure it as wanted.
import PackageDescription
let package deal = Bundle(
identify: "server",
platforms: [
.macOS(.v10_15),
],
dependencies: [
.package(
url: "https://github.com/hummingbird-project/hummingbird",
from: "1.0.0"
),
.package(
url: "https://github.com/apple/swift-argument-parser",
from: "1.0.0"
),
],
targets: [
.executableTarget(
name: "Server",
dependencies: [
.product(
name: "ArgumentParser",
package: "swift-argument-parser"
),
.target(name: "App"),
]
),
.goal(
identify: "App",
dependencies: [
.product(
name: "Hummingbird",
package: "hummingbird"
),
.product(
name: "HummingbirdFoundation",
package: "hummingbird"
),
],
swiftSettings: [
.unsafeFlags(
["-cross-module-optimization"],
.when(configuration: .launch)
),
]
),
.testTarget(
identify: "AppTests",
dependencies: [
.product(
name: "HummingbirdXCT",
package: "hummingbird"
),
.target(name: "App"),
]
),
]
)
Please create the mandatory file and listing construction, as listed beneath, earlier than you proceed to the following steps. It is extremely necessary to call issues as they seem, in any other case SPM will not work and the undertaking will not compile. Anyway, the undertaking construction is kind-of Vapor-like as you’ll be able to see. 💧
.
├── Bundle.resolved
├── Bundle.swift
├── README.md
├── Sources
│ ├── App
│ │ └── HBApplication+Configure.swift
│ └── Server
│ └── principal.swift
└── Assessments
└── AppTests
└── AppTests.swift
The subsequent step is to create the primary entry level for the appliance. For this goal Hummingbird makes use of the Swift Argument Parser library. Place the next contents into the principal.swift
file:
import ArgumentParser
import Hummingbird
import App
struct HummingbirdCommand: ParsableCommand {
@Choice(identify: .shortAndLong)
var hostname: String = "127.0.0.1"
@Choice(identify: .shortAndLong)
var port: Int = 8080
func run() throws {
let app = HBApplication(
configuration: .init(
handle: .hostname(hostname, port: port),
serverName: "Hummingbird"
)
)
attempt app.configure()
attempt app.begin()
app.wait()
}
}
HummingbirdCommand.principal()
The HummingbirdCommand
has two choices, you’ll be able to setup a customized hostname and port by offering these values as command line choices (I will present it afterward), the appliance itself will setup the handle utilizing the enter after which it will begin listening on the desired port.
The configure
technique comes from the App goal, that is the place you’ll be able to customise your server occasion, register route handlers and stuff like that, similar to you’ll do that in Vapor. The principle distinction is that Hummingbird makes use of the HB namespace, which is fairly helpful, and the configure technique is written as an extension. Let’s write it and register a primary route handler. 🧩
import Hummingbird
import HummingbirdFoundation
public extension HBApplication {
func configure() throws {
router.get("https://theswiftdev.com/") { _ in
"Hi there, world!"
}
}
}
That is it. Now it’s best to have the ability to run your server, you’ll be able to press the Play button in Xcode that’ll begin your software or enter one of many following instructions into the Terminal software:
swift run Server
swift run Server --port 3000
swift run Server --hostname 0.0.0.0 --port 3000
swift run Server -p 3000
swift run Server -h 0.0.0.0 -p 3000
LOG_LEVEL=discover swift run Server -p 3000
swift construct -c launch
cp .construct/launch/Server ./Server
LOG_LEVEL=discover ./Server -p 3000
You possibly can set these values in Xcode too, simply click on on the server scheme and choose the Edit Scheme… menu merchandise. Just be sure you’re on the Run goal, displaying the Arguments tag. Merely provde the Arguments Handed On Launch choices to set a customized hostname or port and you’ll set the log stage by including a brand new merchandise into the Surroundings Variables part.
If you would like to unit take a look at your software, I’ve acquired a excellent news for you. Hummingbird additionally comes with a pleasant utility instrument referred to as HummingbirdXCT
, which you’ll be able to simply setup & use if you would like to run some checks towards your API. In our undertaking, merely alter the AppTests.swift
file.
import Hummingbird
import HummingbirdXCT
import XCTest
@testable import App
ultimate class AppTests: XCTestCase {
func testHelloWorld() throws {
let app = HBApplication(testing: .stay)
attempt app.configure()
attempt app.XCTStart()
defer { app.XCTStop() }
attempt app.XCTExecute(uri: "https://theswiftdev.com/", technique: .GET) { response in
XCTAssertEqual(response.standing, .okay)
let expectation = "Hi there, world!"
let res = response.physique.map { String(buffer: $0) }
XCTAssertEqual(res, expectation)
}
}
}
As a substitute of making the appliance from the primary entry level, we are able to arrange a brand new HBApplication
occasion, import the App
framework and name the configure technique on it. the XCT framework comes with a customized XCTStart and XCTStop technique, and you’ll execute HTTP requests utilizing the XCTExecute
perform. The response is on the market in a completion block and it is doable to look at the standing code and extract the physique utilizing a handy String initializer.
As you’ll be able to see Hummingbird is kind of just like Vapor, nevertheless it’s light-weight and you’ll nonetheless add these further issues to your server when it’s wanted. Hummingbird looks like the following iteration of Vapor. I actually do not know if Vapor 5, goes to repair the problems I am presently having with the framework or not, however I do not actually care, as a result of that launch will not occur anytime quickly.
In any case, I will use Hummingbird to construct some superb stuff (hopefully in public too) so if you’re simply observe me on Twitter or Mastodon to be sure you get all the most recent information.