Book Image

Swift Essentials - Second Edition

By : Alex Blewitt
Book Image

Swift Essentials - Second Edition

By: Alex Blewitt

Overview of this book

Swift was considered one of the biggest innovations last year, and certainly with Swift 2 announced at WWDC in 2015, this segment of the developer space will continue to be hot and dominating. This is a fast-paced guide to provide an overview of Swift programming and then walks you through in detail how to write iOS applications. Progress through chapters on custom views, networking, parsing and build a complete application as a Git repository, all by using Swift as the core language
Table of Contents (17 chapters)
Swift Essentials Second Edition
Credits
About the Author
Acknowledgments
About the Reviewer
www.PacktPub.com
Preface
Index

Getting started with Swift


Swift provides a runtime interpreter that executes statements and expressions. Swift is open source, and precompiled binaries can be downloaded from https://swift.org/download/ for both OS X and Linux platforms. Ports are in progress to other platforms and operating systems but are not supported by the Swift development team.

The Swift interpreter is called swift and on OS X can be launched using the xcrun command in a Terminal.app shell:

$ xcrun swift
Welcome to Swift version 2.2!  Type :help for assistance.
>

The xcrun command allows a toolchain command to be executed; in this case, it finds /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/swift. The swift command sits alongside other compilation tools, such as clang and ld, and permits multiple versions of the commands and libraries on the same machine without conflicting.

On Linux, the swift binary can be executed provided that it and the dependent libraries are in a suitable location.

The Swift prompt displays > for new statements and . for a continuation. Statements and expressions that are typed into the interpreter are evaluated and displayed. Anonymous values are given references so that they can be used subsequently:

> "Hello World"
$R0: String = "Hello World"
> 3 + 4
$R1: Int = 7
> $R0
$R2: String = "Hello World"
> $R1
$R3: Int = 7

Numeric literals

Numeric types in Swift can represent both signed and unsigned integral values with sizes of 8, 16, 32, or 64 bits, as well as signed 32 or 64 bit floating point values. Numbers can include underscores to provide better readability; so, 68_040 is the same as 68040:

> 3.141
$R0: Double = 3.141
> 299_792_458
$R1: Int = 299792458
> -1
$R2: Int = -1
> 1_800_123456
$R3: Int = 1800123456

Numbers can also be written in binary, octal, or hexadecimal using prefixes 0b, 0o (zero and the letter "o") or 0x. Please note that Swift does not inherit C's use of a leading zero (0) to represent an octal value, unlike Java and JavaScript which do. Examples include:

> 0b1010011
$R0: Int = 83
> 0o123
$R1: Int = 83
> 0123
$R2: Int = 123
> 0x7b
$R3: Int = 123

Floating point literals

There are three floating point types that are available in Swift which use the IEEE754 floating point standard. The Double type represents 64 bits worth of data, while Float stores 32 bits of data. In addition, Float80 is a specialized type that stores 80 bits worth of data (Float32 and Float64 are available as aliases for Float and Double, respectively, although they are not commonly used in Swift programs).

Some CPUs internally use 80 bit precision to perform math operations, and the Float80 type allows this accuracy to be used in Swift. Not all architectures support Float80 natively, so this should be used sparingly.

By default, floating point values in Swift use the Double type. As floating point representation cannot represent some numbers exactly, some values will be displayed with a rounding error; for example:

> 3.141
$R0: Double = 3.141
> Float(3.141)
$R1: Float = 3.1400003

Floating point values can be specified in decimal or hexadecimal. Decimal floating point uses e as the exponent for base 10, whereas hexadecimal floating point uses p as the exponent for base 2. A value of AeB has the value A*10^B and a value of 0xApB has the value A*2^B. For example:

> 299.792458e6
$R0: Double = 299792458
> 299.792_458_e6
$R1: Double = 299792458
> 0x1p8
$R2: Double = 256
> 0x1p10
$R3: Double = 1024
> 0x4p10
$R4: Double = 4096
> 1e-1
$R5: Double = 0.10000000000000001
> 1e-2
$R6: Double = 0.01> 0x1p-1
$R7: Double = 0.5
> 0x1p-2
$R8: Double = 0.25
> 0xAp-1
$R9: Double = 5

