Server-Side Swift Beta

Learn web development with Swift, using Vapor and Kitura.

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.

5/5 6 Ratings

Version

  • Swift 5, macOS 10.14, Xcode 10

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:

  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.

A New Center of Gravity

In more Copernican terms, Terminal and Swift Package Manager are the twin suns around which the Server-side worlds revolve. It’s possible to develop Client-side Swift projects without using either tool. But both are at the heart of every Server-side Swift project.

The corollary to this is that Xcode is not an essential part of Server-side Swift. This is a huge difference from Client-side Swift. Some Server-side Swift developers choose to omit Xcode and skip building an Xcode project altogether!

Now, if you love Xcode, don’t panic. :] Generating an Xcode project is the Server-side norm. Xcode is, and will remain, a great environment for developing Server-side Swift apps.

But Xcode can’t help you build or run Server-side apps once you switch from Apple devices to Linux, Docker and beyond. You can build, run and test your Server-side apps in Xcode as you’re developing on your mac. However, everything you build via Xcode will be a transient artifact to be discarded when you deploy these apps to Linux, Docker or the cloud.

In fact, you can take this one step further. The entire Xcode project in Server-side Swift is a transient byproduct of core processes controlled by Terminal and Swift Package Manager.

In practice, a common project workflow after creating new assets in Terminal is to run swift package generate-xcodeproj to regenerate a completely new Xcode project file. Imagine doing that to an established Client-side Swift project!

You’ve touched on binaries and frameworks at several points so far. Time to take a look at how frameworks translate to the expanded Server-side universe.

Building a Common Foundation

Swift is a fantastic constant across heterogenous environments. But the Swift code you’ll write isn’t the same everywhere. This is because Swift isn’t an island.

When you write Swift code, you dip into helping frameworks such as Foundation. This is important. It provides an array of critical primitives, types and functionality.

Additionally, its presence helps the Swift Standard Library stay small and highly focused. Simply put, without Foundation, Swift wouldn’t be the Swift we know and love.

It can be jolting to realize that Foundation doesn’t naturally exist everywhere. You shouldn’t take its presence for granted.

In fact, in the early days the lack of Foundation on Linux was a great challenge. A team of relatively unsung heroes worked to implement much of Foundation. This became The Foundation Project.

Thanks to the ongoing efforts of The Foundation Project, you can take for granted that Foundation can do what you need across all Server-side Swift platforms. That said, not all parts of Foundation have been implemented on other platforms. It’s always a good idea to check the project’s current Implementation Status page to see if the part of Foundation you’re planning to use is present beyond Apple’s platforms.

Remember, when you take Swift beyond its Apple-native box, you should make sure all the frameworks you’d like to use will work.

Choose Wisely, Grasshopper

OK, it’s time for a quick confession:

In the early days of Server-side Swift, a foolish grasshopper needed a specific piece of Server-side functionality for a Server-side project. He blithely grabbed a nice-looking macOS/iOS framework from Github without much thought.

“Sure, it doesn’t have a Package.swift file,” I thought. “But I’ll just throw in a simple Package.swift file for it and be done. Am I clever!”

The grasshopper discovered that while you sometimes can to adapt a package for Server-side, that’s not always the case. This is especially true if a package has nested dependencies on platform-specific tools.

The grasshopper learned to respect the differences between Apple-only and heterogenous environments. He also learned to take this into account when making architectural decisions. Learn from the foolish, fellow Server-side grasshoppers. :]

Part of the reason for such foolishness was the lack of available frameworks. These days, extensive sets of packages exist for both Kitura and Vapor .

Still, within any web framework there are cases where you need to reach beyond officially supported frameworks for a piece of functionality. So, how do you determine if a promising-looking framework will work Server-side? The choice comes down to a few simple questions:

  • Does the framework have a valid Package.swift file? If not, don’t pull a Brian®. Just say no and check out the another option. Otherwise, keep going.
  • Does it contain any mac-specific dependencies, either via Carthage or CocoaPods, or nested directly within it? If so, again, get outta Dodge now! Otherwise, proceed, grasshopper.
  • Are all primary and secondary dependencies also available on Linux? When it’s time to build your project beyond the Mac, will the Linux Terminal be able to retrieve them using apt-get?Linux users can check this by running apt-cache search <keyword> . They could also use via a package manager like Synaptic).

