Sie sind auf Seite 1von 87

Co

m
pl
im
en
ts
Extending

of
Swift Value(s)
to the Server

David Ungar &


Robert Dickerson
Extending Swift Value(s)
to the Server

David Ungar and Robert Dickerson

Beijing Boston Farnham Sebastopol Tokyo


Extending Swift Value(s) to the Server
by David Ungar and Robert Dickerson
Copyright © 2017 IBM Corporation. All rights reserved.
Printed in the United States of America.
Published by O’Reilly Media, Inc., 1005 Gravenstein Highway North, Sebastopol, CA
95472.
O’Reilly books may be purchased for educational, business, or sales promotional use.
Online editions are also available for most titles (http://safaribooksonline.com). For
more information, contact our corporate/institutional sales department:
800-998-9938 or corporate@oreilly.com.

Editors: Nan Barber and Susan Conant Interior Designer: David Futato
Production Editor: Shiny Kalapurakkel Cover Designer: Karen Montgomery
Copyeditor: Christina Edwards Illustrator: Rebecca Panzer
Proofreader: Eliahu Sussman

November 2016: First Edition

Revision History for the First Edition


2016-11-04: First Release

The O’Reilly logo is a registered trademark of O’Reilly Media, Inc. Extending Swift
Value(s) to the Server, the cover image, and related trade dress are trademarks of
O’Reilly Media, Inc.
While the publisher and the authors have used good faith efforts to ensure that the
information and instructions contained in this work are accurate, the publisher and
the authors disclaim all responsibility for errors or omissions, including without
limitation responsibility for damages resulting from the use of or reliance on this
work. Use of the information and instructions contained in this work is at your own
risk. If any code samples or other technology this work contains or describes is sub‐
ject to open source licenses or the intellectual property rights of others, it is your
responsibility to ensure that your use thereof complies with such licenses and/or
rights.

978-1-491-97646-3
[LSI]
Table of Contents

Preface: Swift for the Rest of Your Application. . . . . . . . . . . . . . . . . . . . . vii

1. A Swift Introduction. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Types and Type Inference 2
Syntax 2
Simple Enumerations 3
Tuples 3
Custom Operators 5
Closures 6
Object Orientation 7
Protocols Define Interfaces 7
Extending Classes, Structures, and Enumerations 9

2. Optional Types, Structures, & Enumerations. . . . . . . . . . . . . . . . . . . . 13


Optional Types Exterminate Nil-Value Bugs 13
Structures Isolate Mutation 15
Enumerations with Associated Values 21
Choosing an Aggregate 24

3. Swift Promises to Tame Asynchrony. . . . . . . . . . . . . . . . . . . . . . . . . . . 29


Step 1: Chaining Synchronous Errors 30
Step 2: Linearizing Nested Callbacks 35
Step 3: Coping with Asynchronous Errors 40

4. Swift Package Manager. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43


Semantic Versioning 44
Creating an Application 44

v
Importing a Library in Your Project 47
Developing in Xcode 50
Creating Your Own Library 51
Producing More Complex Projects 54
Using C Dynamic Libraries 55

5. Writing a To-Do List with Kitura. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57


Servers and Routers 57
Creating a Web Service 59
Deploying Your Application to Bluemix 62
More Elaborate To-Do Items 65
Adding Authentication 68
Setting up the Database 69
Connecting to the Database 71

Conclusions. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

vi | Table of Contents
Preface: Swift for the Rest of
Your Application

Q: Why did the human put on his boxing gloves?


A: He had to punch some cards.
Today’s applications do not run on a single platform. Rather, some
parts run on resource-limited devices, and other parts run on a vast
and mysterious cloud of servers. This separation has led to a schism
in how we build these applications because different platforms have
different requirements: the mobile portions must conserve battery
power, while the server portions must handle a large number of
requests simultaneously. Consequently, programmers use different
languages for different parts of applications—for instance, JavaScript
for the browser, and Java for the server.
However, constructing an application out of multiple languages is
fraught with drawbacks: different teams in the same organization
speak different languages—literally—and must master different
developer ecosystems. Precious time must be spent translating con‐
cepts across language barriers and a few developers must know all of
the languages in order to be effective. Test cases and data models
must be replicated in different languages, introducing bugs and
incurring future maintenance efforts. Because third-party libraries
cannot be shared across groups, each team must learn different APIs
to obtain merely the same functionality.
Swift was introduced by Apple in 2014 and replaced Objective-C as
the recommended language for all new applications running on
Apple devices. Later, when Swift became open source in 2015, it
spread to new platforms. Currently, Swift is available on x86, ARM

vii
(including Raspberry Pi), and zOS architectures, as well as
Linux, macOS, tvOS, watchOS, and iOS operating systems. So, it is
now possible to write a whole end-to-end mobile application—
front-end, middle, back, and even toaster—all in Swift. That’s why
we wrote this book; we wanted to help you, the developer, who is
most likely writing in Java or JavaScript, to consider a switch to
Swift.
Why adopt Swift?

• The Swift language may well be better than what you are cur‐
rently using.
• You can develop and debug in a consistent environment. Inte‐
grated development environments (IDEs) offer a tremendous
amount of functionality such as text editing, static analysis, code
completion, debugging, profiling, and even source-control inte‐
gration. Switching back and forth between say, Eclipse and
Xcode is a bit like switching between French horn and electric
guitar: neither easy nor productive.
• You can reuse code. When each bit of functionality is expressed
exactly once, there is less work, more understanding, and fewer
bugs.
• You can leverage Swift’s features—such as optional types, value
types, and functional programming facilities—to detect many
bugs at compile time that would otherwise be hard to find.
• Since Swift uses the LLVM compiler toolchain for producing
native-code binaries, your applications have the potential for
competitive performance in terms of speed, startup time, and
memory usage. However, examination of performance is out‐
side the scope of this book.
• You will find an active and approachable community of Swift
developers who are creating web posts, books, and videos. In
2016, Swift was cited as the second “Most Loved” language in a
StackOverflow survey, and the third most upward trending
technology.
This book will introduce you to the Swift language, illustrate some
of its most interesting features, and show you how to create and
deploy a simple web service. Let’s get started!

viii | Preface: Swift for the Rest of Your Application


Coding Style & Implementations
In the examples, the space constraints of this medium
have led us to indent, break lines, and place brackets
differently than we would in actual code. In addition,
space has precluded the inclusion of full implementa‐
tions and blocks of code in this edition contain incon‐
sistencies in color and font. If the inconsistencies
confuse you, please consult the repositories in
Table P-1.

Table P-1. Where to find code examples


Repository name Referenced in
Book snippets Code snippets from the book
MiniPromiseKit Created in Chapter 3; used in Chapter 5
Pipes Used in Chapter 4
Kitura To-Do List Created in Chapter 5

Acknowledgments
This book would not have been possible without the support,
encouragement, and guidance of the IBM Cloud and Swift@IBM
leadership team, including Pat Bohrer, Eli Cleary, Jason Gart‐
ner, Sandeep Gopisetty, Heiko Ludwig, Giovanni Pacifici, John
Ponzo, and Karl Weinmeister. In addition, we want to extend our
thanks to the many IBM Swift engineers and Swift community
members working to bring Swift to the server—including Chris Bai‐
ley, Hubertus Franke, David Grove, David Jones, and Shmuel Kall‐
ner—for sharing their collective technical insights and creating the
tools and libraries described herein. The Swift community’s embrace
of Swift on the server reassured us that our contribution would be
valued. The growing number of their instructive blog posts, videos,
conference talks, and books have been of great help. We would like
to thank our technical reviewers: Chris Devers, Shun Jiang, and
Andrew Black. Nan Barber and the O’Reilly team had the daunting
task of editing our lengthy technical drafts and producing this book.
We owe a huge debt of gratitude to the Apple Core Swift Team for
their courage, intelligence, talent, wisdom, and generosity for bring‐
ing a new language and ecosystem into existence and moving it to
open source. Language design involves many difficult and complex
tradeoffs, and bringing a new language to the world requires a tre‐

Preface: Swift for the Rest of Your Application | ix


mendous amount of work. The rapid acceptance of Swift by devel‐
opers is powerful testimony to the quality of the language.
Words fall short in plumbing the depths of our gratitude for the
support and love of our sweethearts, Barbara Hurd and Valerie
Magolan.

x | Preface: Swift for the Rest of Your Application


CHAPTER 1
A Swift Introduction

Swift supports several different programming paradigms. This chap‐


ter provides a brief overview of the parts of the Swift language that
will be familiar to a Java or JavaScript programmer. Swift is not a
small language, and this chapter omits many of its conveniences,
including argument labels, shorthand syntax for closures, string
interpolation, array and dictionary literals, ranges, and scoping
attributes. Swift’s breadth lets you try Swift without changing your
programming style while you master its basics. Later, when ready,
you can exploit the additional paradigms it offers.
A beginning Swift developer may initially be overwhelmed by the
cornucopia of features in the Swift language, since it gives you many
ways to solve the same problem. But taking the time to choose the
right approach can often catch bugs, shorten, and clarify your code.
For instance, value types help prevent unintended mutation of val‐
ues. Paradigms borrowed from functional programming such
as generics, closures, and protocols provide ways to factor out not
only common code, but also variations on common themes. As a
result, the underlying themes can be written once, used in varying
contexts, and still be statically checked. Your programs will be much
easier to maintain and debug, especially as they grow larger.
As you read this chapter, you may want to refer to the documenta‐
tion, The Swift Programming Language (Swift 3 Edition).

1
Types and Type Inference
Swift combines strong and static typing with powerful type inference
to keep code relatively concise. Swift’s type system and compile-time
guarantees help improve the reliability of nontrivial code.

Deciphering Type Errors in Long Statements


If your program won’t compile, you can often clarify a
type error by breaking up an assignment statement
into smaller ones with explicit type declarations for
each intermediate result.

Syntax
Swift’s syntax borrows enough from other languages to be easily
readable. Here’s a trivial example:
let aHost = "someMachine.com"
aHost = "anotherMachine.com" // ILLEGAL: can't change a constant

aHost is inferred by Swift to be of type String. It is a constant, and


Swift will not compile any code that changes a constant after it has
been initialized. (Throughout this book, ILLEGAL means “will not
compile.”) This constant is initialized at its declaration, but Swift
requires only that a constant be initialized before being used.
var aPath = "something"
aPath = "myDatabase" // OK

aPath is also a String, but is a mutable variable. Swift functions use


keywords to prevent mixing up arguments at a call site. For example,
here is a function:
func combine(host: String, withPath path: String) -> String {
return host + "/" + path
}
and here is a call to it:
// returns "someMachine.com/myDatabase"
combine(host: aHost, withPath: aPath)
Swift’s syntax combines ease of learning, convenience of use, and
prevention of mistakes.

2 | Chapter 1: A Swift Introduction


Simple Enumerations
In Swift, as in other languages, an enumeration represents some
fixed, closed set of alternatives that might be assigned to some vari‐
able. Unlike enumerations in other languages, Swift’s come in three
flavors, each suited for a particular use. The flavor of an enumera‐
tion depends on how much information its values are specified to
include.
An enumeration may be specified by 1) only a set of cases, 2) a set
of cases, each with a fixed value, or 3) a set of cases, each with a set
of assignable values. (The last flavor is covered in Chapter 2.)
The simplest flavor merely associates a unique identifier with each
case. For example:
enum Validity { case valid, invalid }
The second flavor of enumeration provides for each case to be asso‐
ciated with a value that is always the same for that case. Such a value
must be expressed as a literal value, such as 17 or "abc". For exam‐
ple:
enum StatusCode: Int {
case ok = 200
case created = 201
… // more cases go here
case badRequest = 400
case unauthorized = 401
}

The value of this enumeration can be accessed via the rawValue


attribute:
func printRealValue(of e: StatusCode) {
print ("real value is", e.rawValue)
}

Tuples
As in some other languages, a Swift tuple simply groups multiple
values together. For example, here’s a function that returns both a
name and serial number:
func lookup(user: String) -> (String, Int) {
// compute n and sn
return (n, sn)
}

Simple Enumerations | 3
Tuple members can be accessed by index:
let userInfo = lookup(user: "Washington")
print( "name:", userInfo.0, "serialNumber:", userInfo.1 )
or can be unpacked simultaneously:
let (name, serialNumber) = lookup(user: "Adams")
print( "name:", name, "serialNumber:", serialNumber )
Members can be named in the tuple type declaration:
func lookup(user: String)
-> (name: String, serialNumber: Int)
{
// compute n and sn
return (n, sn)
}
and then accessed by name:
let userInfo = lookup(user: "Washington")
print("name:", userInfo.name,
"serialNumber:", userInfo.serialNumber)

When an identifier is declared with let, every element of the tuple


