Hands-On Full-Stack Development with Swift

4 (3 reviews total)
By Ankur Patel
    What do you get with a Packt Subscription?

  • Instant access to this title and 7,500+ eBooks & Videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Getting Started with Server Swift

About this book

Making Swift an open-source language enabled it to share code between a native app and a server. Building a scalable and secure server backend opens up new possibilities, such as building an entire application written in one language—Swift.

This book gives you a detailed walk-through of tasks such as developing a native shopping list app with Swift and creating a full-stack backend using Vapor (which serves as an API server for the mobile app). You'll also discover how to build a web server to support dynamic web pages in browsers, thereby creating a rich application experience.

You’ll begin by planning and then building a native iOS app using Swift. Then, you'll get to grips with building web pages and creating web views of your native app using Vapor. To put things into perspective, you'll learn how to build an entire full-stack web application and an API server for your native mobile app, followed by learning how to deploy the app to the cloud, and add registration and authentication to it.

Once you get acquainted with creating applications, you'll build a tvOS version of the shopping list app and explore how easy is it to create an app for a different platform with maximum code shareability. Towards the end, you’ll also learn how to create an entire app for different platforms in Swift, thus enhancing your productivity.

Publication date:
March 2018
Publisher
Packt
Pages
356
ISBN
9781788625241

 

Chapter 1. Getting Started with Server Swift

Swift is yet another programming language, introduced in 2014 by Apple. According to them, Swift is a general-purpose programming language, built using a modern approach to safety, performance, and software design patterns. They created the language to help make development on their platform more fun and productive as their flagship language Objective-C is bit dated and has a very distinct syntax that makes it hard for anyone to quickly get started.

A few years ago, Swift was open sourced and the Swift community has pushed the language forward by trying to build server components using Swift. This has led to the creation of the term server-side Swift. So what is server-side Swift? What are the benefits of using it on the server? Can it be used to build different stacks of your application?

In this chapter, we will answer those questions while getting our feet wet in the world of server-side Swift. We'll cover the following:

  • Learning about modern app development
  • Seeing how Swift has evolved
  • Looking at the benefits of server-side Swift
  • Learning about the Swift package manager and its CLI
  • Building a simple library and an executable Swift package
  • Learning how web servers work and building a simple web server in pure Swift
  • Discovering server-side web frameworks for Swift and Swift package catalog
  • Learning about Vapor, one of the most used server-side web frameworks for Swift
  • Going over the idea for the apps we will be building in the book
 

Modern app development


Application development in today's world is not just about building for one platform. Modern applications have an ecosystem of apps that run on multiple devices and platforms. In order for these apps to run on the multiple platforms they also need a server-side component to be able to seamlessly save and retrieve data so that a user can switch between them and start using the app from where they left off on another platform. To make these modern applications possible, developers write code on the different technology stacks that have different programming languages and frameworks/libraries. This makes the job of the developer especially difficult due to the context switching between programming languages when building the application.

In the world of the web, developers have enjoyed working on the frontend web applications using JavaScript but after the introduction of Node.js, web developers who were mainly focused on the frontend could finally work on the backend in a language that feels familiar. Web developers have embraced the idea of working across different technology stacks as part of their app development because they can now write the frontend in JavaScript and use the same language to build server-side components.

Similarly, Swift, which is popular in the world of iOS, tvOS, and macOS for building rich client-facing applications, is now available on server-side thanks to Apple. Like JavaScript developers, Swift developers can now finally build server-side components in a language that is familiar to them while they continue to build frontend applications for different platforms, such as mobile, watch, TV, or desktop. Currently Swift is gaining popularity on the server-side, and it has never been a good time to be Swift developers. We can now engage ourselves in true full stack app development by working on different platforms using the same language and standard libraries that we are familiar with when building iOS, tvOS, watchOS, and macOS applications.

 

Swift's evolution


Swift started out as a general-purpose programming language, intended to replace Objective-C as the default language for building iOS, tvOS, watchOS, and macOS applications. Swift is a compiled language that compiles down to Low Level Virtual Machine (LLVM) bytecode and is Just-In-Time (JIT) compiled to native code of the architecture on its first run making Swift a very fast language. Swift also uses Automatic Reference Counting (ARC) to manage memory, making it simple to write applications especially for iOS where memory management is critical.

