Extending Swift Value(s) to the Server pdf pdf

  Extending Swift Value(s) to the Server David Ungar and Robert Dickerson

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 ( . For more information, contact our corporate/institutional sales department: 800-998-9938 or

  corporate@oreilly.com.

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

Revision History for the First Edition

  2017-01-25: 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 subject 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-97196-3 [LSI]

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 concepts 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 (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 currently using.

  You can develop and debug in a consistent environment. Integrated development environments (IDEs) offer a tremendous amount of functionality such as text editing, static analysis, code completion, debugging, profiling, and even source-control integration. 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 or 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 outside 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 , 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!

  

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 implementations and blocks of code in this edition contain

inconsistencies in color and font. If the inconsistencies confuse you, please consult the repositories i .

  

Table P-1. Where to find code examples

Repository name Referenced in Created in

  

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 Gartner, 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 Bailey, Hubertus Franke, David Grove, David Jones, and Shmuel Kallner — 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 bringing 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 tremendous amount of work. The rapid acceptance of Swift by developers 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.

Chapter 1. A Swift Introduction Swift supports several different programming paradigms. This chapter

  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 values. 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.

  

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:

  aHost = "someMachine.com" let

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.

  aPath = "something" var 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:

  

combine ( host : String , withPath path : String ) -> String {

func

  "/" + host + path return

  }

  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.

Simple Enumerations

  In Swift, as in other languages, an enumeration represents some fixed, closed set of alternatives that might be assigned to some variable. Unlike enumerations in other languages, Swift’s come in three flavors, each suited for a particular use. The flavor of an enumeration 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 i

  example:

  { valid , invalid } enum Validity case

  The second flavor of enumeration provides for each case to be associated 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 example:

  : Int { enum StatusCode ok = 200 case created = 201 case// more cases go here badRequest = 400 case unauthorized = 401 case

  }

  The value of this enumeration can be accessed via the rawValue attribute:

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

  }

Tuples

  func lookup ( user : String )

  When an identifier is declared with let, every element of the tuple behaves as if it were declared with a let:

  "serialNumber:" , userInfo . serialNumber )

  let userInfo = lookup ( user : "Washington" ) print ( "name:" , userInfo . name ,

  and then accessed by name:

  ( n , sn ) }

  // compute n and sn return

  Members can be named in the tuple type declaration:

  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:

  let ( name , serialNumber ) = lookup ( user : "Adams" ) print ( "name:" , name , "serialNumber:" , serialNumber )

  or can be unpacked simultaneously:

  let userInfo = lookup ( user : "Washington" ) print ( "name:" , userInfo . , "serialNumber:" , userInfo . 1 )

  Tuple members can be accessed by index:

  ( n , sn ) }

  // compute n and sn return

  func lookup ( user : String ) -> ( String , Int ) {

  • > ( name : String , serialNumber : Int ) {
second = lookup ( user : "Adams" ) let second . name = "Gomez Adams" // ILLEGAL: u is a let anotherSecond = lookup ( user : "Adams" ) var 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:

  first = lookup ( user : "Washington" ) var anotherFirst = first var 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 program 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

  value type is a reference type. The only reference types are instances-of-

classes and closures. Consequently, 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:

  { roundPeg , squarePeg } enum PegShape case

  { roundHole , squareHole , triangularHole } enum HoleShape case howDoes ( peg : PegShape , fitInto hole : HoleShape ) func _

  • > String { ( peg , hole ) { // switches on a tuple

  switch (. roundPeg , . roundHole ): case

  "fits any orientation" return (. squarePeg , . squareHole ): case

  "fits four ways" return

  : default

  "does not fit" return

  } }

  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:

  precedencegroup { LeftFunctionalApply

  : associativity left higherThan :

  AssignmentPrecedence lowerThan :

  TernaryPrecedence }

  |> : infix operator LeftFunctionalApply

  then provide a (generic) implementation:

  |> < In , Out > ( lhs : In , rhs : ( In ) -> Out ) func throws

  • > Out {

  rethrows rhs ( lhs ) return try

  } 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 closure is a first-class entity, a reference type which can be assigned to constants and variables, passed as arguments, and returned as results. Functions and methods are just special cases of closures with a slightly different syntax for their definition. Closures support functional programming, which can be particularly helpful in dealing with asynchr

  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 clarify 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 (previously implicit) types: 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 programming, including classes, instances, constructors (called “initializers”), 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 requirement prevents some errors. Experienced Swift programmers tend to reserve objects for things requiring shared access to a mutable state. (See

Protocols Define Interfaces

  "GET" } } class Post_HTTP_Request

  When declaring a variable to hold a request, instead of using the type Abstract_HTTP_Request , use the type HTTP_Request_Protocol:

  ( url : url ) } }

  . data = data super . init

  ( url : URL , data : String ) { self

  "POST" } var data : String init

  { var requestString : String { return

  : Abstract_HTTP_Request , HTTP_Request_Protocol

  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 operations expected of any HTTP_Request might be that it can supply a URL and a requestString:

  protocol HTTP_Request_Protocol { var url : URL {

get

  : Abstract_HTTP_Request , HTTP_Request_Protocol

  

. url = url }

} class Get_HTTP_Request

  ( url : URL ) { self

  class Abstract_HTTP_Request { let url : URL // A constant instance variable init

  In other languages, Abstract_HTTP_Request might need an abstract requestString . But in Swift, the protocol serves that purpose:

  } }

  } var requestString : String {

get

  { var requestString : String { return

  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 representation.

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 TemperatureResponse 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:

  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 parameter, 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:

  someResponse : ResponseProtocol // ILLEGAL var

  But you can write a function that will work on any type that conforms to ResponseProtocol :

  handleResponse < SomeResponseType : ResponseProtocol > func ( response : SomeResponseType ) { … }

  Because a generic protocol cannot be used as a type, it is often helpful to split up a generic protocol into generic and nongeneric protocols. A full discussion of these generic protocols is beyond the scope of this book. Generic protocols support generic functions, structures, 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 extension, and can be used with classes, structures, enumerations, and 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 computation 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 squared : Int { } var 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:

  {

  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 { … } }

  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 single file, or together with similar functionality for different types.

  

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.

   debugging large programs that run asynchronously and concurrently is hardest. It makes sense to place the burden of checking certain 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 versus 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 dictionary with String keys and values:

  headerFields : [ String : String ] = … let

  Swift uses subscript notation to look up the value of a given key in a dictionary:

  contentType = headerFields [ "Content-Type" ] let

  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:

  contentType . hasPrefix ( "text" ) // ILLEGAL if

  There are many convenient ways to perform this check. For example, 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. ct = headerFields [ "Content-Type" ] { if let 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:

  contentType = headerFields [ "Content-Type" ] ?? "none" let

  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 surprised 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.

Structures Isolate Mutation

  A Swift structure (struct) is like an instance of a class: it groups values 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 variable 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.

  A structure provides two guarantees about its mutability:

  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

  

  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" )

  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 , 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. 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:

  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:

  request = TemperatureRequestStruct ( city : "Paris" ) var

  Now, the Swift compiler catches the error before the program can even run!

  sendToDB ( func 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?

  mutableRequest = request var 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 Value types are always

   copied.

  

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 mutating. This requirement highlights the places where side effects could occur:

  

{

struct TemperatureRequestStruct

  … startTime : Date ? = var nil clearStartTime () { startTime = } mutating func 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: request1 = TemperatureRequestStruct ( city : "Paris" ) let

request2 = request1 // makes a copy because is a struct

var 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 reference, 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 structures, 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 semantics. 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 copying.

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 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 cityAndState property to all objects that conform to the Request protocol.

  protocol Request { var city : String { get

  ( city : String , state : String ) { self

  

  What if you need to express a multilevel hierarchy? Protocols can inherit from other protocols, but it’s not quite the same as subclassing — for example, there is no super. Structures with protocols and protocol extensions provide one way to replace

  

  A protocol extension cannot add new requirements to a protocol, 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

  . state = state } }

  . city = city self

  : Request { let city : String let state : String var startTime : Date ? = nil init

  } var state : String { get

  . state = state } } struct OzoneRequest

  . city = city self

  ( city : String , state : String ) { self

  : Request { let city : String let state : String var startTime : Date ? = nil init

  } struct TemperatureRequest

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

  } } extension Request

  Structures are like instances of classes that are always copied when being passed around. Use structures wherever you don’t need separate references to 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 concept. For instance, you can choose between a class and a structure. Moreover, Swift includes many features from the functional programming 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 i Swift enumeration represents a value taken from a fixed set — for example, one of a fixed set of HTTP requests:

  { enum HTTP_Request_Kind get , post // other request types omitted for brevity case

  }

  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 subclasses for each kind of request. But since you don’t want any asynchronous 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

  == ( a : , b : ) -> Bool static func Self Self

  } : HTTP_Request {...} struct Get

  (. post ( u1 , h1 , d1 ), . post ( u2 , h2 , d2 )) where u1 == u2 && h1 == h2 && d1 == d2 :

  . post ( _

  (. get ( u1 , h1 ), . get ( u2 , h2 )) where u1 == u2 && h1 == h2 : return true case let

  switch ( lhs , rhs ) { case let

  == ( lhs : HTTP_Request , rhs : HTTP_Request )

  . resume () } } static func

  , let data ): // also put, patch s . uploadTask ( with : r , from : data , completionHandler : completionHandler )

  , _

  . resume () case

  let someRequest : HTTP_Request = ... // ILLEGAL let requests : [ HTTP_Request ] = ... // also ILLEGAL

  . get : // also delete s . dataTask ( with : r , completionHandler : completionHandler )

  { case

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

  // also delete, put, & patch func send ( completionHandler :

  enum HTTP_Request { case get ( destination : URL , headerFields : [ String : String ] ) case post ( destination : URL , headerFields : [ String : String ], data : Data )

  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.

  • > Bool {

  // 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:

  requests : [ HTTP_Request ] = [ let . get ( destination : url1 , headerFields : [:]),

. post ( destination : url2 , headerFields : [:], data : someData )

]

  and you can test different kinds of requests for equality with each other:

  aRequest == anotherRequest if

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 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