behaves as if it were declared with a let:
let second = lookup(user: "Adams")
second.name = "Gomez Adams" // ILLEGAL: u is a let
var anotherSecond = lookup(user: "Adams")
anotherSecond.name = "Gomez Adams" // Legal: x is a var
print(anotherSecond.name) // prints Gomez Adams
When you assign a tuple to a new variable, it gets a fresh copy:
var first = lookup(user: "Washington")
var anotherFirst = first
first.name // returns "George Washington"
anotherFirst.name // returns "George Washington" as expected
first.name = "George Jefferson"
first.name // was changed, so returns "George Jefferson"
anotherFirst.name // returns "George Washington" because
// anotherFirst is an unchanged copy

first and anotherFirst are decoupled; changes to one do not


affect the other. This isolation enables you to reason about your pro‐
gram one small chunk at a time. Swift has other constructs with this
tidy property; they are all lumped into the category of value types.
(See Chapter 2.) The opposite of a value type is a reference type. The
only reference types are instances-of-classes and closures. Conse‐

4 | Chapter 1: A Swift Introduction


quently, these are the only types that allow shared access to mutable
state.
Tuples combine nicely with other language features: the standard
built-in method for iterating through a dictionary uses key-value
tuples. Also, Swift’s switch statements become very concise and
descriptive by switching on a tuple:
enum PegShape { case roundPeg, squarePeg }
enum HoleShape { case roundHole, squareHole, triangularHole }

func howDoes( _ peg: PegShape, fitInto hole: HoleShape )


-> String
{
switch (peg, hole) { // switches on a tuple
case (.roundPeg, .roundHole):
return "fits any orientation"
case (.squarePeg, .squareHole):
return "fits four ways"
default:
return "does not fit"
}
}
Tuples are a convenient way to aggregate a few constant fields that
have no meaningful type apart from the types of the fields.

Custom Operators
As in some other statically-typed languages, Swift allows you to
define your own operators based on static types.
One custom operator we’ll be using in our examples later is apply,
which we’ll denote as |>. Like a Unix pipe, it feeds a result on the left
into a function on the right:
9.0 |> sqrt // returns 3
This lets us read code from left to right, in the order of execution.
For example:
send(compress(getImage()))
can be rewritten as:
getImage() |> compress |> send
To define this custom operator, you first tell Swift about the syntax:

Custom Operators | 5
precedencegroup LeftFunctionalApply {
associativity: left
higherThan: AssignmentPrecedence
lowerThan: TernaryPrecedence
}
infix operator |> : LeftFunctionalApply
then provide a (generic) implementation:
func |> <In, Out> ( lhs: In, rhs: (In) throws -> Out )
rethrows -> Out {
return try rhs(lhs)
}
Judiciously used, custom operators can help make code clear and
concise.

Closures
Swift includes closures: anonymous functions defined inline, which
can read and write to their enclosing lexical scope.1 A closure is a
first-class entity, a reference type which can be assigned to constants
and variables, passed as arguments, and returned as results. Func‐
tions and methods are just special cases of closures with a slightly
different syntax for their definition. Closures support functional pro‐
gramming, which can be particularly helpful in dealing with asyn‐
chrony (Chapter 3).

Deciphering Types Errors in Closures


Most of the time, the Swift compiler can infer the types
of closure arguments and results. When it cannot, or
when the code includes a type error, the error message
from the compiler can be obscure. You can often clar‐
ify type errors by adding types to a closure that are not
strictly necessary. In the example below, if the compiler
were to complain about a type, you could add the (pre‐
viously implicit) types:

1 Unlike Smalltalk, Swift closures cannot return from the home method’s scope (a.k.a.,
nonlocal return), so they cannot be used to extend the built-in control structures.

6 | Chapter 1: A Swift Introduction


func makeChannel()
-> ( send: (String) -> Void, receive: () -> String )
{
var message: String = ""
return (
send: { (s: String) -> Void in message = s },
receive: { (_: Void ) -> String in return message }
)
}

Object Orientation
Swift includes full support for class-based object-oriented program‐
ming, including classes, instances, constructors (called “initializ‐
ers”), instance variables, static variables, computed virtual getters
and setters, inheritance, and final attributes. When one method
overrides another, it must be annotated in the code. This require‐
ment prevents some errors. Experienced Swift programmers tend to
reserve objects for things requiring shared access to a mutable state.
(See Chapter 2.)

Protocols Define Interfaces


As with other typed languages, Swift includes a notion that the
expected behavior of an entity is separate from any particular
embodiment of that entity. The former is called a protocol and the
latter a concrete type—think interface versus class if you’re a Java
programmer. A Swift protocol lets you define what is expected of a
type without specifying any implementation. For example, the oper‐
ations expected of any HTTP_Request might be that it can supply a
URL and a requestString:
protocol HTTP_Request_Protocol {
var url: URL {get}
var requestString: String {get}
}

In other languages, Abstract_HTTP_Request might need an abstract


requestString. But in Swift, the protocol serves that purpose:
class Abstract_HTTP_Request {
let url: URL // A constant instance variable
init(url: URL) { self.url = url }
}
class Get_HTTP_Request:
Abstract_HTTP_Request, HTTP_Request_Protocol

Object Orientation | 7
{
var requestString: String { return "GET" }
}
class Post_HTTP_Request:
Abstract_HTTP_Request, HTTP_Request_Protocol
{
var requestString: String { return "POST" }
var data: String
init( url: URL, data: String ) {
self.data = data
super.init(url: url)
}
}
When declaring a variable to hold a request, instead of using the
type Abstract_HTTP_Request, use the type HTTP_Request_Proto
col:
let aRequest: HTTP_Request_Protocol
= Get_HTTP_Request(url: … /* some URL */)
aRequest.requestString // returns "GET"
In the following, you’ll see an example of a generic protocol, which
could not be used as a type. As in other languages, protocols help to
prevent errors as well as remove dependencies on the representa‐
tion.

Generic Protocols
Generic entities allow the same basic code to apply to different
types. In addition to concrete types, Swift also allows protocols to be
generalized to different types, although the mechanism differs.
For example, suppose you have two responses, a TemperatureRes
ponse and a FavoriteFoodResponse:
struct TemperatureResponse {
let city: String
let answer: Int
}
struct FavoriteFoodResponse {
let city: String
let answer: String
}
Even though each answer is a different type, they can share the same
description by adopting a common protocol:

8 | Chapter 1: A Swift Introduction


protocol ResponseProtocol {
associatedtype Answer
var city: String {get}
var answer: Answer {get}
}
struct TemperatureResponse: ResponseProtocol {
let city: String
let answer: Int
}
struct FavoriteFoodResponse: ResponseProtocol {
let city: String
let answer: String
}

The associatedtype in the protocol works a bit like a type parame‐


ter, except that instead of being passed in, it is inferred from context.
Any protocol with an associated type is considered to be generic.
Self, which denotes the concrete type conforming to the protocol,
is also considered to be an associated type.
Unfortunately, generic protocols such as this one are more difficult
to use than nongeneric ones. Specifically, they cannot be used in
place of types, but only as generic constraints. So, you cannot write a
declaration to hold a value that conforms to ResponseProtocol:
var someResponse: ResponseProtocol // ILLEGAL
But you can write a function that will work on any type that con‐
forms to ResponseProtocol:
func handleResponse <SomeResponseType: ResponseProtocol>
( response: SomeResponseType ) { … }
Because a generic protocol cannot be used as a type, it is often help‐
ful to split up a generic protocol into generic and nongeneric proto‐
cols. A full discussion of these generic protocols is beyond the scope
of this book. Generic protocols support generic functions, struc‐
tures, and objects by providing a way to express requirements and
implementations that apply to entities that themselves generalize
over the types of their arguments, results, or constituents.

Extending Classes, Structures, and


Enumerations
Like many other languages, Swift allows you to add new behavior to
a preexisting construct. In Swift, this capability is called an exten
sion, and can be used with classes, structures, enumerations, and

Extending Classes, Structures, and Enumerations | 9


protocols. The last case is a bit different because protocol extensions
supply default behavior, just as method bodies in Java interfaces do.
As you might expect, Swift’s extension facility is especially useful
for large programs because the entity you want to extend is likely to
have been defined in a separate library. You might not even have
source code for it!
Less obviously, Swift’s extensions help in two other situations:

1. An extension can add a bit of specialized functionality that is


only visible within a single file. Suppose that in some computa‐
tion you find yourself squaring a number often, such as (a/b) *
(a/b) + (c/d) * (c/d). You could add the following to the file
containing that code:
private extension Int {
var squared: Int { return self * self }
}
Now you can rewrite the above as (a/b).squared + (c/
d).squared. The extension adds a new member to Int without
cluttering up its namespace everywhere.
2. You might have a set of classes where each performs the same
set of functions. Extensions let you group the code by function
as opposed to class. An extension need not be in the same file or
even the same module as the original definition. For example,
you might have classes for city and state that each perform a
country lookup:
class City {
let name: String
init(name: String) { self.name = name }
func lookupCountry() -> String { … }
}
class State {
let name: String
init(name: String) { self.name = name }
func lookupCountry() -> String { … }
}
Extensions let you group the lookup functions together:
extension City { func lookupCountry() -> String { … } }
extension State { func lookupCountry() -> String { … } }

10 | Chapter 1: A Swift Introduction


This lets you put functionality where it makes the most sense,
whether in a type defined by a library, limited to the scope of a sin‐
gle file, or together with similar functionality for different types.

Extending Classes, Structures, and Enumerations | 11


CHAPTER 2
Optional Types, Structures, &
Enumerations

Programming is hard and debugging is harder, but maintaining and


debugging large programs that run asynchronously and concur‐
rently is hardest. It makes sense to place the burden of checking cer‐
tain runtime properties of your program on the compiler rather
than the developer. Swift’s optional types and structures let you tell
the compiler more about your program now so that you spend less
time debugging later. These features rely on a combination of
compile-time checks and runtime techniques that, in most cases, do
not reduce performance.

Optional Types Exterminate Nil-Value Bugs


Programs represent uninitialized or absent values with nil (a.k.a.,
null). If your code fails to handle a nil value anywhere one can occur,
bad things can happen. So Swift incorporated the might-be-nil ver‐
sus can-never-be-nil distinction into its static type system. These are
called “just” and “maybe” in Haskell. For example, suppose you are
trying to extract the “Content-Type” entry from the header fields of
an HTTP request. You have the header fields represented as a dictio‐
nary with String keys and values:
let headerFields: [String: String] = …
Swift uses subscript notation to look up the value of a given key in a
dictionary:

13
let contentType = headerFields["Content-Type"]

and Swift will infer a type for contentType. But that type is
not “String”! It is “String?” with a question mark, because String
represents a value that can never be nil, whereas String? represents
a value that be either a String or nil. The latter is called an optional
type. The dictionary lookup returns an optional type because the
dictionary might not contain a key for “Content-Type.” The type
String? is not the same type as String and Swift won’t let you use
the value of an optional type without an explicit check:
if contentType.hasPrefix("text") // ILLEGAL
There are many convenient ways to perform this check. For exam‐
ple, the if-let form checks if the value is nonnil. If so, it assigns
the value to a new variable that is local to the then-part. If the value
is nil, it executes the else-part, if any.
let contentType: String
if let ct = headerFields["Content-Type"] {
contentType = ct
}
else {
contentType = "no contentType"
}

The ?? nil-coalescing operator allows you to substitute a value for


nil. It only evaluates the expression on the right if the one on the
left is nil. For example, you might use ?? to supply a default value
for a missing dictionary entry:
let contentType = headerFields["Content-Type"] ?? "none"
Swift’s treatment of nil values will significantly improve the quality
of your programs over many other languages. Swift’s optionals add
security without inconvenience.

Surprisingly Helpful, a Personal Note from David


For decades, I had programmed in languages that treat null as a
universal value of any type. When I adopted Swift, I was quite sur‐
prised by how much its treatment of nil improved the quality of my
code. The unexpected benefit arose where a variable was not
optional because it was guaranteed to never be nil. As a result, that
variable was eliminated as a potential source of crashes.

14 | Chapter 2: Optional Types, Structures, & Enumerations


Structures Isolate Mutation
A Swift structure (struct) is like an instance of a class: it groups val‐
ues together, the values can be fixed or variable, and it includes
methods that operate on those values. However, unlike an instance
of a class, a structure is a value type. Assigning a structure to a vari‐
able creates a fresh copy, decoupling the assignee from the assigned
value. Whenever a struct is assigned to a new variable, as for
instance when passed as an argument to a function, the struct
is passed by value; in other words, the new variable receives a copy of
every value in the structure.

Structure Mutation Guarantees


A structure provides two guarantees about its mutabil‐
ity:

1. When passed to another routine or placed in


another variable, the original structure is insulated
from any changes to the passed structure and vice
versa.1
2. When a structure is associated with an identifier
via a let statement, the value of the identifier may
not change.

Using structures can prevent bugs. Suppose you need to send a