String literals

Strings can contain escaped characters, Unicode characters, and interpolated expressions. Escaped characters start with a slash (\) and can be one of the following:

  • \\: This is a literal slash \

  • \0: This is the null character

  • \': This is a literal single quote '

  • \": This is a literal double quote "

  • \t: This is a tab

  • \n: This is a line feed

  • \r: This is a carriage return

  • \u{NNN}: This is a Unicode character, such as the Euro symbol \u{20AC}, or a smiley \u{1F600}

An interpolated string has an embedded expression, which is evaluated, converted into a String, and inserted into the result:

> "3+4 is \(3+4)"
$R0: String = "3+4 is 7"
> 3+4
$R1: Int = 7
> "7 x 2 is \($R1 * 2)"
$R2: String = "7 x 2 is 14"

Variables and constants

Swift distinguishes between variables (which can be modified) and constants (which cannot be changed after assignment). Identifiers start with an underscore or alphabetic character followed by an underscore or alphanumeric character. In addition, other Unicode character points (such as emoji) can be used although box lines and arrows are not allowed; consult the Swift language guide for the full set of allowable Unicode characters. Generally, Unicode private use areas are not allowed, and identifiers cannot start with a combining character (such as an accent).

Variables are defined with the var keyword, and constants are defined with the let keyword. If the type is not specified, it is automatically inferred:

> let pi = 3.141
pi: Double = 3.141
> pi = 3

error: cannot assign to value: 'pi' is a 'let' constant
note: change 'let' to 'var' to make it mutable
> var i = 0
i: Int = 0
> ++i
$R0: Int = 1

Types can be explicitly specified. For example, to store a 32 bit floating point value, the variable can be explicitly defined as a Float:

> let e:Float = 2.718
e: Float = 2.71799994

Similarly, to store a value as an unsigned 8 bit integer, explicitly declare the type as UInt8:

> let ff:UInt8 = 255
ff: UInt8 = 255

A number can be converted to a different type using the type initializer or a literal that is assigned to a variable of a different type, provided that it does not underflow or overflow:

> let ooff = UInt16(ff)
ooff: UInt16 = 255
> Int8(255)
error: integer overflows when converted from 'Int' to 'Int8'
Int8(255)
^
> UInt8(Int8(-1))
error: negative integer cannot be converted to unsigned type 'UInt8'
UInt8(Int8(-1))
^

Collection types

Swift has three collection types: Array, Dictionary, and Set. They are strongly typed and generic, which ensures that the values of types that are assigned are compatible with the element type. Collections that are defined with var are mutable; collections defined with let are immutable.

The literal syntax for arrays uses [] to store a comma-separated list:

> var shopping = [ "Milk", "Eggs", "Coffee", ]
shopping: [String] = 3 values {
  [0] = "Milk"
  [1] = "Eggs"
  [2] = "Coffee"
}

Literal dictionaries are defined with a comma-separated [key:value] format for entries:

> var costs = [ "Milk":1, "Eggs":2, "Coffee":3, ]
costs: [String : Int] = {
  [0] = { key = "Coffee" value = 3 }
  [1] = { key = "Milk"   value = 1 }
  [2] = { key = "Eggs"   value = 2 }
}

Tip

For readability, array and dictionary literals can have a trailing comma. This allows initialization to be split over multiple lines, and if the last element ends with a trailing comma, adding new items does not result in an SCM diff to the previous line.

Arrays and dictionaries can be indexed using subscript operators that are reassigned and added to:

> shopping[0]
$R0: String = "Milk"
> costs["Milk"]
$R1: Int? = 1
> shopping.count
$R2: Int = 3
> shopping += ["Tea"]
> shopping.count
$R3: Int = 4
> costs.count
$R4: Int = 3
> costs["Tea"] = "String"
error: cannot assign a value of type 'String' to a value of type 'Int?'
> costs["Tea"] = 4
> costs.count
$R5: Int = 4

