How to Think in Server-Side Swift

Server-side Swift opens exciting new worlds to Swift developers and best of breed frameworks like Kitura and Vapor expose the full power of Swift. In this post you’ll learn the key differences between Client- and Server-side Swift environments, and how to use them to take better advantages of Swift in its Server-side form. By Brian Schick.

Leave a rating/review
Download materials
Save for later
Share

Server-side Swift opens exciting new worlds to Swift developers. Best of breed frameworks like Kitura and Vapor expose the full power of Swift, while adding compelling features and API’s. Being able to develop end to end with full stack Swift can be a game-changing revelation.

However, there are subtle but significant differences between Swift for Client-side apps, like iOS or watchOS apps, and Swift for Server-side web environments.

Swift remains a constant North Star so many of these differences are easy to overlook. That’s a good thing. But they are worth understanding.

You can do a lot of productive Server-side Swift development without understanding these differences. But, learning to recognize them and think in Server-side Swift can greatly improve your productivity.

In this post, you’ll explore of some of the key differences between Client-side and Server-side Swift. You’ll also learn how to use these to take advantage of Swift in its Server-side form. Time to dive in!

Getting Started

To kick things off, start by downloading the materials for this tutorial (you can find a link at the top or bottom of this tutorial). The sample app is a simple Server-side Swift app called Copernicus. Note that everything discussed here applies universally to all Server-side Swift frameworks. I’ve used Kitura because it exposes raw Server-side Swift commands especially well.

Take the sample app out for a test flight. Start by opening the project in Xcode, choose Build>Run to quickly build and run it, and…

But wait- you don’t have an Xcode project yet, so even these most basic first steps don’t work here!

Like most Server-side projects and repos, the sample project files don’t come with an Xcode project. It also lacks the binary dependencies it’ll need to build and run. Before you can do anything in Xcode, you’ll need to drop down into Terminal to prepare things. You’ll do that now.

Open Terminal and run cd into the sample project directory. Then use ls -la to list the directory’s full contents.

-rw-r--r--@  1 brian  staff   417 Jun 10 15:49 Package.swift
-rw-r--r--@  1 brian  staff    45 Jun 10 15:45 README.md
drwxr-xr-x@  3 brian  staff    96 Jun 10 15:45 Sources
drwxr-xr-x@  4 brian  staff   128 Jun 10 15:45 Tests

As you can see, your Server-side Swift project isn’t very… there yet. But the seed of the project is present in it’s Package.swift file and our ./Sources and ./Tests directories. Runcat Package.swift to view the simple dependencies:

// swift-tools-version:5.0

import PackageDescription

let package = Package(
  name: "Copernicus",
  dependencies: [
    .package(url: "https://github.com/IBM-Swift/Kitura.git",
             .upToNextMajor(from: "2.5.0")),
  ],
  targets: [
    .target(
      name: "Copernicus",
      dependencies: ["Kitura"]),
    .testTarget(
      name: "CopernicusTests",
      dependencies: ["Copernicus", "Kitura"]),
  ]
)    

As you can see, the project has a single dependency on the Kitura framework. To retrieve and assemble this, run:

swift build
ls -la

You’ll see something similar to the following:

drwxr-x---@  6 brian  staff   192 Jun 10 15:57 .build
-rw-r--r--@  1 brian  staff  2525 Jun 10 15:57 Package.resolved
-rw-r--r--@  1 brian  staff   417 Jun 10 15:49 Package.swift
-rw-r--r--@  1 brian  staff    45 Jun 10 15:45 README.md
drwxr-xr-x@  3 brian  staff    96 Jun 10 15:45 Sources
drwxr-xr-x@  4 brian  staff   128 Jun 10 15:45 Tests

Here’s what just happened:

Next, it recursively checked each dependency for any potential sub-dependencies until all needed assets were cached locally in ../.build/repositories. It then stored the versions of each retrieved dependency in a newly-created Package.resolved file.

  1. swift build first retrieved the repository of Kitura itself. Next, it retrieved the repos of Kitura's dependencies. It based this on its Package.swift requirements.

    Next, it recursively checked each dependency for any potential sub-dependencies until all needed assets were cached locally in ../.build/repositories. It then stored the versions of each retrieved dependency in a newly-created Package.resolved file.

  2. Finally, it transformed these repos into a clean set of project assets for each dependency in ../.build/checkouts.

To get a sense of how these assets assemble, run open .build. Take a quick peek at the .build folder structure in Finder:


Finder .build folder structure

OK, back to Terminal. You’ve retrieved the assets needed, so now you need to build and configure an Xcode project to house them. Do this with the following commands:

swift package generate-xcodeproj
ls -la

When completed, you’ll see a new Copernicus.xcodeproj Xcode project file:

drwxr-x---@  6 brian  staff   192 Jun 10 15:57 .build
drwxr-xr-x@ 16 brian  staff   512 Jun 10 15:58 Copernicus.xcodeproj
-rw-r--r--@  1 brian  staff  2525 Jun 10 15:57 Package.resolved
-rw-r--r--@  1 brian  staff   417 Jun 10 15:49 Package.swift
-rw-r--r--@  1 brian  staff    45 Jun 10 15:45 README.md
drwxr-xr-x@  3 brian  staff    96 Jun 10 15:45 Sources
drwxr-xr-x@  4 brian  staff   128 Jun 10 15:45 Tests

With this initial work completed, you can now open the sample project. Do this now. A handy Terminal shortcut to do this is the command xed ..

In Xcode, build and run your project as you normally would. Swift’s Command Line Tools have assembled the completed project and assets. So, everything should build and run cleanly.

When Copernicus gets off the ground, you should see the following:

Running Copernicus in Xcode

Next, you’ll see the differences between the client and server side worlds. You’ll find key differences between the two Swift-y worlds. These in turn will aid you in your quest to think more clearly in Server-side Swift.

An Expanded Universe

The single most important differences between Client-side and Server-side Swift projects are the platforms they target.

Client-side Swift apps exclusively target Apple-native hardware platforms. The entire universe of Client-side apps lives within the conceptual box of Apple products. This means all Client-side project dependencies, whether added manually or via tools like Carthage or CocoaPods, are ultimately binaries targeted and compiled directly for the appropriate Apple hardware platform.

The universe of Server-side Swift is considerably larger. Everything must by definition work not only on macOS, but also on Ubuntu Linux, Docker and the cloud.

This expanded world requires significant architectural changes. It also requires some subtle changes in how you think about your projects. Specifically, you must use tools and build frameworks that work across all supported environments.

This means that when you’re developing Server-side, even project dependency must be able to be downloaded and built on the target platform’s native using native toolsets. More specifically, every dependency must be registered and built by the cross-platform Swift Package Manager, via the instructions in Package.swift file.

If you added Apple platform-compiled binaries to a Server-side project using Carthage or CocoaPods, they would work within the confines of Apple-branded hardware. But you couldn’t use those same dependencies when you deployed your project to Linux, Docker or the cloud. Your project would not build.

Brian Schick

Contributors

Brian Schick

Author

Cesare Rocchi

Tech Editor

Darren Ferguson

Final Pass Editor

Tim Condon

Team Lead

David Okun

Team Lead

Over 300 content creators. Join our team.