request to a database to find out the current temperature in a given
city, and you also need to check and see how long the database took.
You could use a class with an instance variable, startTime:
class TemperatureRequestClass {
let city: String
var startTime: Date? = nil // an optional Date

init(city: String) {
self.city = city
}
}
After creating a request object:
let request = TemperatureRequestClass(city: "Paris")

1 Unless it is passed in-out, in which case the caller must mark it with an ampersand.

Structures Isolate Mutation | 15


you might hand it off to be processed:
request.startTime = Date.now
sendToDB(request: request, callback: receivedResponse)
and later print the time difference:
func receivedResponse(temperature: Int) {
let dbTime = Date.now.timeIntervalSince(request.startTime!)
print("It took", dbTime,
"seconds to discover that the temperature in",
request.city, "is", temperature)
}

But if there’s a bug in that sendToDB routine, it could corrupt the


startTime!
func sendToDB(
request: TemperatureRequestClass,
callback: (Int) -> Void
) {
… // Do lots of slow work to prepare to connection
request.startTime = Date.now //The BUG!
… // Send the request on the prepared connection
}

Now your requestTime calculation would be wrong. (See


Figure 2-1.)

Figure 2-1. Because instances of classes are reference types, when


request is passed to sendDB that function gets a reference to the same
object. Then when it incorrectly mutates startTime, the original object
is corrupted.

Swift provides a better way—using a structure instead of a class:

16 | Chapter 2: Optional Types, Structures, & Enumerations


struct TemperatureRequestStruct {
let city: String
var startTime: Date? = nil

init(city: String) {
self.city = city
}
}

Because your calling code alters the startTime, which is contained


in a structure, it must put that structure in a var, not a let:
var request = TemperatureRequestStruct(city: "Paris")
Now, the Swift compiler catches the error before the program can
even run!
func sendToDB(
request: TemperatureRequestStruct,
callback: (Int) -> Void
) {
… // Do lots of slow work to prepare to connection
request.startTime = Date.now // ILLEGAL: will not compile!!
… // Send the request on the prepared connection
}

Why is this assignment an error? Function parameters are lets by


default, and the let-ness of the request parameter “flows down”
into the startTime field.
But what if you see the compile error and try a quick fix to get your
code through the compiler?
var mutableRequest = request
mutableRequest.startTime = Date.now
Swift will still protect your code from the bug because even though
sendToDB has changed the startTime, it would not change for the
caller. The caller would not see the mutation because when the
request was passed to sendToDB, it was copied. (See Figure 2-2.)
Value types are always copied.2

2 Swift does include a way to pass in a structure so that the callee can mutate it—in-out
parameters. But the call site looks different when passing such a parameter, so that you
need only inspect the local part of the code to see that there might be a possibility of
mutation.

Structures Isolate Mutation | 17


Figure 2-2. Because structures are value types, the sendToDB function
receives a whole new copy of the request. The first attempt at mutation
won’t even compile because parameters are lets. If the request is
assigned to a mutableRequest var, the mutation will compile but won’t
corrupt the original.

Mutating Methods
Unlike any other value type, a structure can include methods that
mutate the contents of var fields in the structure. As a helpful signal
to the programmer, such methods must be annotated with mutat
ing. This requirement highlights the places where side effects could
occur:
struct TemperatureRequestStruct {

var startTime: Date? = nil
mutating func clearStartTime() { startTime = nil }
}

Since a let prohibits mutation, a mutating function may be invoked


only upon a var. Unlike mutating an instance variable of a class, a
mutation to a structure only changes the copy referenced by one
variable:

18 | Chapter 2: Optional Types, Structures, & Enumerations


let request1 = TemperatureRequestStruct(city: "Paris")
var request2 = request1 // makes a copy because is a struct

request1.clearStartTime() // ILLEGAL: cannot mutate a let


request2.clearStartTime() // OK, but does not change request1

Structures Containing References to Objects


A structure may contain a reference to an instance of a
class, and vice versa. If a structure contains such a ref‐
erence, some of the desirable properties of a value type
may be lost. In particular, if a structure includes an
attribute that computes its result by querying a class
instance, the result may be subject to unexpected
changes. This problem is transitive: it can occur even if
your value type contains a value type from a library
and that type contains a reference to a class instance.
Despite its potential for havoc, it is the above feature
that enables the Swift standard library to provide one
of the most valuable features of Swift, collection value-
types: Swift’s Arrays, Dictionaries, and Sets are struc‐
tures, and therefore provide strong support for
mutation isolation and debugging. Under the covers,
they use class instances to optimize mutation with the
goal of providing both performance and robust seman‐
tics. For instance, if you write a function that passes an
array into a library, you can rest assured that the
library cannot change your array. It behaves as if
copied, but can often avoid the actual expense of copy‐
ing.

Default Implementations with Protocol Extensions


Swift classes can inherit from each other, but if you want to factor
out behavior that two structures share, instead of creating a super-
class, you must use a protocol extension. This technique is sometimes
called protocol-oriented programming. For example, suppose you
write two similar structures, a temperature request and an ozone-
level request:
struct TemperatureRequest {
let city: String
let state: String
var startTime: Date? = nil

Structures Isolate Mutation | 19


init(city: String, state: String) {
self.city = city
self.state = state
}
var cityAndState: String { return city + ", " + state }
}
struct OzoneRequest {
let city: String
let state: String
var startTime: Date? = nil

init(city: String, state: String) {


self.city = city
self.state = state
}
var cityAndState: String { return city + ", " + state }
}

Both structures have identical cityAndState properties that return a


string combining the city and state. You can factor out the common
code with a protocol and a protocol extension. First, the
Request protocol ensures that the properties used by the common
code, in this case city and state, are implemented by the adopter
of the protocol. Then the protocol extension adds the new cityAnd
State property to all objects that conform to the Request protocol.
protocol Request {
var city: String {get}
var state: String {get}
}
extension Request {
var cityAndState: String { return city + ", " + state }
}
struct TemperatureRequest: Request {
let city: String
let state: String
var startTime: Date? = nil

init(city: String, state: String) {


self.city = city
self.state = state
}
}
struct OzoneRequest: Request {
let city: String
let state: String
var startTime: Date? = nil

20 | Chapter 2: Optional Types, Structures, & Enumerations


init(city: String, state: String) {
self.city = city
self.state = state
}
}
A protocol extension cannot add new requirements to a proto‐
col, nor can it add additional data fields to an object; all it can do is
add implementations of new behavior to the types that adopt the
protocol. (A full description of protocol extensions is outside the
scope of this book; see The Swift Programming Language (Swift 3).)
What if you need to express a multilevel hierarchy? Protocols can
inherit from other protocols, but it’s not quite the same as subclass‐
ing—for example, there is no super.
Structures with protocols and protocol extensions provide one way
to replace a class hierarchy and limit mutation. Swift includes yet
another: enumerations with associated values. These can do an even
better job in the right situations.
Structures are like instances of classes that are always copied when
being passed around. Use structures wherever you don’t need sepa‐
rate references to the same mutable state. With structures, you don’t
have to code the copy operation, you don’t have to clutter up the
code with copying, and in the case of larger structures, the compiler
can optimize away much of the expense of copying. Substituting a
structure for an instance of a class changes the easiest thing to write
from passing a reference to copying a value. Bye-bye bugs.

Enumerations with Associated Values


In Swift, there is usually more than one way to express a given con‐
cept. For instance, you can choose between a class and a structure.
Moreover, Swift includes many features from the functional pro‐
gramming school. Judiciously exploited, these constructs can clarify,
shorten, and generalize your code. Like color TV, air conditioning,
or the smartphone, you may never have missed this next construct,
yet may soon find you would not think of living without it. One of
these is the enumeration with associated values.
As described in “Simple Enumerations” on page 3, a Swift enumera‐
tion represents a value taken from a fixed set—for example, one of a
fixed set of HTTP requests:

Enumerations with Associated Values | 21


enum HTTP_Request_Kind {
case get, post // other request types omitted for brevity
}
But Swift enumerations can do so much more because any case can
also include one or more associated values. Such an enumeration is
more like a discriminated union than a Java or C enumeration. It
can replace a small class hierarchy or a group of structures that
implement a common protocol.
For example, suppose you need to implement HTTP requests with
an equality (==) operation. You could create a class hierarchy, with
an abstract superclass containing common code and concrete sub‐
classes for each kind of request. But since you don’t want any asyn‐
chronous code to mutate requests, you would do better to use
structures and a protocol. The protocol would have to be generic
(because a requirement of == references the associated type, Self).
However, recall that Swift disallows the use of a generic protocol as a
type:
protocol HTTP_Request {
static func == (a: Self, b: Self) -> Bool
}
struct Get: HTTP_Request {...}
struct Post: HTTP_Request {...}
let someRequest: HTTP_Request = ... // ILLEGAL
let requests: [HTTP_Request] = ... // also ILLEGAL
So you could neither declare a variable that could contain any kind
of request, nor an array that could contain a mixture of requests.
However, since the set of requests is fixed, there is no need to allow
others to create new kinds of requests. Thus, an enumeration with
associated values is the best fit.
enum HTTP_Request {
case get ( destination: URL,
headerFields: [String: String] )
case post ( destination: URL,
headerFields: [String: String],
data: Data )
// also delete, put, & patch

func send(
completionHandler:
@escaping (Data?, URLResponse?, Error?) -> Void
) {
let s = URLSession()
let r = URLRequest(url: someURL)

22 | Chapter 2: Optional Types, Structures, & Enumerations


switch self {
case .get: // also delete
s.dataTask(
with: r,
completionHandler: completionHandler )
.resume()
case .post(_, _, let data): // also put, patch
s.uploadTask(
with: r,
from: data,
completionHandler: completionHandler )
.resume()
}
}
static func == ( lhs: HTTP_Request, rhs: HTTP_Request )
-> Bool
{
switch (lhs, rhs) {
case let (.get(u1, h1), .get(u2, h2))
where u1 == u2 && h1 == h2:
return true
case let (.post(u1, h1, d1), .post (u2, h2, d2))
where u1 == u2 && h1 == h2 && d1 == d2:
return true
// also delete, put & patch
default:
return false
}
}
}
Now that an HTTP request is represented by an enumeration, there
is a single concrete type, HTTP_Request, that subsumes every kind of
request. As a consequence, the same array can hold any kind of
HTTP_Request:
let requests: [HTTP_Request] = [
.get (destination: url1, headerFields: [:]),
.post(destination: url2, headerFields: [:], data: someData)
]
and you can test different kinds of requests for equality with each
other:
if aRequest == anotherRequest

Enumerations as Inside-Out Class Hierarchies


When you replace a class hierarchy with an enumeration, it’s like
turning the hierarchy inside out: the hierarchy first divides the code

Enumerations with Associated Values | 23


into various types, such as the request types above; then, within each
class the methods divide up the code by function. But the methods
of an enumeration first divide up the code by function, and the
switches within each function then divide up the code by case.
The enumeration isn’t always better than the hierarchy. There are
drawbacks to using an enumeration:
More code
Instead of implicit dynamic dispatching within a class hierarchy,
you must write explicit switch statements.
Less extensibility
You can add a subclass to a class that was defined in a different
file, but you cannot add a case to an enumeration that was
defined in a different file. (This restriction can be a benefit
where appropriate.) An enumeration is favored when the set of
cases is less likely to expand later; a class hierarchy is favored
when the set of cases is more likely to expand, or be expanded
by importers of your library.
Awkward for common data
A superclass or protocol offers better support than an enumera‐
tion for data attributes that are common to all the alternatives. It
may be better to use a structure holding the common attributes
and an enumeration for the specifics.
As you get used to Swift, you will find more and more uses for
generic enumerations with associated values.
An enumeration with associated values provides a single, concrete
type for disparate cases. Where the set of alternatives is fixed and
has few data attributes in common, an enumeration with associated
values can significantly improve the quality of your code.

Choosing an Aggregate
Tuples, classes, structures, protocols, enumerations: Swift offers
many different aggregates. Which to use in any given situation?
Figure 2-3 and the following subsections provide a simplified guide.
It neglects two aspects: some constructs may run faster than others,
and classes offer more implicitly dynamic dispatch of methods than
do other aggregates.

24 | Chapter 2: Optional Types, Structures, & Enumerations


Figure 2-3. Issues to ponder when choosing an aggregate

Instantiate a Class for a Thing with an Identity


Use an instance of a class to represent a thing in the real world with
an identity that persists as its state changes. For example, to model a
user whose identity remains the same as his location changes, you
use a class:

Choosing an Aggregate | 25
class User {
let name: String // the User’s name can not change
var location: String // the User’s location can change

init( name: String, location: String ) {


self.name = name
self.location = location
}
}
All of the different variables in your program that refer to the same
user will “see” the user’s location change when any variable changes
the location instance variable.

Otherwise, Replace the Class with a Value Type