With all of these features, Swift definitely stands out from the rest of the languages popular during that time, which included Scala, Rust, Elixir, Kotlin, and C#. With growing popularity, all it needed was more platforms to run on and that is what Apple did when they open sourced it in late 2015.

Open source

In December 2015, Apple announced that it would open source Swift; this opened up the possibility of writing applications in Swift on other platforms, especially Linux. Open sourcing Swift meant that anyone could take the Swift code base and build a Swift compiler and toolset on their host operating system (OS) where LLVM is supported. This is exactly what Apple did soon after Swift was open sourced by creating Swift toolset that worked on Ubuntu, a popular distribution of Linux. Apple kept its promise of truly open sourcing Swift by also porting its libraries and frameworks, including Foundation, which is used extensively in iOS and macOS platforms, and made them work on Ubuntu. Without these frameworks, it would be hard to build cross-platform applications in Swift that work on both Apple's OS and Linux with same feature parity since Foundation is the standard library that contains access to essential data types, collections, and operating system services to define the base layer of functionality for any application.

Server-side Swift

Since Swift is a language that is elegant and expressive while being performant, it was about time that it would be ported to run on a server-side platform. Building command-line tools with Swift become popular on the macOS platform soon after Apple made it easy to use Swift for general purpose programming outside of iOS app development with the use of the Hash Bang, #!, syntax specified on top of the Swift filejust like in a scripting language such as Perl, Ruby, or Python. This made it very easy for anyone to write and run Swift code without having to compile it. The same technique works on Linux platform; so, let's see how it works:

  1. Creating a Swift file called hello.swift
  2. Adding the following code to the file:
#!/usr/bin/swift
print(“Hello World from Swift!”)
  1. Making the file an executable by changing the permission on the file using chmod:
$ chmod +x hello.swift
  1. Running the Swift code by typing the filename in the Terminal:
$ ./hello.swift

You should see Hello World from Swift! printed on the command line. This shows you how easy it is to create an executable in Swift without even having to compile it ahead of time; you can quickly test Swift code from the command line.

 

Benefits of server-side Swift


There are several benefits of using Swift on the server side. Some of them include:

  • Being able to work on a feature as a whole: Being able to work on an entire feature helps deliver the feature on time and as expected. Traditionally, teams are divided into frontend and backend teams but if you have the same language used for both front and backend then it will help developers contribute to the entire stack. Developers working on building the app can create the API endpoints needed to avoid the unnecessary back and forth between developers and prevent an app developer from being blocked by the backend engineer and move the feature development forward.
  • Working with familiar language and tools: Working with a familiar language reduces the biggest hurdle to working across different stacks. You can build both mobile and server-side components using the same language and tools. Swift developers can use their favorite IDE, Xcode, to build their backend server and do not have to learn new tools or install different IDEs.
  • Sharing code base: Code shareability is another big win for using Swift as you can share models, validations, and business logic easily across platforms. Not having to rewrite the same logic in different languages saves times and helps avoid expensive bugs caused by inconsistencies introduced by different developers who might have worked on rewriting the business logic on a different stack.
  • Leveraging great APIs: Apple did a great job building easy-to-use APIs on their platform and now being able to use those APIs server-side is a big benefit for developers as they do not have to learn new standard libraries or reinvent them on the Linux platform.
 

Getting started with Swift package manager


Mastering the command line is important, especially when trying to build and deploy Swift on a production Linux machine or in the Cloud. Since Xcode will not be available on those hosts, Apple has provided us with an easy-to-use command-line tool to help create, build, and distribute our Swift code. This tool is called the Swift package manager and it is useful for managing the distribution of Swift code while integrating with the Swift build system to automate the process of downloading, compiling, and linking dependencies. The following are some of the useful commands provided by the package manager to quickly get you started:

  • swift package init: This will create a Swift package or module that is an easy portable way to share code. It will create a package using the name of the folder you are currently in. Passing a --type executable option will make an executable package where the product of the build will be an executable program such as a web server or a command-line program. Think of this as gems for Ruby or node modules for Node.js.
  • swift build: This builds the Swift package you currently are in by compiling Swift code in your Sources folder. If your package is an executable, then it will generate a binary in the .build/debug folder. If you pass a release configuration using the --configuration release option, then it will build a highly optimized binary and place it in .build/release. The same output is generated for non-executable binary but generate Swift modules instead to be imported by whoever wants to use this module.
  • swift run: A quick way to run a Swift executable package from the command line. This command builds the Swift code if it is not built already and runs the binary. You can pass the -c release option to build and run the optimized version of the binary.
  • swift test: To run tests written in the Test folder of your package.
  • swift package generate-xcodeproj: This command generates an Xcode project file so that you can work on the package in Xcode instead of a plain text editor.