Sets are similar to dictionaries; the keys are unordered and can be looked up efficiently. However, unlike dictionaries, keys don't have an associated value. As a result, they don't have array subscripts, but they do have the insert, remove, and contains methods. They also have efficient set intersection methods, such as union and intersect. They can be created from an array literal if the type is defined or using the set initializer directly:

> var shoppingSet: Set = [ "Milk", "Eggs", "Coffee", ]
> // same as: shoppingSet = Set( [ "Milk", "Eggs", "Coffee", ] )
> shoppingSet.contains("Milk")
$R6: Bool = true
> shoppingSet.contains("Tea")
$R7: Bool = false
> shoppingSet.remove("Coffee")
$R8: String? = "Coffee"
> shoppingSet.remove("Tea")
$R9: String? = nil
> shoppingSet.insert("Tea")
> shoppingSet.contains("Tea")
$R10: Bool = true

Tip

When creating sets, use the explicit Set constructor as otherwise the type will be inferred to be an Array, which will have a different performance profile.

Optional types

In the previous example, the return type of costs["Milk"] is Int? and not Int. This is an optional type; there may be an Int value or it may be empty. For a dictionary containing elements of type T, subscripting the dictionary will have an Optional<T> type, which can be abbreviated as T? If the value doesn't exist in the dictionary, then the returned value will be nil. Other object-oriented languages, such as Objective-C, C++, Java, and C#, have optional types by default; any object value (or pointer) can be null. By representing optionality in the type system, Swift can determine whether a value really has to exist or might be nil:

> var cannotBeNil: Int = 1
cannotBeNil: Int = 1
> cannotBeNil = nil
error: cannot assign a value of type 'nil' to a value of type 'Int'
cannotBeNil = nil
              ^
> var canBeNil: Int? = 1
canBeNil: Int? = 1
> canBeNil = nil
$R0: Int? = nil

Optional types can be explicitly created using the Optional constructor. Given a value x of type X, an optional X? value can be created using Optional(x). The value can be tested against nil to find out whether it contains a value and then unwrapped with opt!, for example:

> var opt = Optional(1)
opt: Int? = 1
> opt == nil
$R1: Bool = false
> opt!
$R2: Int = 1

If a nil value is unwrapped, an error occurs:

> opt = nil
> opt!
fatal error: unexpectedly found nil while unwrapping an Optional value
Execution interrupted. Enter Swift code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

Particularly when working with Objective-C based APIs, it is common for values to be declared as an optional although they are always expected to return a value. It is possible to declare such variables as implicitly unwrapped optionals; these variables behave as optional values (they may contain nil), but when the value is accessed, they are automatically unwrapped on demand:

> var implicitlyUnwrappedOptional:Int! = 1
implicitlyUnwrappedOptional: Int! = 1
> implicitlyUnwrappedOptional + 2
3
> implicitlyUnwrappedOptional = nil
> implicitlyUnwrappedOptional + 2
fatal error: unexpectedly found nil while unwrapping an Optional value

Tip

In general, implicitly unwrapped optionals should be avoided as they are likely to lead to errors. They are mainly useful for interaction with existing Objective-C APIs when the value is known to have an instance.

Nil coalescing operator

Swift has a nil coalescing operator, which is similar to Groovy's ?: operator or C#'s ?? operator. This provides a means to specify a default value if an expression is nil:

> 1 ?? 2
$R0: Int = 1
> nil ?? 2
$R1: Int = 2

The nil coalescing operator can also be used to unwrap an optional value. If the optional value is present, it is unwrapped and returned; if it is missing, then the right-hand side of the expression is returned. Similar to the || shortcut, and the && operators, the right-hand side is not evaluated unless necessary:

> costs["Tea"] ?? 0
$R2: Int = 4
> costs["Sugar"] ?? 0
$R3: Int = 0