An instance of a class is passed by reference: any portion of your pro‐
gram with that reference can couple to any other portion with that
reference by changing a field in that instance. In contrast, a value
type is passed by value: handing off a value type to another portion
of your program (or a library) does not create the potential for
future interaction.
The potential for interaction impedes the prevention and elimina‐
tion of bugs. Most bugs surface as undesired behavior resulting from
unexpected values in data structures. Fixing the bug requires you to
find the statement that wrote the bad value. But if the bad value
occurs in an instance of a class, you must examine every place in the
program that could possibly refer to that instance. Even if the fault
turns out to be in a part of the program that is obviously related to
the bug, the mere chance that it could be in some unrelated part
imposes significant practical and psychological overhead in the
search for the offending code.
Value types help with both sorts of reverse reasoning. They help to
get from crash to offending code by reducing coupling. With value
types, your program has fewer potential interconnections that you
must trace and rule out. They also help with backtracking from code
to rationale because there are fewer things to be believed about a
value type than a reference type. Once an instance of a value type is
referenced by a let, you don’t have to believe any details about how
its mutation preserves invariants because there can be no further
mutations. Whenever possible, replace a class with a value type,
such as a structure or enumeration.

26 | Chapter 2: Optional Types, Structures, & Enumerations


Value Type Mutation Restrictions
• The values associated with an instance of an enu‐
meration cannot change.
• The immutability implied by a let constant hold‐
ing a tuple or structure flows down into its fields.
• Each time a tuple or structure is passed to another
variable it is copied. The two variables are decou‐
pled unless passed via in-out, and in that case the
caller cannot pass a constant and must mark the
variable to be passed with an ampersand prefix.
• Any member function that mutates a structure
must be specially marked and cannot be invoked
on a constant.

If you are currently using a language with little support for optional
types or value types, your code will benefit from moving to Swift
and using them wherever you can. The assurances they provide
about freedom from nil values, mutation, and coupling will help you
write cleaner code. This code will be easier to reason about, main‐
tain, and debug.

Choosing an Aggregate | 27
CHAPTER 3
Swift Promises to Tame
Asynchrony

Server-side applications typically make requests to other web serv‐


ices or databases, and these requests are usually asynchronous; the
application does not stop to wait for the result to return. These
requests may succeed or fail, and often the result contains the input
to the next asynchronous request. For example, consider an applica‐
tion that can find the temperature at the user’s home. The applica‐
tion makes a request to a database to discover where the user lives,
then makes a subsequent request to a weather service to get the tem‐
perature in that location. But if the database is missing an entry for a
user, the subsequent request to the weather service must be skipped.
In addition to sequencing the chain of asynchronous operations, an
application must vary the sequence to cope with errors. The
required logic can be a challenge to program.
This chapter shows how Swift’s support for first-class functions can
be harnessed to implement promises, one of the most effective con‐
structs for programming such chains. After reading this chapter,
you will understand how promises work, and why they are so bene‐
ficial. To illustrate this, we have implemented a very basic version of
PromiseKit called MiniPromiseKit that you can clone, refer to, and
use in your projects.
This chapter explains promises in three incremental steps:

29
1. Synchronous operations are linked together in a chain, each
operation depending on the previous one, and each potentially
throwing an exception. By using the Result enumeration, the
divergent control flow that results from handling exceptions
gets restructured into a simpler linear data flow that is easier to
follow.
2. The nested callbacks needed to chain asynchronous operations
are hard to read and maintain. This section shows how to trans‐
form the nest into a straightforward chain of BasicPromises.
3. Steps 1 and 2 converge, combining Result and BasicPro
mise into Promise. This synthesis simplifies and clarifies the
construction of chains of asynchronous operations that cope
with errors at any stage.
Each of these steps takes advantage of Swift’s extensibility and static
checking to create more concise and robust code.

PromiseKit Conventions
Faced with many different names for the concepts in
this chapter, we chose the terminology used in Promis‐
eKit because it is one of the most popular libraries in
this domain. It is a third-party, open-source library
written by Max Howell and available at promiseKit.org
or at https://github.com/mxcl/PromiseKit. For
instance, we use the terms fulfill and reject instead
of the terms success, fail, or map, etc.

Step 1: Chaining Synchronous Errors


To set the stage for programming chains of asynchronous requests,
you encapsulate exceptions thrown by synchronous calls. For exam‐
ple, suppose you need to print out the ambient temperature at a
user’s home. You are composing two calls: one that returns the city
where a user lives, and another that returns the temperature of a
given city. Each operation can fail by throwing an exception:

30 | Chapter 3: Swift Promises to Tame Asynchrony


enum City { case Austin, Mountain_View, Podunk }
enum Errors: String, Error { case unknownUser, unknownCity }

func getCity(of user: String) throws -> City {


switch user {
case "Rob": return .Austin
case "David": return .Mountain_View
case "John": return .Podunk
default: throw Errors.unknownUser
}
}
func getTemperature(in city: City) throws -> Int {...}
Since each call returns a unique exception, you coalesce the excep‐
tion handling into a single catch block that reports a specific error:
do {
let city = try getCity(of: user)
let temperature = try getTemperature(in: city)
print("temperature:", temperature)
}
catch { print("error:", error) }

In the preceding code, the getCity and getTemperature functions


either return an answer or throw an exception. In turn, each of their
call sites must implicitly test for the exception and conditionally
branch to the catch block. But in order to prepare for asynchronous
requests, you need to eliminate those branches. So, you’ll change
each of those functions to return something, a Result, that contains
either an answer or the error, with a bit of information to indicate
which. Following PromiseKit nomenclature, call the successful case
fulfilled and the type of that value FulfilledValue. Call the error
case rejected. It will always have type Error.
How do you implement Result? Use a Swift generic enumeration
with associated values.1 Just as with a generic class, a generic enu‐
meration can handle a multitude of result types, including in this
example both City and Int. Here is the enumeration with its cases:2

1 If you know other functional languages, you might recognize the enumeration by
names such as “maybe” or “either.”
2 Throughout this chapter, in the interest of brevity, we omit public on what would nor‐
mally be exported library declarations. We also omit the @discardableResult func‐
tion attribute, which silences warnings when a function is invoked without consuming
its return value.

Step 1: Chaining Synchronous Errors | 31


enum Result<FulfilledValue> {
case fulfilled(FulfilledValue)
case rejected(Error)
}

The last argument to the initializer for Result is a closure, which the
initializer invokes. If the closure returns normally, the initializer cre‐
ates a fulfilled Result that includes the closure’s return value. If
the closure throws an exception, the initializer creates a rejected
Result that includes the error:
extension Result {
init( of body: () throws -> FulfilledValue ) {
do { self = try .fulfilled( body() ) }
catch { self = .rejected ( error ) }
}
}

Using trailing closure syntax to omit the parentheses, a Result


might be created as follows:
let aResult = Result<City> { try getCity(of: "David") }

Success Can Be Vacuous


Since Swift treats the Void type consistently, Result
can also be used with a closure that does not return
anything, such as Result() {}.

Now that you have a Result<City>, use it to get the temperature for
a city. Although you could write a switch statement for this particu‐
lar chain:
func getTemperatureFromCity( aResult: Result<City> )
-> Result<Int>
{
switch aResult {
case let .fulfilled(city):
return Result<Int> { try getTemperature(in: city) }
case let .rejected(err):
return Result<Int>.rejected(err)
}
}
You can do better by encapsulating the switch statement inside a
reusable member function of Result:

32 | Chapter 3: Swift Promises to Tame Asynchrony


extension Result {
func then <NewFulfilledValue> (
execute body:
(FulfilledValue) throws -> NewFulfilledValue
) -> Result<NewFulfilledValue>
{
switch self {
case .rejected (let e):
return .rejected(e)
case .fulfilled(let r):
do { return try .fulfilled( body(r) ) }
catch { return .rejected ( error ) }
}
}
}
Now the chain can be written more simply:
Result { try getCity(of: user) }
.then { try getTemperature(in: $0) }
.then { print("temperature:", $0) }

If getCity encounters an error, the calls to getTemperature and


print will never occur because getCity returns a rejected case.
To process rejected results, two methods, recover and catch,3
respectively, allow errors to be replaced with successful results or
merely caught:
extension Result {
func recover(execute body: (Error) throws -> FulfilledValue)
-> Result
{
switch self {
case .fulfilled:
return self
case .rejected(let e):
do { return try .fulfilled( body(e)) }
catch { return .rejected( error ) }
}
}
func `catch`(execute body: (Error) -> Void)
-> Result
{
switch self {
case .fulfilled: break

3 Following PromiseKit, we reuse “catch” as a method name to introduce error-handling


code. (Swift permits the reuse of many of its reserved words by quoting them in back‐
ticks.) Don’t confuse this “catch” method invocation with the “catch” Swift keyword.

Step 1: Chaining Synchronous Errors | 33


case .rejected(let e): body(e)
}
return self
}
}
For example, if you were willing to assume that any unknown users
lived in Austin, you could situate them there with a recover:
// Type inference allows omission of <City> after Result below:
Result { try getCity(of: user) }
.recover { _ in .Austin }
.then { try getTemperature(in: $0) }
.catch { print("error:", $0) }
.then { print("temperature:", $0) }
Imagine the complexity of the control flow you would otherwise
need, such as branching into recovery code if the first link failed,
and bypassing the temperature query if the recovery code failed. The
code would be complicated and obscure; it would be easy to make
mistakes. Result modularizes the control flow: it is linear at this
level, from recover to then to catch to then. It is only inside of
these methods that the control flow branches. Result exploits Swift’s
support for higher-order functions to pass the work required to
handle each eventuality (as closures) into other functions (then,
catch, and recover). Instead of conditionally branching at the top
level, the enumeration carries the information as data.

Functions Operating on Functions


Result encapsulates control flow by providing functions that oper‐
ate on functions. Such functions that operate on functions instead of
data are called "higher-order functions.” These functions-on-
functions are a hallmark of functional programming and can be a
bit mind-bending at first, but once understood, they can do a lot to
improve the quality of your code. To stave off cognitive overload,
you can try out these functions-that-wrap-functions a little at a
time, first to defend against nils (for instance, by using map or
flatMap), then to untangle error handling. As code gets moved into
higher-order functions, the compiler helps out more and more to
simplify the code and prevent bugs.

So far, this book has dealt with only synchronous operations, but
server-side programming often relies on asynchronous operations

34 | Chapter 3: Swift Promises to Tame Asynchrony


such as web requests and I/O. The next step shows how functions
operating on functions can help tame asynchrony by unpacking nes‐
ted callbacks.

Step 2: Linearizing Nested Callbacks


Now that you’ve straightened out the code to handle synchronous
errors, apply the same techniques to straighten out nested call‐
backs: pass the work into other functions as closures, and arrange
these functions into a tidy linear sequence.

Composing Asynchronous Operations


Asynchronous operations are usually programmed with callbacks,
and when an operation depends on the result of a previous opera‐
tion, those callbacks must be nested. But this nesting obfuscates the
sequence of operations. For example, suppose getting the user’s city
and that city’s temperature require asynchronous operations with
callbacks:
func requestCity(
of user: String,
_ callback: @escaping (City?, Error?) -> Void
) {
DispatchQueue.global(qos: .userInitiated).async {
switch user {
case "Rob": callback(.Austin, nil)
case "David": callback(.Mountain_View, nil)
case "John": callback(.Podunk, nil)
default: callback(nil, Errors.unknownUser)
}
}
}
func requestTemperature(
in city: City,
_ callback: @escaping (Int?, Error?)-> Void
) {...}

Getting the temperature of the user’s city now requires a callback


nested inside of a callback. Despite Swift’s trailing closure syntax, the
code below is still obscure:
requestCity(of: user) {
// Outer callback:
if let city = $0 {
requestTemperature(in: city) {
// Inner callback:

Step 2: Linearizing Nested Callbacks | 35


if let temperature = $0 {
print("temperature:", temperature)
}
else {
print("error:", $1)
}
}
}
else {
print("error:", $1)
}
}
Although the code first requests a city and then requests a tempera‐
ture for that city, callbacks don’t allow the second request to be writ‐
ten after the first. Instead, it ends up inside the first. Since
asynchronous programming with callbacks is functional program‐
ming, it is not surprising that its practitioners have found a way out.
Because Swift supports functional programming, that way is open to
you.

Replacing Callbacks
In order to get rid of nested callbacks, an asynchronous operation
must return some thing that can be fed to the next operation. That
thing must be something that can be created and returned before the
first asynchronous operation completes. That way, it can be passed
on to the next operation without being nested inside the first opera‐
tion. This thing is commonly called a future or a promise. It works
like a Unix pipe between two threads: the first thread creates the
pipe and immediately returns it. Then, the second thread receives
the pipe and waits for some data. Later, the first thread computes its
data and sends it down the pipe. The pipe wakes up the second
thread and presents it with the data from the first thread.
For now, defer error-handling considerations to “Step 3: Coping
with Asynchronous Errors” on page 40, so define a BasicPromise,
which does not account for errors. Recall that Result is generic
because the value for success can be any type, but that type can be
determined statically. BasicPromise is also generic for the same rea‐
son. Call the type of the value for (eventual) success Out
come. Because a shared BasicPromise communicates the readiness
of a result, it must be a class: classBasicPromise<Outcome>.

