Blog

Latest Post

Open-Sourcing The Swift Talk Backend

Today, we are open-sourcing the Swift Talk backend, written in Swift.

The Ruby Beginnings

Before launching Swift Talk, our weekly video series, we spent much of our time trying to figure out the best format for an episode. We experimented with many pilot recordings, discarding every one of them before settling on our current conversational style. To help us focus on the content we decided to outsource our backend development, working with an external contractor to build the site in Rails. This was great for launching, but very quickly our limited Rails experience, and the major differences between Swift and Ruby, made changes to the backend very time-consuming.

When refactoring in Swift, we rely heavily on the strong, static type system. For example, this allows us to change a method signature, then rely on the compiler to guide us through all call sites and any tests that are in place. In Ruby or Rails, you can’t rely on a static type system when making changes. Instead, you rely exclusively on an extensive test suite, and your knowledge of the code base and the frameworks to guide your refactoring.

Ruby on Rails also relies heavily on meta-programming, which makes it possible to achieve a lot with very little code. Likewise, if you’re not intimately familiar with the framework it can be quite hard to trace through your application and understand what is happening.

Finally, even for such a simple project, our Ruby code base had a lot of dependencies β€”Β 75 to be exact (some were transitive, but most were direct dependencies). Over time these needed to be updated, and we had to make sure that all the dependencies and our code kept working together as expected.

Rewriting the Backend in Swift

After finishing our latest book, App Architecture, we decided to spend our newly quiet days tackling the backend rewrite. A rewrite would have no direct business value, but even so, the project would be fun and interesting, and this can be reason enough. We wanted to prove to ourselves that Swift was feasible as a backend language, that we could rely on the type system for refactoring, and that we could make do with minimal dependencies.

Over the last few months, all of these have turned out to be true.

Preparing the code for public release, we wanted to clean it up and make sure that it was readable. We extracted non-app-specific parts into frameworks, conducted some large-scale renaming, and refactored many things. The compiler and type system were a huge help.

We now only have six direct dependencies: the web server library SwiftNIO, a Postgres client, a Markdown library, an XML parsing library, an encryption library and SourceKitten for source code highlighting. Most notably, we’re not using a web framework, but are building directly on top of SwiftNIO, so that we can structure the code in our own way.

Good News: it Works!

So far, we love working on this code base. With so few dependencies we own almost all of our code, and can design and extend it in ways that works for us. We are no longer stuck with a Rails backend that we found hard to change, and we can add features quickly, such as the new team member signup pages.

For us, the biggest downside is that Swift on Linux isn’t battle-tested. We ran into a few bugs (most notably around URLSession/URLRequest behavior), but they were easily solved or worked around. Battle-testing has to start somewhere, and we expect this will only improve with time.

We aren’t the first to build a project like this in Swift. Point-Free was written in Swift at launch, and it continues to be an inspiration β€” it’s no coincidence that our Dockerfile is so similar to theirs.

We’re planning to add more features to Swift Talk, and we hope that open-sourcing our code base will inspire other people to write their backend in Swift as well.

The full source code is on GitHub.

If you’d like to learn more, our latest public episode starts a new series exploring what it’s like to work with the backend. The rewrite also informs many of the episodes in our Server-Side Swift collection.

To follow the series, you can subscribe.

Enjoy! 😊

Previous Posts

Swift Tip: An NSScanner Alternative

For one reason or another, we find ourselves writing small scanners and parsers quite often. Sometimes we parse a specific file format, or a small expression language, or just a file name that conforms to a certain naming scheme.

One approach is to use the Scanner class from Foundation (it used to be called NSScanner). A Scanner instance stores the scanned String and a scan location, which is the position in the string. For example, scanning a single character just returns the character at the current scan location and increases the scan location.

In pure Swift, there’s another type that stores a String and an offset into that String: Substring. Instead of using a scanner, we could write mutating methods on Substring. As an illustration, here are three such methods. The first matches a character that matches a certain condition, the second scans exactly count characters, and the last scans a specific prefix:

extension Substring {
    mutating func scan(_ condition: (Element) -> Bool) -> Element? {
        guard let f = first, condition(f) else { return nil }
        return removeFirst()
    }

    mutating func scan(count: Int) -> Substring? {
        let result = prefix(count)
        guard result.count == count else { return nil }
        removeFirst(count)
        return result
    }

    mutating func scan<C>(prefix: C) -> Bool where C: Collection, C.Element == Character {
        guard starts(with: prefix) else { return false }
        removeFirst(prefix.count)
        return true
    }
}

To use this with strings, we first need to make a mutable Substring out of a String, and then we can call the scan method:

var remainder = "value: 123"[...]
if remainder.scan(prefix: "value: "),
   let firstDigit = remainder.scan({ "0123456789".contains($0) }) {
  print(firstDigit)
}

You can write a whole bunch of these scanning methods, there is no need for an extra Scanner type. You can even write “higher-order” scanners, like this:

extension Substring {
  mutating func many<A>(until end: Character, _ f: (inout Substring) throws -> A, separator: (inout Substring) throws -> Bool) throws -> [A] {
    // ... left as an exercise
  }
}

So far, we could have done similar things with a Scanner. However, one of the fun things about Swift is that the code we write is actually far more generic! Instead of defining it on Substring, we can define it on any Collection that supports removeFirst. Reviewing the method’s definition, we learn that it exists on any collection that has itself as a Subsequence. This means we only have to change the definition of the method, but not the method body:

extension Collection where SubSequence == Self {
    mutating func scan(_ condition: (Element) -> Bool) -> Element? {
        guard let f = first, condition(f) else { return nil }
        return removeFirst()
    }