These are some of the more important commands that will come in handy when trying to build and test your web server in Swift and also when deploying and running your web application in production. There are a lot more commands and you can learn about them by running swift package in the Terminal:

Building a Swift package

Right now, we will go through an exercise to build a simple Swift package and learn about the important files and folders. We will also publish this package and consume it in another Swift package to show how we can publish packages and import them as dependencies. For our exercise, we will create a simple cat command-line tool which will concatenate and print the contents of the files specify relative to the current directory.

In order for us to do so we will first build a package called FileReader which will read and return the contents of the file. To build this Swift package, we need to do the following:

  1. Create a folder called FileReader (mkdir FileReader) and change directory (cd) into that folder
  2. Run Swift package init and it will generate files and folders for the package

Let's inspect the contents of the package. The following is the file and folder structure inside of FileReader:

~/W/FileReader $ tree .
.
├── Package.swift
├── README.md
├── Sources
│   └── FileReader
│       └── FileReader.swift
├── Tests
   ├── FileReaderTests
   │   └── FileReaderTests.swift
   └── LinuxMain.swift
4 directories, 5 files
  • Package.swift: This file is where you describe meta-information about the package, including dependencies of the package.
  • Sources: This is where you place your Swift code that will get built by the Swift package manager when you run the swift build command. It can contain multiple folders if you want to build multiple products or targets in your package.
  • Tests: This is where you place your test files and that get run when swift test is run from the command line.

Now that we know the basic file and folder structure, we can start writing our Swift code to read files from disk inside of the FileReader.swift file. By default, it will contain boilerplate code which we can remove and replace with this:

import Foundation
class FileReader {
 static func read(fileName: String) -> String? {
   let fileManager = FileManager.default
   let currentDirectoryURL = URL(fileURLWithPath: 
                          fileManager.currentDirectoryPath)
   let fileURL = currentDirectoryURL.appendingPathComponent(fileName)
   return try? String(contentsOf: fileURL, encoding: .utf8)
 }
}

In this file, we import Foundation, which is a standard library available in macOS and Linux and it provides us with the standard library to read from a file path using the FileManager. After that, we define the FileReader class and create one static function in it, called read, that takes a filename and this function will return the contents of the file if the file exists. The code inside the function does the following:

  1. Gets a singleton FileManager object:
   let fileManager = FileManager.default
  1. Creates a URL pointing to the current directory. The current directory is set to the directory from which the OS Process using this library was called from:
   let currentDirectoryURL = URL(fileURLWithPath: fileManager.currentDirectoryPath)
  1. Appends the filename passed to this function to the current directory:
   let fileURL = currentDirectoryURL.appendingPathComponent(fileName)
  1. Tries to read contents of the file if it exists and return it:
   return try? String(contentsOf: fileURL, encoding: .utf8)

Now that we have the code, we can build it using Swift build. To test that our code is working, we need to write a test for it and we can do so by taking the following steps:

  1. Editing the FileReaderTests.swift file and replacing the body of testExample function block with the following:
XCTAssertEqual(FileReader.read(fileName: "hello.txt"), "Hello World")
  1. Running the following command to create a hello.txt file in the root directory of the package with the contents Hello World:
printf "Hello World" > hello.txt
  1. Run the test for your package using the swift test command. You should see the test pass and print as such:
~/W/FileReader $ swift test
Compile Swift Module 'FileReaderTests' (1 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/FileReaderPackageTests.xctest/Contents/MacOS/FileReaderPackageTests
Test Suite 'All tests' started at 2017-09-29 12:14:57.278
Test Suite 'FileReaderPackageTests.xctest' started at 2017-09-29 12:14:57.278
Test Suite 'FileReaderTests' started at 2017-09-29 12:14:57.278
Test Case '-[FileReaderTests.FileReaderTests testExample]' started.
Test Case '-[FileReaderTests.FileReaderTests testExample]' passed (0.094 seconds).
Test Suite 'FileReaderTests' passed at 2017-09-29 12:14:57.372.
Executed 1 test, with 0 failures (0 unexpected) in 0.094 (0.094) seconds
Test Suite 'FileReaderPackageTests.xctest' passed at 2017-09-29 12:14:57.372.
Executed 1 test, with 0 failures (0 unexpected) in 0.094 (0.094) seconds
Test Suite 'All tests' passed at 2017-09-29 12:14:57.372.
Executed 1 test, with 0 failures (0 unexpected) in 0.094 (0.094) seconds

Now that we have a working Swift package, we can publish it.

Publishing a Swift package

Publishing a Swift package is as simple as committing code, tagging it, and pushing it up to a git repository. To publish the package, perform the following steps:

  1. Create a public git repository on github.com.
  2. Open the Terminal and change your directory to your package's path, cd /path/to/your/swift/package. Then initialize the git repository by running the git init command.
  3. Add a remote origin to the local git repo by running this command:
git remote add origin [email protected]:<repoaccount>/<reponame>.git
  1. Make sure to replace the repo account and repo name with the one you created in Step 1.

 

  1. Add all files to this repo using git add . and commit them using git commit -m "Initial Commit".
  2. Tag it with a version. Since it is our first package we will tag it 1.0.0,git tag 1.0.0.
  3. Publish it by pushing it up to the repo along with the tag:
 git push origin master --tags

It is that easy to make a Swift package and publish it. All you need is a git repository to push your code to and tag your code appropriately so that whoever uses your package as a dependency can point to a specific version.

Consuming a Swift package

Next, we will try to use this package to create an executable package called cat that concatenates and prints the contents of the files passed in as arguments to the command. This executable will work like the built-in-system cat command found in most Unix based operating systems. To do so, we need to perform the following steps:

  1. Open the Terminal and create a directory called cat (mkdir cat) and change the directory into it (cd cat).
  2. Initialize the package by running swift package init --type executable. This will generate a main.swift, which is the entry point for the executable and the code will start executing line by line starting from that file.
  3. Add the URL to your GitHub repo that contains the FileReader package and add the following line in your Package.swift under dependencies:
.package(url: "https://github.com/<repoaccount>/<reponame>", from: "1.0.0"),
  1. Add your FileReader package to the dependencies under the targets section in Package.swift:
import PackageDescription

let package = Package(
  name: "cat",
  dependencies: [
    .package(url: "https://github.com/ankurp/FileReader", from: "1.0.0"),
  ],
  targets: [
    .target(
      name: "cat",
      dependencies: ["FileReader"]),
  ]
)
  1. Add the following code to main.swift:
import FileReader

for argument in CommandLine.arguments {
  guard argument != "arg1" else { continue }

  if let fileContents = FileReader.read(fileName: argument) {
    print(fileContents)
  }
}

Let's try to understand what we have done in the preceding code:

  1. Import the FileReader package:
import FileReader
  1. Iterate over the command-line arguments:
for argument in CommandLine.arguments {
  1. We ignore the first argument using the guard clause in Swift because it is the command name cat:
guard argument != "arg1" else { continue }
  1. Print the contents of the file by printing it in the console:
if let fileContents = FileReader.read(fileName: argument) {
  print(fileContents)
}

Now that we have understood the code, let's build and run it to see whether it works. To build and run, just type the following command in the Terminal:

$ swift run cat Package.swift Sources/cat/main.swift

You should see the contents of both the files, Package.swift and Sources/cat/main.swift, printed in the console. Great job! We have a working command line tool written in Swift using one of our published Swift packages:

Installing the package's executable

How do we install the command line tool we just created? Don't worry, it's simple too. All you need to do is build it with the release configuration, so that it builds a highly optimized binary and also add flags to statically link the Swift standard library. This means that the executable can work even when Swift versions change on your operating system, or if you plan on distributing it on another platform, such as Linux. The following is the command to build the executable command with the release configuration:

$ swift build -c release -Xswiftc -static-stdlib

Once you have the binary built, you need to copy it to one of the directories where binaries are stored in your user path. One such place is /usr/local/bin. To copy it, just run the following command and call your binary file whatever you want. In my case, I chose to rename my command to swiftycat:

$ cp -f .build/release/cat /usr/local/bin/swiftycat

Now, try it out in the Terminal by running the following command:

$ swiftycat Package.swift Sources/cat/main.swift
 

How do the web servers work?


Before we start building web applications or web servers using Swift, it's a good idea to understand the basics of how they work. What is HTTP? What is a request and a response? What does it look like? These are some of the questions that will be answered in this section. To reinforce the concepts, we will try to go through the steps of what happens when a user types a URL in the browser.

User requesting a web page

When a user goes to the browser and types a URL, what exactly is happening?

  1. The browser tries to look up the IP address of the domain name in the URL by getting it from the DNS Server. Think of DNS as a directory mapping the domain name to the IP Address.

 

  1. Once the browser has the IP Address, it sends a HTTP request to that IP Address on port 80. If the URL is secure (HTTPS), then the request is sent to port 443. A simple HTTP request that is sent in plain text format looks like this: GET /hello.html HTTP/1.1.
  2. The request also contains headers that are shared to pass additional information about the request, such as authentication information, cookies, or the type of the browser making the request.
  3. The request is routed through all of the routers to the final destination, which is an application or web server serving web pages.
  4. The server looks at the request and figures out what type of request it is. In our simple example, we made a GET request. The following types of request are supported by the web/application server: GET, POST, PUT, PATCH, DELETE, and HEAD
  5. The server also looks at the request and figures out what path is being requested. In our example, we are requesting a web page at the /hello.html path.
  6. In the request, we specify the protocol we are using, which is the HTTP/1.1 protocol. Currently HTTP/2 is also available and certain browsers that support it will make a request with it.
  7. The request can also contain headers that contain extra information from the browser for the server to figure out how to respond. After the header section, the request is followed by two empty new lines that tell the server that it has received the entire request message and now it is time for the server to respond.
  8. The server then will either generate the hello.html page or serve it from disk. The web servers that serve HTML pages from disk are called static web servers while the web servers that dynamically generate content are called application servers as they have some business logic to generate the HTML content dynamically based on the type and the user requesting it.
  9. The server replies back to the request with a response. The response format is similar to the request where the first few lines are called the response headers and they are key value pairs of metadata, followed by two new lines, and then followed by the HTML response or plain text response from the server.
  10. The server closes the connection, which tells the browser that it has received all of the response and then it renders the HTML or plain text in the browser.

Mobile application requesting data

Similar to how the browser requests a web page from the web server, a mobile application can request data in JSON or XML format from the application server via HTTP or HTTPS. It uses the same protocol to send similar types of requests with headers followed by two new line characters and gets a reply back in the same format with headers followed by two new lines and data as plain text in JSON format.

Note

JSON is a lightweight data format used to exchange data on the web by mobile, desktop, and web applications. It stands for JavaScript Object Notation and is easy to read and write as it is in plain text. It has become the de facto format for exchanging data on the web and for mobile applications, and has quickly replaced XML as a medium for exchanging data.

HTTP request and response

To see all of this in action, let's make all of the requests that the browser does behind the scenes using telnet, which is a command-line tool used to connect to a host, and send messages to it on a port, which is exactly what the browser does. We will do the following in the Terminal:

  1. Connect to a host called httpbin.org, which is a free HTTP server sending back fake data for testing and learning purpose. To make a HTTP request via telnet, we need to connect on port 80 using the following command. If you do not have the telnet command installed locally on your macOS, install it using the brew package manager:
$ telnet httpbin.org 80
  1. This should print out the following, saying it is trying to connect to an IP address, which is the IP it got from the DNS server for this domain, httpbin.org:
Trying 54.243.145.223...
Connected to httpbin.org.
Escape character is '^]'.
  1. Enter the HTTP request followed by the header(s) to get our IP address as the response from the server. We need to type the following in the Terminal in our telnet session. Make sure to add two new lines at the end that will mark the end of our request to the server:
GET /ip HTTP/1.1
Host: httpbin.org
  1. After you enter the second new line, you will get back a response which is an HTTP response from the application server. The format is similar to the request where the headers are followed by the two new lines and then by the content, as follows:
HTTP/1.1 200 OK
Connection: keep-alive
Server: meinheld/0.6.1
Date: Tue, 03 Oct 2017 21:26:23 GMT
Content-Type: application/json
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
X-Powered-By: Flask
X-Processed-Time: 0.000671863555908
Content-Length: 30
Via: 1.1 vegur

{
  "origin": "73.80.254.5"
}

Servers can pass back a header value of Connection: keep-alive, which keeps the telnet client connected to the server even after it prints the response. Without this header line, the telnet client would disconnect after printing to the console. Browsers also obey this and reuse the live connection to send other HTTP requests via the same connection.

To verify this, we can send another GET request to /ip by passing the same request and header value followed by two new lines:

This exercise was designed to show you how HTTP works behind the scenes. You should now have a better understanding of how the browser or mobile applications make the request to our server and how we can respond back so that the browser or applications can accept and understand the web page or data passed back. It also touched on different parts of the HTTP request and response, such as the request signature, which contains the headers followed by two new lines and the response signature, which contains the headers followed by the two new lines, and finally followed by the text (for HTML/XML or JSON) or binary content (for images or videos).

I hope this gives you a better understanding of the request response cycle and how the server can deliver a better network performance by keeping the connection alive via the keep-alive header. The following diagram shows how a persistent connection with keep-alive can help make the web application or transfer data quickly, as it does not need to establish a connection every time:

 

Building a web server in Swift


Now that we have a little background on HTTP and formats for its request and response payloads, let's try to build an HTTP server from scratch using Swift and some C libraries that we can access via Swift. The point of this exercise is to learn how to build a very basic HTTP web server so we have a better understanding of how all of these web servers are built using sockets. Getting a full stack view is helpful in case you need to dive into low-level code to debug an issue or fix a bug in a package or library you might be using to build our web server. You might also be curious on how to code your own simple web servers, it's actually fairly easy. To create a web server, let's try the following steps:

  1. Import the C libraries in Swift:
import Darwin.C
  1. Create a socket using the socket system call. Sockets are a way for other hosts on the network to connect to this process:
let sock = socket(AF_INET, SOCK_STREAM, 0)
  1. Create a socket address structure and initialize it with host and port information. Then call the bind system call with the socket address structure and bind the server to the localhost on the port specified:
bind(sock, sockaddrPtr, socklen_t(socklen))
  1. Listen for incoming requests by calling listen and specifying the max number of requests to be added to the queue to be served by our process:
listen(sock, 5)
  1. Now, we can accept incoming connections by calling accept with the socket file descriptor for our socket that clients connect to. It will remove requests from the listen queue and return a new client socket connection:
let client = accept(sock, nil, nil)
  1. We can read from the new client socket connection and send data to it using HTTP Protocol:
  let html = "<!DOCTYPE html><html><body><h1>Hello from Swift Web Server.</h1></body></html>"
  let httpResponse: String = """
    HTTP/1.1 200 OK
    server: simple-swift-server
    content-length: \(html.count)

    \(html)
    """
  httpResponse.withCString { bytes in
    send(client, bytes, Int(strlen(bytes)), 0)
  }
  1. Close the connection using the close system call:
close(client)

That was a quick overview of how network-based programs work and how our web server will work as well. Now, let's look at the code as a whole:

import Darwin.C
let zero = Int8(0)
let transportLayerType = SOCK_STREAM // TCP
let internetLayerProtocol = AF_INET // IPv4
let sock = socket(internetLayerProtocol, Int32(transportLayerType), 0)
let portNumber = UInt16(4000)
let socklen = UInt8(socklen_t(MemoryLayout<sockaddr_in>.size))
var serveraddr = sockaddr_in()
serveraddr.sin_family = sa_family_t(AF_INET)
serveraddr.sin_port = in_port_t((portNumber << 8) + (portNumber >> 8))
serveraddr.sin_addr = in_addr(s_addr: in_addr_t(0))
serveraddr.sin_zero = (zero, zero, zero, zero, zero, zero, zero, zero)
withUnsafePointer(to: &serveraddr) { sockaddrInPtr in
  let sockaddrPtr = UnsafeRawPointer(sockaddrInPtr).assumingMemoryBound(to: sockaddr.self)
  bind(sock, sockaddrPtr, socklen_t(socklen))
}
listen(sock, 5)
print("Server listening on port \(portNumber)")
repeat {
  let client = accept(sock, nil, nil)
  let html = "<!DOCTYPE html><html><body style='text-align:center;'><h1>Hello from <a href='https://swift.org'>Swift</a> Web Server.</h1></body></html>"
  let httpResponse: String = """
    HTTP/1.1 200 OK
    server: simple-swift-server
    content-length: \(html.count)

    \(html)
    """
  httpResponse.withCString { bytes in
    send(client, bytes, Int(strlen(bytes)), 0)
    close(client)
  }
} while sock > -1

To run our server, let's take look at the following steps:

  1. Create a Swift file and call it simple-server.swift.
  2. Copy the preceding code into the file.
  3. Run the code using the Swift command and pass the file name as the first argument, as follows:
$ swift simple-server.swift
  1. The Swift compiler will try to compile the contents of simple-server.swift and run it in one command. You should see the following printed in the Terminal when the server has started:
Server listening on port 4000
  1. Open the browser and go to http://localhost:4000. You will see the response from our Swift simple HTTP server replying back with HTML content:

The example code works only on macOS but you can easily make it work in Linux by using import Glibc instead of import Darwin.C and changing some of the datatypes that are passed in creation of a socket. Swift supports some of the C directives, such as #if,#elsif,#else, and #endifto help include or skip code blocks before compiling on certain platforms, such as Linux or macOS, where a certain feature or API usage may be different. In our case, since we depend on C-based libraries, we'd need to import Glibc when OS is Linux or import Darwin.C and also set different types for two variables we use in our code, as follows:

#if os(Linux)

import Glibc
let zero = UInt8(0)
let transportLayerType = SOCK_STREAM.rawValue // TCP

#else

import Darwin.C
let zero = Int8(0)
let transportLayerType = SOCK_STREAM // TCP

#endif

Note

Directives are a way to let the compiler know how to process the source code before compiling. There are specific language constructs that let the compiler preprocess the source code and this comes in handy when we want to build a server-side Swift web applications where we need to ignore certain code blocks in Linux that are specific to macOS and vice versa so that the compiler does not fail to compile the code because certain standard libraries are missing or do not exist on certain platforms.

 

Server-side web frameworks


Building a web server like we just did is tedious and not scalable. There are a lot of things we need to implement, from routing to persisting data to rendering views. For such use cases, it's best to use a framework that can provide us with all the bells and whistles needed to quickly get started so we can focus on the application logic rather than spending time configuring and reinventing what others have already built.

Developers and even giant corporations, such as IBM, are betting that server-side Swift is the future by building frameworks in the form of Swift packages that make it very easy to build a web application. A few months after Swift was open sourced, one startup created a server-side framework called Perfect, which is very popular for building an entire server backend in Swift. IBM has spent a lot of effort creating Kitura, which is their take on a server-side swift framework that is lightweight and customizable, similar to express in Node.js or Sinatra in Ruby. Vapor is also a very popular framework with a lot of features.

Vapor

Vapor (https://vapor.codes/ ) is the Swiss Army knife of the web frameworks in Swift. It is a framework to get developers building modern web apps, sites, APIs, and even real-time web apps, using web sockets. It is currently the most used package in Swift (https://packagecatalog.com/browse?chart=mostessential&page=1), more used than Kitura and Perfect, which are the other two popular server-side frameworks for Swift. Vapor has a strong and vibrant developer community where developers from different companies, including Apple, are contributing to the framework to make Vapor fast, stable, and extensible so that it is easy to use and build large-scale web apps with. Swift is the next big platform for web and backend development, and Vapor is the framework that will help Swift get there. Vapor is the future of web development on the server platform. Here are some reasons to get excited about Vapor:

  • It has an amazing CLI tool that helps you create, build, run, and even deploy a Vapor app.
  • It is very fast compared to other frameworks, such as Kitura or Perfect, based on independent benchmark tests. It is especially fast when compared to other languages, such as Ruby, PHP, or Node.js.
  • It is secure from the beginning and has trusted encryption and TLS from OpenSSL and BCrypt hashing included by default to make security easy.
  • It is very extensible as it is very easy to add middleware and even create extensions for both the framework and CLI tools to customize the developer experience. Vapor is also modular, so you can use parts of Vapor, such as the Vapor Engine, to build your HTTP Server. Vapor is more than just an HTTP Server with Routing, and you can substitute a Kitura HTTP Server in place of Vapor's default HTTP Server while using other parts of the framework, such as its powerful Object-relational mapping engine, database migrations, and the view rendering engine.
  • It is heavily configurable via the config files that are in JSON format. The configurations allow for environment variable substitution, so you can easily swap out database URLs or other configurations for different environments using only environment variables.
  • It uses the model-view-controller architectural pattern, popularized by Rails, making it easy to create and debug apps.
  • It is resourceful by default, and has great APIs to build RESTful web applications. It is also resourceful in the sense that you can serve static assets such as CSS, JS, and even render views in different formats, such as HTML or JSON, depending on who is requesting a resource and with which format.
  • It is expressive where you write less code to do more, making Vapor apps more concise and powerful.
  • Vapor Apps are easy to deploy thanks to its cloud service, which is similar to Heroku, but you can also deploy the Vapor app to your cloud or data center if you like:

Considering that Vapor is more than just a simple HTTP server with routing like other server-side Swift frameworks and has all the bells and whistles of a full stack web application framework, it's an obvious choice that allows us to focus on writing the business logic for our application. We will be using Vapor to build our server-side component for the iOS apps that we'll be creating throughout this book.

 

Book roadmap


The goal of the book is to try to help guide you into the world of full stack Swift development by building a frontend in both a mobile and browser app. Throughout this book, we will try to work on building a Shopping List App. The final product will consist of the following components:

  • Native mobile app written in Swift for iOS
  • API backend written in Swift using Vapor
  • Frontend web app built using HTML rendered by Vapor, along with JavaScript and CSS for client-side interaction and styling

We will begin by building out the native application, and then proceed to building the backend for the app and integrate the app with the API. Then we will focus on rendering web views and adding authentication for both the API and the web app. Toward the end of the book, we will look at how to add real-time updates using web sockets in these apps and also try to build a simple chat application using Swift and Vapor.

 

Summary


I hope by now you have a better understanding of the state of Swift and where it is heading. Swift, which was once a language for mobile app development, can now be used on the server to build server-side web applications. By now, you should have the following:

  • An understanding of how Swift has evolved and is being used for web development
  • An idea of how you can use Swift for full stack app development and reuse the tooling and knowledge of the language to build backends in Swift
  • Knowledge about the Swift package manager and how to use its CLI
  • Built and published a Swift package and know how to share code and import dependencies into your Swift package
  • An understanding of how web servers are built and work under the hood using sockets
  • Some knowledge of the server-side Swift frameworks that can help you build a full stack web application
  • Familiarity with Vapor, which we will be using throughout the book
  • A mental model of what we are trying to build throughout the book to learn how to build full stack native apps using Swift

In the next chapter, we will dive into building an actual native iOS app in Swift and flesh out the features of the app.

About the Author

  • Ankur Patel

    Ankur Patel is a web and mobile application developer specializing in iOS, with a passion for making innovative consumer applications. Ankur is a generalist who has worked with a variety of programming languages, such as Objective-C, Ruby, JavaScript, Swift, Java, and C. He has held multiple roles in firms both big and small, including IBM, Oracle, Goldman Sachs, Bloomberg, Shutterstock, and MyTime. He is also the author of the book Learning Swift - Building an iOS Game, which teaches readers how to build an iOS game and publish it to the App Store.

    Browse publications by this author

Latest Reviews

(3 reviews total)
購買流程還算流暢,沒遇到什麼困難或問題,如果可以的話評價欄不要限制最低字數
The contents are good but it's difficult to read. Please add more pictures to understand easily.
Leggendo il libro ho approfondito alcune lacune che avevo.
Hands-On Full-Stack Development with Swift
Unlock this book and the full library FREE for 7 days
Start now