36 | Chapter 3: Swift Promises to Tame Asynchrony


BasicPromise has three fundamental operations4:
Creating
A BasicPromise object:
let aBasicPromise = BasicPromise<Int>()

Fulfilling the promise


When an outcome becomes available, fulfill the promise with
the outcome:
aBasicPromise.fulfill(17)

Chaining one promise to another


The code that consumes the outcome of a fulfilled promise goes
in the body of a then. Although this body is called back, it does
not itself invoke the next step. Instead, it returns a new value,
which could be another BasicPromise, to continue the chain.
Here is a simple example using then:
aBasicPromise
.then {
// asynchronous request returning a BasicPromise
}
.then { print($0) }

Specifying a DispatchQueue
Since then runs its body asynchronously, you
might want the body to run on a specific
DispatchQueue. Methods such as then take an
optional argument to allow you to specify a Dis
patchQueue. In PromiseKit, the DispatchQueue
defaults to .main, which can often result in a
deadlock. We suggest .userInitiated instead.

There are three versions of then:


1. The simplest one takes a body with no return value and
calls it when the original BasicPromise is fulfilled:

4 See MiniPromiseKit for full implementations.

Step 2: Linearizing Nested Callbacks | 37


func then(
on q: DispatchQueue = BasicPromise.defaultQueue,
execute consumer:
@escaping (Outcome)
-> Void
)
2. Sometimes the body synchronously processes the result of
the original BasicPromise. In such a case, the then opera‐
tion needs to continue the chain by returning a new
BasicPromise for the transformed result:
func then<NewOutcome> (
on q: DispatchQueue = BasicPromise.defaultQ,
execute transformer:
@escaping (Outcome)
-> NewOutcome
) -> BasicPromise<NewOutcome>
3. Finally, the body may perform another asynchronous opera‐
tion with the result of the original BasicPromise. Such a
body must return a new BasicPromise with the new result.
As in the previous case, the then operation containing that
body also needs to return a new BasicPromise for the new
result. However, the BasicPromise returned by the then
method cannot be the same one that its body returns
because the then method must return something immedi‐
ately, so then returns a new BasicPromise, which will even‐
tually be fulfilled with the right result:
func then<NewOutcome>(
on q: DispatchQueue = BasicPromise.defaultQ,
execute asyncTransformer:
@escaping (Outcome)
-> BasicPromise<NewOutcome>
) -> BasicPromise<NewOutcome>

In order to use BasicPromises, you will have to wrap each request


so that it returns a BasicPromise result instead of receiving a call‐
back argument. For example, ignoring errors for now, assume there
is an existing function that has a callback, requestCityIgnoringEr
rors:

38 | Chapter 3: Swift Promises to Tame Asynchrony


func requestCityIgnoringErrors(
of user: String,
callback: @escaping (City) -> Void
) {
// Simulate the asynchrony of a web request
DispatchQueue.global(qos: .userInitiated).async {
switch user {
case "Rob": callback(.Austin)
case "David": callback(.Mountain_View)
case "John": callback(.Podunk)
default: abort()
}
}
}
Although you could write a general wrapper for turning a function
that receives a callback into one that returns a promise, skip that
step for now and just write two wrappers for this running example:
func requestCityIgnoringErrors(of user: String)
-> BasicPromise<City>
{
let promise = BasicPromise<City>()
requestCityIgnoringErrors(of: user) { promise.fulfill($0) }
return promise
}
// Also wrap the temperature request:
func requestTemperatureIgnoringErrors(in city: City)
-> BasicPromise<Int>
{…}

Using BasicPromises, the composition of asynchronous queries


becomes just as simple as that of synchronous ones:
requestCityIgnoringErrors(of: user)
.then { requestTemperatureIgnoringErrors(in: $0) }
.then { print("Temperature is", $0) }

The BasicPromise has wrapped the asynchrony inside of an abstrac‐


tion. Now, the call to print need not be nested inside the tempera‐
ture request, which need not be nested inside the city request.
Instead, a promise is passed along from each link in the chain to the
next. Inside of these promises, events are being synchronized,
threads launched, and answers awaited. Requests are written in the
order they occur and asynchronous code becomes clearer.
But what about errors?

Step 2: Linearizing Nested Callbacks | 39


Step 3: Coping with Asynchronous Errors
The Result enumeration and the BasicPromise class combine to
handle asynchronous errors with style and grace.

Promising Either Success or Failure


You could handle asynchronous errors by using a Result enumera‐
tion as the Outcome of a BasicPromise:
func requestCity(of user: String) -> BasicPromise<Result<City>>
{
let promise = BasicPromise<Result<City>>()
// Simulate the asynchrony of a web request
DispatchQueue.global(qos: .userInitiated).async {
switch user {
case "Rob":
promise.fulfill(.fulfilled(.Austin))
case "David":
promise.fulfill(.fulfilled(.Mountain_View))
case "John":
promise.fulfill(.fulfilled(.Podunk))
default:
promise.fulfill(.rejected(Errors.unknownUser))
}
}
return promise
}
func requestTemperature(in city: City)
-> BasicPromise<Result<Int>>
{…}
With these request routines, the asynchronous chain would be:
requestCity(of: user)
.then(on: myQ) {
cityResult -> BasicPromise<Result<Int>> in
switch cityResult {
case let .rejected(err):
let bp = BasicPromise<Result<Int>>()
bp.fulfill(.rejected(err))
return bp
case let .fulfilled(city):
return requestTemperature(in: city)
}
}
.then(on: myQ) {
$0.then { print( "Temperature is", $0) }
$0.catch { print( "error: ", $0) }
}

40 | Chapter 3: Swift Promises to Tame Asynchrony


But this code is a bit cumbersome. You can fix it.

Promises with Handy Shortcuts for Success Versus


Failure
The final version, called Promise, is presented here. It stands for the
result of an asynchronous computation that may fail. Under the cov‐
ers, the implementation of Promise in the MiniPromiseKit is just a
structure holding a BasicPromise that takes a Result for its Out
come.
struct Promise<FulfilledValue> {
let basicPromise: BasicPromise<Result<FulfilledValue>>
}

The Promise methods named “then” only run their bodies if the
computation has succeeded, and the Promise method named
“catch”5 only runs its body if the computation has failed. Unlike a
BasicPromise, a Promise only gets fulfilled if the computation
succeeds, and it gets rejected if the computation fails. In addition
to explicitly calling reject, a body can also signal failure by throw‐
ing an exception.
Here is a requestCity function that returns a Promise. It creates the
Promise with pending, a static function that returns a new Promise,
together with its fulfill and reject functions. It simulates the
asynchrony that would occur with a real web request with an asyn‐
chronous invocation of the switch statement:
func requestCity(of user: String) -> Promise<City> {
// obtain a new Promise & fulfill & reject functions
let (promise, fulfill, reject) = Promise<City>.pending()
DispatchQueue.global(qos: .userInitiated).async {
switch user {
case "Rob": fulfill(.Austin)
case "David": fulfill(.Mountain_View)
case "John": fulfill(.Podunk)
default: reject(Errors.unknownUser)
}
}
return promise
}

5 “Catch” here is just another method name, a sort of pun on the catch keyword in Swift.

Step 3: Coping with Asynchronous Errors | 41


Using Promises, a chain of asynchronous requests that also handles
errors becomes quite readable:
requestCity(of: user)
.then (on: myQ) { requestTemperature(in: $0 ) }
.then (on: myQ) { print("Temperature is", $0 ) }
.catch(on: myQ) { print("error:", $0 ) }

The first requestCity sends out a request. If the request later fails,
each of the then blocks will bypass its body, and only the catch will
run its body. If the request instead succeeds, the first invocation of
the then method will run its body, requesting the temperature of the
city that was returned from the city request. Then, if the tempera‐
ture request succeeds, the temperature will be printed. Otherwise,
the body of the catch will run.
Although the code above is asynchronous, it is as clear as the syn‐
chronous version:
Result { try getCity(of: user) }
.then { try getTemperature(in: $0) }
.then { print("temperature:", $0) }
.catch { print("error:", $0) }
Space precludes a thorough tour through the implementation here;
see MiniPromiseKit.
Asynchrony and errors have been abstracted away! Swift’s support of
functional programming and type-based overloading have raised
the level of discourse so that the code focuses on what is happening,
not when or what-if failure.

42 | Chapter 3: Swift Promises to Tame Asynchrony


CHAPTER 4
Swift Package Manager

Reinventing the wheel is a great way to waste your time and effort.
When another developer has already created a solid library that per‐
forms a function needed by your application, you can save weeks of
time if you are able to reuse it. The Swift ecosystem is growing very
quickly, and often there are many similar libraries that solve the
same problem. If you adopt Swift for your project, you will find
hundreds of Swift packages on GitHub or listed in the IBM Swift
Package Catalog, which can be easily incorporated with the aid of
the Swift Package Manager (SwiftPM). Whether you are looking to
store your data in a database, parse JSON, or use reactive program‐
ming, chances are that there is a Swift package out there that can do
what you need it to.
As a developer, you might want to add one of these libraries to your
project. This is where the SwiftPM comes into play. If you already
use a package manager such as Rust’s Cargo, NodeJS’s npm, Java’s
Maven, or Ruby’s Gem, you will feel right at home using the
SwiftPM. It helps to resolve dependencies by automatically down‐
loading the sources that match the version you want, and arranges
the build process so that you have the executable or Swift library
that you need. Because the SwiftPM works on many platforms, you
can kick off builds, test, or fetch dependencies with shell commands.
In addition, you can use it to generate an Xcode project, as
described in “Developing in Xcode” on page 50.

43
Semantic Versioning
When SwiftPM fetches a required package, it must fetch an appro‐
priate version that will work with your project. So, each package
uses a three-level version number, consisting of major, minor, and
patch numbers:

• The major version indicates API compatibility.


• The minor version indicates functionality.
• The patch version indicates fixes.
The major version is incremented when there are changes made to
the API that could break the packages that depend on it. The minor
version is incremented when there is a new feature added that does
not break the compatibility with older versions. The patch version
is incremented when bugs are fixed that do not break backwards
compatibility.
SwiftPM will only clone the dependencies at specific revision points
set by the developer’s tags. Therefore, you cannot fetch the latest
commit from the head of a specific branch; instead, SwiftPM will
clone the source code at specific tagged revisions in the repository.
Although this requirement may seem limiting, it prevents broken
builds and introduction of bugs by ensuring that every commit
made to the repository does not affect the many applications that
could be using the library.
Consider this typical workflow: suppose your project uses the mas‐
ter branch for your production-ready releases, and currently your
project is at version 0.1.3. When developing a new API-breaking
feature, you can branch from the master into a new feature branch
called add_feature. From this feature branch, you can tag a new
release: 0.2.0. The existing stable code at 0.1.3 will not be affected.
You can change the Package.swift file in your executable to point to
majorVersion 0 and minor version 2 for your development. This
will clone the repository at version 0.2.0 and not 0.1.3.

Creating an Application
This section steps you through the process of using the SwiftPM to
create a new project from scratch, find a package to include, import
that package, and build your project.

44 | Chapter 4: Swift Package Manager


First, generate a new executable project by creating a new directory
called MyProject. The following command uses the name of the
current working directory for automatically creating a new project
with that name:
~$ cd MyProject
MyProject$ swift package init --type executable

Creating executable package: MyProject


Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/main.swift
Creating Tests

There were a few files created in that step. The Sources/main.swift


is the main entry file, and it starts off with very basic Swift code for a
“Hello, World!” application. All executable projects or targets will
contain a main.swift in the Sources directory or a subdirectory in
the Sources directory, respectively.
print("Hello, world!")

The Tests directory is empty. See “Creating Your Own Library” on


page 51 for unit-testing information.
The built .gitignore file specifies which files or directories to
exclude from the git repository. Here, these are the .build directory,
Packages directory, and any .xcodeproj files. These files and fold‐
ers will be built during the compilation step, and are not necessary
to check into your project.
The Package.swift file is a manifest file containing the name of the
package and other information such as the dependencies that your
project has:
import PackageDescription
let package = Package(name: "MyProgram")

Now, produce an executable file from your project by running swift


build:
$ swift build
Compile Swift Module ‘MyProject’ (1 sources)
Linking ./.build/debug/MyProject

The swift build command reads the Package.swift file and


checks if there are any dependencies to fetch. Next, it runs the Swift
compiler, swiftc, on each of your .swift files to convert them into

Creating an Application | 45
swift modules. Finally, it links the .swiftmodule files to produce
your executable, which is left in the .build/debug/ directory. Run
your program now:
$ ./build/debug/MyProject
Hello, world!

Compiling in Release Mode