    mutating func scan(count: Int) -> Self? {
        let result = prefix(count)
        guard result.count == count else { return nil }
        removeFirst(count)
        return result
    }
}

extension Collection where SubSequence == Self, Element: Equatable {
    mutating func scan<C>(prefix: C) -> Bool where C: Collection, C.Element == Element {
        guard starts(with: prefix) else { return false }
        removeFirst(prefix.count)
        return true
    }
}

Now we can use our scan method on many other types as well, most notably ArraySlice and Data. For example, we can use it to parse the beginning of a GIF header:

var t = try! Data(contentsOf: URL(string: "https://media.giphy.com/media/gw3IWyGkC0rsazTi/giphy.gif")!)[...]
guard t.scan(prefix: [71,73,70]), // GIF
    let version = t.scan(count: 3), // 87a or 89a
    let width = t.scan(count: 2),
    let height = t.scan(count: 2)
    else {
    fatalError()
}

print(version, width, height)

For further inspiration, see this gist by Michael Ilseman.

In Swift Talk Episode 78 (a public episode), we show how to work with Swift’s String and Substring types by writing a simple CSV parser.

To support our work you can subscribe, or give someone a gift.

Swift Talk for Teams

When we launched Swift Talk more than two and a half years ago, we decided to include support for team members right from the beginning. Our initial implementation was a little basic, but despite this companies big and small have signed up their teams. We always love hearing stories about how people are watching new episodes together at work, or discussing them over lunch.

Today we’re releasing several improvements for team subscriptions:

  1. We now support team manager accounts that don’t have access to the content, but can manage billing and team members.
  2. Inviting your colleagues has become much easier: just send them a signup link.
  3. European companies can now enter their VAT-ID for reverse charge invoices.

To sign up for a team account please check out the team subscription page. For enterprise teams (more than thirty people), please get in touch.

Swift Tip: Non-Empty Collections

Last week, we wrote about an extension on Optional that we used to deal with either a nil value or an empty string:

extension Optional where Wrapped: Collection {
    var nonEmpty: Wrapped? {
        return self?.isEmpty == true ? nil : self
    }
}

This extension allowed us to handle both the nil and the empty case in one go:

var vatNumber: String?
vatNumber.nonEmpty.map { "Your VAT Number is: \($0)" } ?? "No VAT Number."

Pyry Jahkola sent us a good suggestion on Twitter: instead of adding a nonEmpty property on Optional, why not extend Collection with an optional nonEmpty property? This extension is both simpler and more versatile:

extension Collection {
    var nonEmpty: Self? {
        return isEmpty ? nil : self
    }
}

Using this extension together with optional chaining we can write our previous example like this:

vatNumber?.nonEmpty.map { "Your VAT Number is: \($0)" } ?? "No VAT Number."

Now we can use the same extension on non-optional collection types to substitute a default value, using the nil coalescing operator instead of a ternary operator:

var vatNumber: String = "123"
vatNumber.nonEmpty.map { "Your VAT Number is: \($0)" } ?? "No VAT Number."

Admittedly, the differences between these variants are slight. However, we always like to discover new ways of solving a given problem. πŸ˜€

To support our work, you can subscribe, or give someone a gift.

Swift Tip: Non-Empty Optionals

In our Swift Talk backend, we need to display some information that comes from a third-party API. For example, we receive the following data:

struct BillingInfo {
    var name: String
    var vatNumber: String?
}

Here’s a simplified computed property that displays the data:

extension BillingInfo {
    var lines: [String] {
        return [
            "Name: \(name)",
            vatNumber.map { number in
                "Your VAT Number is: \(number)"
            } ?? "No VAT Number."
        ]
    }
}

Note that in the cases where we don’t have a VAT number, we display a different text. Unfortunately, sometimes the API we work with also returns an empty VAT number. As a first try, we dealt with it like this:

return [
    "Name: \(name)",
    vatNumber.map { number in
        number.isEmpty ? "No VAT Number." : "Your VAT Number is: \(number)"
    } ?? "No VAT Number."
]

Of course, the duplication isn’t very nice, so we tried another approach:

return [
    "Name: \(name)",
    vatNumber?.isEmpty == false ? "Your VAT Number is: \(vatNumber!)" : "No VAT Number."
]

Unfortunately, now we have a force-unwrap, which we usually try to avoid. Another variant would be to use an if let, and pull out the formatted VAT string into a variable:

let vatString: String
if let number = vatNumber, !number.isEmpty {
    vatString = "Your VAT Number is: \(number)"
} else {
    vatString = "No VAT Number."
}

return [
    "Name: \(name)",
    vatString
]

The if let variant is safe, and has no duplication, but it involves quite a bit more code. The nice thing about the other two approaches was that the transformations were very local: they happened at the point of usage.

Even though it’s a very minor piece of code, we didn’t really like any of the solutions above. Instead, we wrote a helper that solves this in a more general way:

extension Optional where Wrapped: Collection {
    var nonEmpty: Wrapped? {
        return self?.isEmpty == true ? nil : self
    }
}

The nonEmpty property returns nil when the collection within the optional is empty. This allows us to collapse both checks (for nil and for isEmpty) into one:

return [
    "Name: \(name)",
    vatNumber.nonEmpty.map { "Your VAT Number is: \($0)" } ?? "No VAT Number."
]

We’re almost certain that we aren’t the first to come up with this solution, but if you haven’t seen it yet, we figured it might still be useful.

Our Server-Side Swift Collection covers a range of related topics. We’ll be talking more about our backend rewrite soon.

To support our work, please subscribe, or give a gift.