An Introduction to Vapor Development: Using Await and Protocols in Swift

A portrait painting style image of a pirate holding an iPhone.

by The Captain

on
April 14, 2023

An Introduction to Vapor Development: Using Await and Protocols in Swift

If you're looking to build powerful backend services in Swift, Vapor is a great framework to learn. While it can seem intimidating at first, its use of async-await and protocol-oriented programming make it a cutting-edge toolkit for backend development. In this tutorial, we'll take a closer look at how to use await and protocols in Vapor.

Using Await in Vapor

One of the most powerful features of Vapor (and Swift in general) is async-await. If you're not familiar with them, async-await is a way to write asynchronous code that looks and feels like synchronous code. Rather than writing callbacks or completion handlers, you simply use the "await" keyword to pause execution of a method until an asynchronous call has completed.

For example, if you're working with a database in Vapor, you might have a function like this:

func fetchUsers() -> EventLoopFuture<[User]> {
    return dbClient.users.query(on: dbClient.database).all()
}

This function returns an EventLoopFuture (a special type in Vapor that represents a future value), which will eventually contain an array of User objects. To use this method in async-await code, we simply add "await" before the method:

let users = await fetchUsers()

This code will pause execution until the database call has completed, and then assign the resulting array of users to the "users" constant.

Protocol-Oriented Programming in Vapor

Vapor also makes heavy use of protocol-oriented programming (POP), a design pattern in Swift that focuses on defining interfaces (protocols) rather than concrete types. By using protocols, we can write code that's much more flexible and testable.

For example, let's say we want to define a protocol that represents a user in our system:

protocol User {
    var username: String { get }
    var email: String { get }
}

This protocol defines two properties that all User objects must implement: a username and an email address. Once we've defined this protocol, we can write functions that work with any object that conforms to it. For example:

func sendWelcomeEmail(to user: User) -> EventLoopFuture<Void> {
    return emailClient.send(
        subject: "Welcome to our site!",
        to: user.email,
        body: "Hi \\(user.username), thanks for joining!"
    )
}

This function takes any object that conforms to the User protocol, so we can pass in any kind of User object (e.g. a database model, an API response, etc.). This makes our code more flexible and easier to test, since we can create mock User objects for testing purposes.

Conclusion

Vapor is a cutting-edge framework for backend development, and its use of async-await and protocols make it an especially powerful tool for Swift developers. By taking advantage of these features, we can write code that's more flexible, testable, and easier to reason about.