Projects can be built in either debug or release mode.
By default, swift build compiles the project in debug
mode. When moving your project to production, con‐
sider building your project in release mode so that you
have the best performance. You may compile with
swift build –-configuration release, which will
generate an optimized executable in the direc‐
tory .build/release. Consequently, debugging sym‐
bols will be included and optimizations excluded so
that LLDB can examine data, add breakpoints, and
step through your program.

The Package.swift file describes the project and how to build it.
Builds are not described by a special language like Gradle or a
Makefile—it’s native Swift code! You don’t have to learn another lan‐
guage to control the build process. You can write if statements that
will select the correct library to use based on whether you are run‐
ning on Darwin or Linux:
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import Darwin
#elseif os(Linux)
import Glibc
#endif
The system’s environment variables can also influence the build pro‐
cess. For example, you can check for the existence of an environ‐
ment variable before executing a code block:
let env = ProcessInfo.processInfo.environment
if env["MY_SYSTEM_ENV"] == "true" { ... }

46 | Chapter 4: Swift Package Manager


Importing a Library in Your Project
Once the necessary boilerplate for a Swift project has been created,
you can add new dependencies to it—for example, that nifty pipe
operator ( |>) introduced in Chapter 1. First, find the source code,
which is often in GitHub. You can find libraries with the IBM Swift
Package Catalog. Open the Catalog in your browser and search for
“Pipes” in the search bar as shown in Figure 4-1.

Figure 4-1. Searching for a package in the IBM Swift Package Catalog

Select the Pipes package to view the details about the project shown
in Figure 4-2. These include the last tagged version number, dis‐
played at the top of the right column:

Importing a Library in Your Project | 47


Figure 4-2. Pipes library description page on IBM Swift Package Cata‐
log

Click on “Include project in your code,” which will copy the neces‐
sary dependency link to your clipboard so you can paste it into Pack
age.swift.
Go back to your Package.swift file and insert that dependency in
your Package constructor:
import PackageDescription

let package = Package(


name: "MyProject",
dependencies: [
.Package(url: "https://github.com/IBM-Swift/Pipes.git",
majorVersion: 0, minor: 1),
]
)

Other Git Servers


In the dependencies package definition, any Git repos‐
itory can work: it can be on the local filesystem, a pri‐
vate Git server, or on GitHub. You can use the file,
https, or ssh protocols for any of these repositories.

48 | Chapter 4: Swift Package Manager


There are different ways to specify the package versions as described
in Table 4-1:

Table 4-1. Other ways to specify package versions


Version Range: Version(0, 1, 0)..<Version(0, 2, 0)
Major Version only: majorVersion: 0
Major and Minor Version: majorVersion: 0, minor: 1
Version: Version(0, 1, 0)
Version String: 0.1.0

Although you have the flexibility to use a large version range (such
as only specifying the major version), we recommend locking down
both the major and minor versions. Although, in theory, minor ver‐
sion changes should not break a build, they often do so at early ver‐
sion numbers.
You can now write code that uses Pipes. Open main.swift and
import the Pipes module, and write some basic code:
import Pipes

func sayHello(str: String) -> String {


return “Hello, \(str)”
}

"Alice" |> sayHello |> print

Compile with swift build. Before the compilation begins, the


SwiftPM will clone the Pipes library into a subdirectory of the
Packages directory.
$ swift build
Cloning https://github.com/IBM-Swift/Pipes
HEAD is now at 71c2c80 Improve Pipes
Resolved version: 0.1.2
Compile Swift Module 'Pipes' (1 sources)
Compile Swift Module 'MyProject' (1 sources)
Linking ./.build/debug/MyProject
The compiled program is located in the .build/debug folder. You can
now run your program:
$ ./.build/debug/MyProgram

Hello, Alice!

Importing a Library in Your Project | 49


Other useful Swift Package Commands

swift package fetch fetches package dependencies


only
swift package update updates packages to latest
version while matching
constraints

Developing in Xcode
While you can easily develop entirely on the command line using
your favorite text editor, many macOS users will want to use Xcode
for their server-side Swift development. You can use Xcode to edit
and compile the code, run tests, and profile your Swift code just like
you can with iOS and macOS projects. But first, in order to use
Xcode, a project must be created from your Package.swift. The fol‐
lowing command will create your project:
$ swift package generate-xcodeproj
Running this command line fetches the necessary dependencies
before creating the Xcode project. When you build with Xcode, it
does not use SwiftPM to build your project, but instead will use
Xcode’s native build system.
In the future, we expect to see tighter integration of SwiftPM and
Xcode.

Beware of Manually Changing Xcode Project Files


If you add or remove a dependency to your
Package.swift file, it will be necessary for you to
recreate the Xcode project again. Note that any
changes made to your project will be lost when a new
project file is generated over the old one.

You can open the project in Xcode, and once in Xcode, tools such as
the debugger, instruments, code coverage, and testing will work as
expected. If you want to run an executable, make sure you change
your active scheme in Xcode to the executable before you use the
Run (<Cmd>-r) command.

50 | Chapter 4: Swift Package Manager


Creating Your Own Library
Suppose you wanted to create a new library from scratch. What
would you have to do to create one? You could use the SwiftPM!
This section walks you through the steps to create a Pipes library
from scratch.
To start, create a new subdirectory called Pipes. Go inside this
directory and run the SwiftPM project generator to create a boiler‐
plate for a library:
$ swift package init --type library
This time the following files are generated:
./Package.swift
./.gitignore
./Sources/Pipes.swift
./Tests/LinuxMain.swift
./Tests/PipesTests/PipesTests.swift

swift init generates files for a library, just as it does for an exe‐
cutable. But instead of generating a main.swift file, it generates a
file named after the module. Instead of nothing in Tests, it creates a
very basic test suite.
Next, open Sources/Pipes.swift, erase what’s there, and add an
implementation of the Pipe operator that will be used for your
library:
precedencegroup LeftFunctionalApply {
associativity: left
higherThan: AssignmentPrecedence
lowerThan: TernaryPrecedence
}

infix operator |> : LeftFunctionalApply

@discardableResult
public func |> <A, B> (
x: A,
f: (A) throws -> B
) rethrows -> B
{
return try f(x)
}

Creating Your Own Library | 51


What hasn’t been tested doesn’t necessarily work, so add a test by
opening the Tests/PipesTests.swift file. Erase the content inside
of the testExample() function and replace it with:
import XCTest
@testable import Pipes

func double(a: Int) -> Int { return 2 * a }

class PipesTests: XCTestCase {


func testDouble() { XCTAssertEqual(6 |> double, 12) }
}

Tell the Linux test runner which methods of PipeTests to run:

extension PipesTests {
static var allTests : [
( String, (PipesTests) -> () throws -> Void )
]
{
return [
("testDouble", testDouble)
]
}
}

The allTests array is not needed to run your tests in Xcode, but it
is important for the SwiftPM to run the tests with the swift test
command.
Now, compile and test your library by running swift build, then
swift test:
Pipes$ swift build
Compile Swift Module 'Pipes' (1 sources)
Pipes $ swift test
Compile Swift Module 'PipesTestSuite' (1 sources)
Linking .build/debug/PipesTests.xctest/Contents/MacOS/PipesTests
Test Suite 'All tests' started ...
Test Suite 'PipesTests.xctest' ...
Test Suite 'PipesTests' started ...
Test Case '-[PipesTestSuite.PipesTests testDouble]'
started.
Test Case '-[PipesTestSuite.PipesTests testDouble]'
passed (0.001 seconds).
Test Suite 'PipesTests' passed at ...
Executed 1 test, with 0 failures (0 unexpected) in 0.001
(0.001) seconds
Test Suite 'PipesTests.xctest' passed at 2016-07-22 13:59:39.629.

52 | Chapter 4: Swift Package Manager


Executed 1 test, with 0 failures (0 unexpected) in 0.001
(0.001) seconds
Test Suite 'All tests' passed at 2016-07-22 13:59:39.629.

Executed 1 test, with 0 failures (0 unexpected) in 0.001


(0.001) seconds
This example includes a single test case. But the SwiftPM can run
many test cases or even larger test suites in the test directory. The
library has been built and tested, but it’s still confined to one com‐
puter. How can you set it loose to conquer the world?

Sharing a Library with the Swift Community


Now that you have a working and tested Pipes library, you should
share this library with the community. Initialize a new Git reposi‐
tory in the Pipes directory:
$ git init
Initialized empty Git repository in Pipes/.git/
Stage these source files, create a first commit, and then tag it as ver‐
sion 0.1.0:
$ git add .
$ git commit -m "Initial release"
Next, sign up for a GitHub account, if you don’t already have one,
and create a new repository with your GitHub account. Once you
have your repository created remotely, you can link your local
repository in your directory with this GitHub one as its remote ori‐
gin. To set this remote, use:
$ git remote add origin https://github.com/<my-org>/Pipes.git
Finally, push your code out to the GitHub repository with the tag
0.1.0 with:
$ git tag 0.1.0
$ git push –u origin master --tags

We suggest you add a LICENSE file, copyright headers for each of


your source files, and a README file before making your library avail‐
able to the public.

Creating Your Own Library | 53


Version conflicts
When building, you may get the dreaded error mes‐
sage: “The dependency graph could not be satisfied.”
If your dependency tree contains several common
libraries on different major, minor, and patch versions,
your build will break when the SwiftPM fetches the
Packages.swift file recursively throughout the entire
dependency chain, clones the dependencies into the
packages directory, and links the libraries in each of
the repositories. For example, if there are Pipes 1.0.1
and Pipes 1.2.3 in the dependency tree, there will be a
dependency collision and the linker will fail.

Producing More Complex Projects


A complex Swift project often incorporates multiple modules, each
contained in a different subdirectory of the Sources directory.
Because these modules might depend on one another, the build
steps must follow a logical ordering. For instance, if A depends on B,
B must be built first. The SwiftPM will compute the transitive clo‐
sure and generate an appropriate ordering, but you must specify the
individual dependency relationships.
This process is analogous to the way a Makefile specifies individual
dependencies: for each target, you add a list of dependencies. For
example, assume your Sources directory contains the following:

Sources/A/main.swift (which requires B)


Sources/B/WebController.swift (which requires C)
Sources/C/DatabaseConnector.swift

A Package.swift file will contain the following to describe the tar‐


gets and the dependencies so that the project gets built in the correct
order:
import PackageDescription
let package = Package(
name: "MyWebApp",
targets: [
Target(name: "A", dependencies: [.Target(name: "B")]),
Target(name: "B", dependencies: [.Target(name: "C")]),
Target(name: "C")]
)

54 | Chapter 4: Swift Package Manager


With this information, SwiftPM will ensure that C is built first, then
B, then A.
Now that you can have a single repository containing multiple Swift
modules, it might be unclear whether you should put them in a sin‐
gle repository or separate them into different repositories. When
making this decision, consider the following:

1. Is there valuable reusable code in the Swift modules within your


complex project? If so, factor it out.
2. Does the Swift package have executable targets as well as library
targets? Keep in mind that when someone adds your package as
their dependency, these executables will be built, too. This
might be surprising if the user was expecting only a library.

Using C Dynamic Libraries


A Swift package can leverage existing C libraries, which can be
installed with Homebrew or a Linux package manager like Ubuntu
or Debian’s APT (Advanced Package Tool). In order to create a
binding to this C dynamic library that can be used in your Swift
projects, create a system module package:
$ swift package init --type system-module
Creating system-module package: CSQlite
Creating Package.swift
Creating .gitignore
Creating module.modulemap

Inside module.modulemap, you write a simple specification that


points to the library’s header file and dynamic library. The header
files must be on absolute paths. The link argument is the name of
the dynamic library. For instance, if it is “sqlite3”, SwiftPM will find
files named libsqlite.dylib on macOS or libsqlite.so on Linux as it tra‐
verses the library search path.
module CSQLite [system] {
header "/usr/local/opt/sqlite/include/sqlite3.h"
link "sqlite3"
export *
}
When you include this Swift system module package, the Swift
importer will read from the header file and automatically generate
the necessary code to call C from Swift.

Using C Dynamic Libraries | 55


SwiftPM does not install the C library automatically, because that
would require superuser privileges. Instead, you can let each user
know how to install the C library using the operating system’s pack‐
age manager. If you do, the package name will be presented to the
user during the swift build. Currently, SwiftPM only supports the
package managers Homebrew and APT.
let package = Package(
name: "CSQLite",
providers: [
.Brew("sqlite"),
.Apt("libsqlite3-dev")
]
)
The SwiftPM makes it easy to generate a project, import an external
library into an existing project, and also create a new library for oth‐
ers to use. In addition, it supports interfacing with other libraries
not written in Swift.

56 | Chapter 4: Swift Package Manager


CHAPTER 5
Writing a To-Do List with Kitura

This chapter explains how to build a simple web service in Swift.


The service stores a list of to-do list items in a database, and is
accessed with a REST interface.
Along the way, you will learn to:

1. Create handlers for GET and POST requests


2. Receive and send JSON
3. Deploy your application to Bluemix
4. Do error handling
5. Use authentication in your application
You’ll use Kitura, which is a Swift web framework library produced
at IBM that helps developers build web applications. This frame‐
work includes an HTTP server that can listen for new client connec‐
tions and route their requests to an appropriate handler. Its
treatment of URL routing and pluggable middleware follows the
course set by other popular web middleware frameworks like Sina‐
tra (from Ruby), Flask (from Python), and Express.js (from
NodeJS).

Servers and Routers


In order to direct incoming requests to places in your program, you
must register routes with an instance of Kitura’s Router class. After
you import the Kitura module with SwiftPM, your code must create
the router:

57
let router = Router()
Before an incoming request can get to the router, an HTTP server
must be listening on a TCP port. Create the server, register it to your
Router, and have it listen on port 8090:
Kitura.addHTTPServer(onPort: 8090, with: router)
Kitura.run()

Multiple Servers
The Kitura runtime supports creating multiple HTTP
servers. These servers can be listening on different
ports, and you can also create different routers for
each. While some users may use a separate TLS/SSL
proxy for their secure transport, it is also possible to
create an HTTPS server in Kitura.

When a new request is received, Kitura creates a thread that invokes


your handler with the request. A handler (with type RouterHandler)
is a closure with three parameters: request: RouterRequest,
response: RouterResponse, and callNextHandler: ()->Void.
The request contains information that comes from the client, and it
might contain headers including authentication information and/or
a body with information about the request. The RouterRes
ponse contains the data, if any, that needs to go back out to the cli‐
ent. The callNextHandler argument is a function called to invoke
the next RouterHandler registered for the request. This function,
callNextHandler, since it’s a closure argument, can be named any‐
thing you want. If there are no other handlers, the response is sent
out to the client, and the network connection is closed.
Tell the router to send HTTP GET requests with path "/hello" to a
simple handler that just says “Hello, World!" for now:
router.get("/hello") { request, response, callNextHandler in
response.status(.OK).send("Hello, World!")
callNextHandler()
}

58 | Chapter 5: Writing a To-Do List with Kitura


Matching Requests with Regular Expressions
Instead of "/hello,” the route could have specified any
regular expression to match the incoming path. In that
regular expression, an identifier prefixed by a colon
works as a parameter in that expression that can be
used in the handler. For example, for the expression
"user/:name", a request going to "user/robert"
would match correctly, and “robert” would be available
in request.parameters["name"].

Hang Up When Done


The handler that finishes servicing a request must
invoke either callNextHandler() or response.end().
If not, the client will be busy waiting for a response
and the connection might never be closed.

Build and run your project, then test it with cURL:


$ curl 127.0.0.1:8090/hello
Hello, World!

Creating a Web Service


Next, create a very basic “to-do list.” You will create the application
in multiple steps, beginning with a rudimentary data model and
increasing its sophistication in subsequent steps. To-do items are
first stored as an array of strings, then as an array of dictionaries,
and finally as an array of structs. Finally, you’ll save to the to-do list
items in an external database.
You’ll need an array to hold the tasks:
var itemStrings = [String]()
and also two handlers: one for getting all of the to-do items and the
other for adding a to-do item. (We omit showing an example of
removing and modifying items for this tutorial.) Getting a list of
items is easier than adding a new one, so start there. The code needs
to serialize the array of strings to JSON before sending to the client.
To make JSON serialization easier than using Foundation’s
JSONSerialization class directly, you can use the SwiftyJSON
library. This library is able to serialize Swift primitive types such as
String, Int, and Double, as well as collections containing these

Creating a Web Service | 59


primitives such as Array and Dictionary. The handler sends the
serialized array to the client via the RouterResponse.send(json:)
method, which automatically sets the Content-type of the response
as application/json so that the client knows how to interpret the
result:
import SwiftyJSON
func handleGetStringItems(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) throws {
response.send( json: JSON(itemStrings) )
callNextHandler()
}

What is @escaping?
Swift requires the callNextHandler argument to be
marked @escaping because it is a closure that might
outlive the callee. More information can be found in
the Swift documentation.

When a client requests the addition of a new to-do item to the list,
the request is fulfilled by a RouterHandler that is registered to a
POST request type. A POST request will typically contain a message
body in it, but in order to extract the JSON in this body, Kitura must
first parse the body before passing it to the handler.
Kitura has a piece of middleware called BodyParser that will parse
the body of the request before sending it to the handler. BodyParser
reads the Content-Type of a message, and fills in the body field of
the RouterRequest with a ParsedBody enumeration. The enumera‐
tion includes cases for json, text, raw, and other values. But for the
BodyParser to work, you must register it on routes that need this
payload information, namely, the POST on"/v1/task". In order to
have it be used on all routes, you can use "/*":
router.all("/*", middleware: BodyParser())
In Kitura, unlike NodeJS, multiple handlers run concurrently on dif‐
ferent threads. To avoid data loss and potential heap corrup‐
tion, serialize mutations to the itemStrings array with a statically-
allocated semaphore:
let itemStringsLock = DispatchSemaphore(value: 1)

60 | Chapter 5: Writing a To-Do List with Kitura


Define the second handler:
func handleAddStringItem(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) {
// If there is a body that holds JSON, get it.
guard case let .json(jsonBody)? = request.body
else {
response.status(.badRequest)
callNextHandler()
return
}
let item = jsonBody["item"].stringValue

itemStringsLock.wait()
itemStrings.append(item)
itemStringsLock.signal()

response.send("Added '\(item)'\n")
callNextHandler()
}
After creating the router, register each handler:
router.get ("/v1/string/item", handler: handleGetStringItems)
router.post("/v1/string/item", handler: handleAddStringItem)
If you compile and run the project now, you will have a working to-
do list! The server will run and accept incoming connections, so test
if the server is working properly by using cURL.
$ curl -H "Content-Type: application/json" \
-X POST \
-d '{"item":"Reticulate Splines"}' \
localhost:8090/v1/string/item
Added 'Reticulate Splines'

$ curl localhost:8090/v1/string/item
["Reticulate Splines"]

Creating a Web Service | 61


Deploying Your Application to Bluemix
There are many options for deploying a Kitura web application. If
you use a Docker-based platform, there is a Swift Dockerfile and
image at DockerHub. If you use a CloudFoundry environment, you
can use a Swift buildpack for deploying your application. Swift
applications are compiled to native binary, which means that the
binary must match the platform that will be running the application.
This ahead-of-time compilation might be different from what you
are used to if you use NodeJS, where the code is just-in-time com‐
piled, or Java, where the compiled byte-code runs in a virtual envi‐
ronment.
The IBM Bluemix cloud application platform supports Swift as a
first-class language along with JavaScript (NodeJS) and Java (Lib‐
erty). If you choose to deploy your application with IBM Bluemix,
you may consider using the IBM Cloud Tools for Swift. The tool can
be used to generate new starter applications, such as a TodoList ser‐
vice or an image-sharing service. It can help you deploy any Swift
application to Bluemix. You can monitor a deployed application as
shown in Figure 5-1. It will also help you configure your iOS appli‐
cation to direct network requests to the URL of the deployed web
service once it’s on Bluemix.

62 | Chapter 5: Writing a To-Do List with Kitura


Figure 5-1. IBM Cloud Tools for Swift helps users create new Kitura
applications or start with sample applications and deploy them to
Bluemix

If you are not on a Mac, or wish to use the command line, you must
first create some files to prepare your project for deployment.
Because Bluemix is a CloudFoundry-based hosting platform, it uses
buildpacks for deploying your code. These buildpacks are scripts
that are used to build your project in the cloud. When you push
your application to Bluemix, your code is bundled and uploaded to
a build server. The code is compiled based on the Swift buildpack.
Next, Bluemix uploads copies of the binary onto the number of vir‐
tual machine instances that you provisioned. When the application
is launched, and a load balancer distributes network traffic to all of
your instances running your server.
To describe your deployment, your application must have a
manifest.yml in the root path. This manifest file declares the appli‐
cation’s memory requirement, the number of instances, hostname,
and the address to the buildpack.

Deploying Your Application to Bluemix | 63


applications:
- name: todolist
memory: 256M
instances: 2
random-route: true
buildpack: https://github.com/IBM-Swift/swift-buildpack.git

Next, you must include a Procfile. It specifies the executable to run


once the instance has been provisioned and ready for running.
Assuming the executable name is “Server,” the Procfile would be:
web: Server
When the application runs on Bluemix, it will be automatically
assigned a port that it must listen on. This port number can be
retrieved with the Swift-cfenv library and used in a new HTTP
server as follows:
import CloudFoundryEnv
do {
let appEnv = try CloudFoundryEnv.getAppEnv()
let port: Int = appEnv.port
Kitura.addHTTPServer(onPort: port, with: router)
} catch CloudFoundryEnvError.InvalidValue {
print("Oops, something went wrong... Server did not start!")
}
You might consider adding the preceding code to all of the applica‐
tions you intend to deploy to Bluemix, because when the application
is running on Bluemix, the port will match what Bluemix gives your
application. However, if these cannot be found in the environment
variables, the application is assumed to be running in a development
environment, and therefore localhost:8090 will be used instead.
When you’re ready to deploy your application, you can install and
use the Bluemix CLI tools. Once installed, you can use the cf utility
to push the code to the server:
$ cf push
This step will take roughly four to five minutes. After your applica‐
tion has been deployed, you will see the assigned hostname. Navi‐
gate your browser or use cURL to test your app:
$ curl https://<application name>.bluemix.net/v1/string/item

64 | Chapter 5: Writing a To-Do List with Kitura


More Elaborate To-Do Items
You might want a to-do item to have more properties than just a
String, for example, ID, completion status, and due date. For now,
you’ll extend the code so that each item includes an ID and a title.

Start with a Dictionary


Before moving straight to using a structure for this purpose, you will
use a dictionary to store these fields. Such a Dictionary is denoted
by [String: Any], and an array of them by [[String: Any]].
Define the array of items and the semaphore:
var itemDictionaries = [[String: Any]]()
let itemDictionariesLock = DispatchSemaphore(value: 1)
This simple, untyped model corresponds so closely to JSON that
only small changes are needed to the handlers.
func handleGetItemDictionaries(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) throws {
response.send(json: JSON(itemDictionaries))
callNextHandler()
}
func handleAddItemDictionary(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) {
guard
case let .json(jsonBody)? = request.body,
let title = jsonBody["title"].string

else {
response.status(.badRequest)
callNextHandler()
return
}

itemDictionariesLock.wait()
itemDictionaries.append( [ "id": UUID().uuidString,
"title": title ] )
itemDictionariesLock.signal()

response.send("Added '\(title)'\n")

More Elaborate To-Do Items | 65


callNextHandler()
}
Register the handlers:
router.get ("/v1/dictionary/item",
handler: handleGetItemDictionaries)
router.post("/v1/dictionary/item",
handler: handleAddItemDictionary)
Test:
$ curl -H "Content-Type: application/json" \
-X POST \
-d '{"title":"Reticulate Splines"}'
Added 'Reticulate Splines'

$ curl localhost:8090/v1/dictionary/item
[
{
"id" : "2A6BF4C7-2773-4FC9-884C-957F205F940A",
"title" : "Reticulate Splines"
}
]

Move to a Structure
Now, represent an item with a structure. This change will allow the
item to be expressed in Swift more naturally, and has the benefit of
guaranteeing type safety on the properties:
struct Item {
let id: UUID
let title: String
}
var itemStructs = [Item]()
let itemStructsLock = DispatchSemaphore(value: 1)

The transition from a dictionary to a struct requires you to write


some code to convert Items to and from JSON. Create the conver‐
sion routines for Item:
enum ItemError: String, Error { case malformedJSON }
extension Item {
init ( json: JSON ) throws {
guard
let d = json.dictionary,
let title = d["title"]?.string
else {
throw ItemError.malformedJSON
}
id = UUID()

66 | Chapter 5: Writing a To-Do List with Kitura


title = title
}
var dictionary: [String: Any] {
return ["id": id.uuidString as Any,
"title": title as Any]
}
}
Write the new handlers:
func handleGetItemStructs(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) throws {
response.send( json: JSON(itemStructs.dictionary) )
callNextHandler()
}

func handleAddItemStruct(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) {
guard case let .json(jsonBody)? = request.body
else {
response.status(.badRequest)
callNextHandler()
return
}
do {
let item = try Item(json: jsonBody)

itemStructsLock.wait()
itemStructs.append(item)
itemStructsLock.signal()

response.send("Added '\(item)'\n")
callNextHandler()
}
catch {
response.status(.badRequest)
let err = error.localizedDescription
response.send(err)
callNextHandler()
}
}
Register the handlers:
router.get ("/v1/struct/item", handler: handleGetItemStructs)
router.post("/v1/struct/item", handler: handleAddItemStruct)

More Elaborate To-Do Items | 67