If a framework clears these hurdles, it’ll make the leap to compile beyond Apple platforms. Again, on the majority of server-side projects, this likely won’t be an issue. But a little understanding about what’s needed for a framework to go beyond Planet Apple goes a long way towards thinking in Server-side Swift.

A Copernican Revolution

The shift from Apple-controlled hardware and operating systems to a broader universe leads to several changes in architecting and building out Server-side Swift applications. Paradoxically, this might seem like either:

  • No Big Deal: It’s just a few things to do differently. Otherwise, don’t worry about it.

or

  • A Very Big Deal Indeed: If you really think about it, you’re in a whole different universe here.

So, which point of view is right? As you may have guessed, the answer is both. :]

The best way to understand the subtle differences between Swift’s Server- and Client-side worlds is with an analogy to The Copernican Revolution.

In Copernicus’s time, common sense knew that the earth was at the center of the cosmos. The sun, moon, planets and stars all rotated around the fixed foundation of the earth. This is like the Xcode- and Apple-controlled cosmos that Client-side apps have inhabited.

Of course, Copernicus realized that sun was at the center of the cosmos. Thinking differently opened new possibilities. It became easier to reason about and resolve long-standing problems.

Ultimately, new realms of discovery and exploration opened that simply weren’t possible before.

This is the perfect model for thinking in Server-side Swift. The expanded universe of Server-side Swift asks you to change the way you think about Swift and Xcode. In exchange, it opens new possibilities that can enhance your Swift skills and apps.

Like the original Copernican Revolution, nothing changes. And everything does.

Copernicus’ critics scoffed that his discovery changed nothing practical from day to day. People still went to bed, woke up and performed the same tasks they always had. Shifting the center of the cosmos from the earth to the sun changed nothing.

This is true in a post-Xcode-centric Swift world as well. Xcode still works when developing for Server-side. Other than a few small workflow changes, nothing looks much different on the surface.

But there are advantages to expanding your thinking to include changes brought by Server-side Swift. It gives you greater insight into why we do things differently when targeting the server.

You gain the simple but essential tools you need to leverage Terminal and Swift Package Manager. Similarly, it gives you the tools to choose and then implement frameworks on your Server-side projects.

Beyond these pragmatic benefits, it’s important to appreciate the work that went into Foundation, Vapor, Kitura and any other framework designed to build and run cross-platform. You rely on that work every time you write a line of Swift that uses these platforms. It’s amazing that in a few years, Swift has grown from a proprietary Apple-only language to a cross-platform tool with a broad and growing scope!

One More Thing®

As I wrote this post, WWDC 2019 delivered several new developments. These included Xcode 11’s new integrated support for Swift Package Manager.

Conceptually, this doesn’t have any impact on what you’ve learned here. But practically, Xcode 11’s SPM integration promises some nice streamlining of practical workflows.

Here’s a couple of quick teasers:

First, in Xcode 11 beta you can now choose to create a new Swift Package project:


Xcode 11 integration with Swift Package Manager from File Menu

When I create a quick Package, I’m presented with a sample Package.swift file. Even better, I’m presented with a new hierarchical visualization of the assets specified in the left-hand Project Navigator.


Xcode 11 Package.swift display

Now I’ll pop in my simple Kitura Package.swift from the Copernicus sample project. It’s a revelation to watch Xcode 11 beta sequentially analyze its contents and recursively nested dependencies.


Xcode 11 showing package dependencies resolving

It then retrieves and assembles these frameworks. It even creates a Package.resolved file on my behalf.


Xcode 11 Package Dependencies opened

I won’t pursue this further here. But, I’m exciting to see how the deep integration of Swift Package Manager into Xcode 11 will impact real-world Server-side Swift workflows.

Where to Go From Here?

Thinking in Server-side Swift isn’t a big leap. Consider the platform’s breadth and think about what you’re asking your Swift code and tools to do. As a result, you’ll write better, more efficient code that works with the Server-side world.

What do you think? Is the expanded world of Server-side Swift, like much of the recent WWDC, a bit of a mind-blower? Or does the continuity of Swift make it feel more like business as usual from your perspective? Let us know in the comments, and happy Server-side Swifting!

Average Rating

5/5

Add a rating for this content

6 ratings

Contributors

Comments