Test:
$ curl -H "Content-Type: application/json" \
-X POST \
-d '{"title":"Finish book!"}' localhost:8090/v1/struct/item
Added 'Item(id: 054879B8-B798-4462-AF0B-79B20F9617F4,
title: "Herd llamas")'

$ curl localhost:8090/v1/struct/item
[
{
"id" : "054879B8-B798-4462-AF0B-79B20F9617F4",
"title" : Finish book!
}
]

Adding Authentication
You may not want to allow a user to add a task to someone else’s list.
In order to prevent that from happening, you must first add a layer
of authentication. User identity will be assured by the client securely
sending a token that uniquely identifies the user. This token is
obtained by a third-party service the user already has an account on,
such as Facebook, Google, GitHub, or LinkedIn. This method is
convenient and more secure because your application’s database will
not need to store each user’s login name and password. The database
can just link each user’s login name to that user’s to do list items.
Kitura has a middleware library called Kitura Credentials that
makes it easy to add authentication to your application. There are
several plug-ins that currently support Facebook, Github, and Goo‐
gle OAuth token validation. In addition, there are plug-ins for
HTTP basic and digest authentication. The middleware intercepts
each RouterRequest, and then ensures that the token in the header
correctly authenticates the user. The RouterRequest will only pro‐
ceed to execute the handler if the authentication succeeds.
The example below shows how to use the Facebook plug-in. After
credentials middleware is created, the Facebook plug-in is registered
with the middleware. Next, the routes matching the item resource
are protected with the authentication layer. This is useful, since there
might be other routes that you might not want to protect; for
instance, the welcome page. The following sets up the authentica‐
tion:

68 | Chapter 5: Writing a To-Do List with Kitura


let credentials = Credentials()
let facebookCredentialsPlugin = CredentialsFacebookToken()
credentials.register(facebookCredentialsPlugin)
router.all("/v1/*/item", middleware: credentials)
The login flow works as follows: The client must first obtain a Face‐
book OAuth token using the Facebook login web service. If you are
developing an iOS application, this is done through the Facebook
SDK for iOS. If you are developing a web application, you will use
the Facebook SDK for JavaScript. When the user imports the Face‐
book iOS SDK, it will allow you to present a Facebook login panel to
the user requesting a username and password. If the user has already
signed into Facebook (and a cache exists with the token), the login
screen is bypassed. The username and password are sent to the
Facebook server through HTTPS and, assuming the username and
password checks out, an OAuth token is returned to the client. The
client then must include that token in the header of future requests
to add or get items. For Kitura Facebook Credentials to use this
token, the client must bundle the token in the access_token header
field, and “FacebookToken” in the X-token-type field for all HTTP
requests.
The user’s Facebook profile information can be read using the
userProfile attribute of the RouterRequest. The code can obtain
the user’s unique user ID from request.userProfile.id.
For brevity, we did not show how to relate a user ID to the to-do
items, but this would be an easy modification to make to the above
examples.

Setting up the Database


There are many different databases to choose from if you are build‐
ing a to-do list in Swift. You can see some examples by cloning
our example Kitura TodoList applications, which will show you how
to interface with some of the most popular databases. You will see,
for instance, MongoDB, PostgreSQL, Redis, and more. There are
many different database drivers for Swift in the Swift Package Cata‐
log. Some were written using C bindings to existing database drivers
written in C, so you may need to install some development headers
and libraries before you can use them. Others have been developed
from the ground up with pure Swift and therefore will not require
the installation of any additional libraries.

Setting up the Database | 69


In this section, we explain how to use CouchDB with your to-do list.
We chose this database since it will demonstrate how to make out‐
bound network calls from your server. CouchDB uses a simple
REST API that receives and accepts JSON payloads. CouchDB is
supported well on Bluemix as an offering called Cloudant.
Everything in CouchDB is a document. In order to query the data‐
base, you must write a special kind of document called a design
document that contains some logic that should be run in the data‐
base. When that logic is run, it prepares a “view” that is returned to
the user. The map function is run on every document in the data‐
base. Usually some condition is checked, and if it passes, a new
document is emitted containing the values of that document.
Another kind of logic is the reduce function. It is useful for collaps‐
ing the documents down to a single document. For instance, if you
wanted to get the count or the sum of many documents.
To create a design, open a new file named todolist_design.json,
and add the following to it:
{
"_id": "_design/tododesign",
"_views" : {
"all_todos" : {
"map" :
"function(doc) { emit(doc._id, [doc._id, doc.title]); }"
}
}
}
Since CouchDB uses a REST API, you can make new designs, add
documents, and query for documents all using basic HTTP calls.
Before showing how to write the Swift code for these network calls,
it is helpful to see what the cURL commands would be for each of
these operations. For instance, to create a new database called todo‐
list:
$ curl -X PUT http://127.0.0.1:5984/todolist

To upload the todolist_design.json document to the newly cre‐


ated database:
$ curl -X PUT http://127.0.0.1:5984/todolist/_design/tododesign \
--data-dinary @todolist_design.json
Then, in order to get back all the to-dos, you could make a request
to:

70 | Chapter 5: Writing a To-Do List with Kitura


$ curl http://127.0.0.1:5984/todolist/_design/tododesign \
/_views/all_todos
To add a new item to the database, you make the call to the database
with a UUID for the document ID:
$ curl http://127.0.0.1:5984/todolist/<UUID goes here> \
-d '{ "title": "Reticulate Splines" }'
Now we can show how to make outbound network connections
with Swift.

Connecting to the Database


Because outbound network requests introduce a fair amount of
complexity to your application, we recommend using a promise
library. You can add the promise library created in Chapter 3 by
adding the following dependency to Package.swift:
.Package(url: "https://github.com/davidungar/miniPromiseKit",
majorVersion: 4, minor: 1)

Now, you can extend URLSession to use a promise instead of using a


callback:
extension URLSession {
func dataTaskPromise(with url: URL) -> Promise<Data> {
return Promise { fulfill, reject in
let dataTask =
URLSession(configuration: .default).dataTask(
with: url) {
data, response, error in
if let d = data { fulfill(d) }
else { reject(error!) }
}
dataTask.resume()
}
}
}
By default, the miniPromiseKit and PromiseKit libraries will use the
main queue for dispatching the then, catch, and always blocks, so it
is important to create a new concurrent queue and use it as the con‐
text for executing these blocks:
let queue = DispatchQueue(label: "com.todolist.controller",
qos: .userInitiated,
attributes: .concurrent)

Connecting to the Database | 71


The body of the firstly call returns a promise for the array of
items. In the examples below, url is the URL for the all_todos
view. Once the data is returned, we convert it to an array of items. In
the interest of space, we have omitted the dataToItems method,
which does the JSON parsing. Refer to it in the example code. This
method can throw an error if the data could not be parsed properly,
and that error can percolate through the promise chain.
func getAllItems() -> Promise<[Item]> {
return firstly {
URLSession().dataTaskPromise(with: url)
}
.then(on: queue) { dataResult in
return try dataToItems(data: dataResult)
}
}

You can add a task by sending a URLRequest with a PUT method.


Assume for the sake of this example that URL is the path to the data‐
base and the UUID of the item you want to insert—for example,
"127.0.0.1:5984/todolist/E71233AD-01D2-430B-8708-
F7E2496AEFB2":
func addItem(item: Item) -> Promise<Item> {
return Promise { fulfill, reject in
let session = URLSession(configuration: .default)
var request = URLRequest(url: url)
request.httpMethod = "PUT"
request.httpBody = try JSONSerialization.data(
withJSONObject: item.dictionary,
options: [])
let dataTask = session.dataTask(with: request) {
data, response, error in
if let error = error { reject(error) }
fulfill(item)
}
dataTask.resume()
}
}
Next, you can write the handlers. Promises help to improve the flow
when responding to these error cases. They help you write your
error handling logic in a linear way. The errors “short circuit” to the
end of the handler where the code can serialize that error to the cli‐
ent.
Getting back a list of items takes the following steps: first, all of the
items are retrieved from the database as a promise. Once that

72 | Chapter 5: Writing a To-Do List with Kitura


promise is fulfilled, the items can be sent back to the client. But if
there was an error with servicing the request, the error can be serial‐
ized back to the user. In all cases, we want the next handler in the
chain to be invoked:
func handleGetCouchDBItems(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) throws {
firstly {
getAllItems()
}
.then(on: queue) {
response.send(json: JSON(items.dictionary))
}
.catch(on: queue) {
response.status(.badRequest)
response.send(error.localizedDescription)
}
.always(on: queue) {
callNextHandler()
}
}
The add item handler also benefits from using promises. A new item
is parsed from the body, and the item is sent to the database to be
persisted. If there was an error either with the parsing or with the
network connectivity, that error is handled and sent to the client:
func handleAddCouchDBItem(
request: RouterRequest,
response: RouterResponse,
callNextHandler: @escaping () -> Void
) {
firstly { () -> Promise<Item> in
guard case let .json(jsonBody)? = request.body
else {
throw ItemError.malformedJSON
}
let item = try Item(json: jsonBody)
return addItem(item: item)
}
.then(on: queue) { item in
response.send("Added \(item.title)")
}
.catch(on: queue) { error in
response.status(.badRequest)
response.send(error.localizedDescription)
}

Connecting to the Database | 73


.always(on: queue) {
callNextHandler()
}
}
Register the handlers:
router.get ( "/v1/couch/item", handler: handleGetCouchDBItems )
router.post( "/v1/couch/item", handler: handleAddCouchDBItem )
You can now test your application using the same aforementioned
cURL statements.
$ curl -H "Content-Type: application/json" \
-X POST \
-d '{"title":"Finish book!"}' localhost:8090/v1/couch/item
Added 'Item(id: 054879B8-B798-4462-AF0B-79B20F9617F4,
title: "Finish book!")'

$ curl localhost:8090/v1/couch/item
[
{
"id" : "054879B8-B798-4462-AF0B-79B20F9617F4",
"title" : Finish book!
}
]
You have learned how to create a fully working to-do list application
in Swift that uses a database and can be deployed to a server. Scratch
that off your bucket list.

74 | Chapter 5: Writing a To-Do List with Kitura


Conclusions

Swift is a powerful programming language that supports both


object-oriented and functional programming. Its static type check‐
ing, type inferencing, optional types, value types, and closures allow
you to write code that is clear, concise, and robust. It catches more
bugs at compile-time—such as accesses to nil values—than many
other popular languages. Swift’s facilities ease the implementation of
constructs such as promises (a.k.a., futures) that greatly simplify
asynchronous programming. Its ahead-of-time compilation pro‐
vides predictable performance and plays nicely with libraries written
in other languages such as C. Swift is well suited for real applica‐
tions.
If you choose to implement your server-side code in Swift, you’ll
find the necessary tools to help you. The Swift Package Manager
aids in importing other libraries into your project and building your
application correctly. The Kitura library helps get you started
quickly building web apps by handling incoming web requests and
routing them to your application code. Bluemix and the IBM Cloud
Tools for Swift provide for easy deployment.
We hope you consider writing the entire stack of your next applica‐
tion in Swift, from client to sensor to server.

Conclusions | 75
About the Authors
David Ungar holds a Ph.D. in Computer Science from U.C. Berke‐
ley, taught at Stanford, and enjoys a research position at IBM in the
Ubiquitous Platforms group in Cloud and Mobile Enterprise
Research. He loves programming and has enjoyed APL, C, C++,
Smalltalk, Self (which he codesigned), Java, JavaScript, Objective-C,
and Swift. His interests have included UNIX system programming,
microprocessors, object-oriented language design and implementa‐
tion, cartoon animation techniques for user interfaces, reflection
APIs, IDEs, emergence for massive parallelism, and contextual pro‐
gramming. Now he builds iOS and macOS applications.
Four of his papers have been honored by the Association for Com‐
puting Machinery for lasting impact. In 2009, he was awarded the
Dahl-Nygaard prize for outstanding career contributions to object-
oriented language technology by the Association Internationale
pour les Technologies Objets.
He blogs at http://blog.davidungar.net and tweets at @senderPath.
Robert F. Dickerson is a software engineer in the Swift@IBM engi‐
neering group. Having written many end-to-end applications in
Java, JavaScript, and Swift (on iOS), he loves being able to write his
server-side code in Swift! He was one of the original developers who
made Kitura, a web-service middleware framework for Swift. He
leads a team that builds applications and libraries that use Kitura.
He is active in the open-source Swift community, where he has
given talks about server-side Swift at AltConf 2016, Try! Swift NYC
2016, and the Server-side Swift Year-Long Conference 2016. Before
joining IBM as a developer, he was on the faculty in the computer
science departments at the College of William and Mary and then at
the University of Texas at Austin. He holds a Ph.D. in Computer Sci‐
ence from the University of Virginia.
He blogs at https://developer.ibm.com/swift/ and tweets at @rfdicker‐
son.

Das könnte Ihnen auch gefallen