The previous chapter looked at the basic building blocks of programming in Swift. In this chapter, we’re going to look at some of the more advanced features of Swift, such as memory management, working with files and external data, and error handling. We’ll also touch on interoperating with Apple’s older programming language, Objective-C.
Swift’s features allow it to be used as an object-oriented programming language. This means that you do the majority of your work by creating and manipulating objects—chunks of data and code that represent a thing that can perform some useful work or store some useful data.
In Swift, as with Objective-C, Java, and C++ (and many other languages), you define templates for your objects using classes. Classes in Swift look like this:
class
Vehicle
{
}
Classes contain both properties and methods. Properties are variables that are part of a class, and methods are functions that are part of a class.
The Vehicle
class in the following example contains two properties: an optional String
called color
, and an Int
called maxSpeed
. Property declarations look the same as variable declarations do in other code:
class
Vehicle
{
var
color
:
String
?
var
maxSpeed
=
80
}
Methods in a class look the same as functions anywhere else. Code that’s in a method can access the properties of a class by using the self
keyword, which refers to the object that’s currently running the code:
class
Vehicle
{
var
color
:
String
?
var
maxSpeed
=
80
func
description
()
->
String
{
return
"A \(self.color) vehicle"
}
func
travel
()
{
(
"Traveling at \(maxSpeed) kph"
)
}
}
If you are wondering what the \()
inside the string is, this is string interpolation, which lets you make strings from myriad types. We talk more about strings in “Working with Strings”.
You can omit the self
keyword if it’s obvious that the property is part of the current object. In the previous example, description
uses the self
keyword, while travel
doesn’t.
When you’ve defined a class, you can create instances of the class (called an object) to work with. Instances have their own copies of the class’s properties and functions.
For example, to define an instance of the Vehicle
class, you define a variable and call the class’s initializer. Once that’s done, you can work with the class’s functions and properties:
var
redVehicle
=
Vehicle
()
redVehicle
.
color
=
"Red"
redVehicle
.
maxSpeed
=
90
redVehicle
.
travel
()
// prints "Traveling at 90 kph"
redVehicle
.
description
()
// = "A Red vehicle"
When you create an object in Swift, a special method known as its initializer is called. The initializer is the method that you use to set up the initial state of an object and is always named init
.
Swift has two types of initializers, convenience initializers and designated initializers. A designated initializer sets up everything you need to use that object, often using default settings where necessary. A convenience initializer, as its name implies, makes setting up the instance more convenient by allowing for more information to be included in the initialization. A convenience initializer must call the designated initializer as part of its setup.
In addition to initializers, you can run code when removing an object, in a method called a deinitializer, named deinit
. This runs when the retain count of an object drops to zero (see “Memory Management”) and is called right before the object is removed from memory. This is your object’s final opportunity to do any necessary cleanup before it goes away forever:
class
InitAndDeinitExample
{
// Designated (i.e., main) initializer
init
()
{
(
"I've been created!"
)
}
// Convenience initializer, required to call the
// designated initializer (above)
convenience
init
(
text
:
String
)
{
self
.
init
()
// this is mandatory
(
"I was called with the convenience initializer!"
)
}
// Deinitializer
deinit
{
(
"I'm going away!"
)
}
}
var
example
:
InitAndDeinitExample
?
// using the designated initializer
example
=
InitAndDeinitExample
()
// prints "I've been created!"
example
=
nil
// prints "I'm going away"
// using the convenience initializer
example
=
InitAndDeinitExample
(
text
:
"Hello"
)
// prints "I've been created!" and then
// "I was called with the convenience initializer"
An initializer can also return nil
. This can be useful when your initializer isn’t able to usefully construct an object. For example, the NSURL
class has an initializer that takes a string and converts it into a URL; if the string isn’t a valid URL, the initializer returns nil
.
To create an initializer that can return nil
—also known as a failable initializer—put a question mark after the init
keyword, and return nil
if the initializer decides that it can’t successfully construct the object:
// This is a convenience initializer that can sometimes fail, returning nil
// Note the ? after the word 'init'
convenience
init
?
(
value
:
Int
)
{
self
.
init
()
if
value
>
5
{
// We can't initialize this object; return nil to indicate failure
return
nil
}
}
When you use a failable initializer, it will always return an optional:
var
failableExample
=
InitAndDeinitExample
(
value
:
6
)
// = nil
Classes store their data in properties. Properties, as previously mentioned, are variables or constants that are attached to instances of classes. Properties that you’ve added to a class are usually accessed like this:
class
Counter
{
var
number
:
Int
=
0
}
let
myCounter
=
Counter
()
myCounter
.
number
=
2
However, as objects get more complex, it can cause a problem for you as a programmer. If you wanted to represent vehicles with engines, you’d need to add a property to the Vehicle
class; however, this would mean that all Vehicle
instances would have this property, even if they didn’t need one. To keep things better organized, it’s better to move properties that are specific to a subset of your objects to a new class that inherits properties from another.
When you define a class, you can create one that inherits from another. When a class inherits from another (called the parent class), it incorporates all of its parent’s functions and properties. In Swift, classes are allowed to have only a single parent class. This is the same as Objective-C, but differs from C++, which allows classes to have multiple parents (known as multiple inheritance).
To create a class that inherits from another, you put the name of the class you’re inheriting from after the name of the class you’re creating, like so:
class
Car
:
Vehicle
{
var
engineType
:
String
=
"V8"
}
Classes that inherit from other classes can override functions in their parent class. This means that you can create subclasses that inherit most of their functionality, but can specialize in certain areas. For example, the Car
class contains an engineType
property; only Car
instances will have this property.
To override a function, you redeclare it in your subclass and add the override
keyword to let the compiler know that you aren’t accidentally creating a method with the same name as one in the parent class.
In an overridden function, it’s often very useful to call back to the parent class’s version of that function. You can do this through the super
keyword, which lets you get access to the superclass’s functions:
class
Car
:
Vehicle
{
var
engineType
:
String
=
"V8"
// Inherited classes can override functions
override
func
description
()
->
String
{
let
description
=
super
.
description
()
return
description
+
", which is a car"
}
}
In the previous example, the property is a simple value stored in the object. This is known in Swift as a stored property. However, you can do more with properties, including creating properties that use code to figure out their value. These are known as computed properties, and you can use them to provide a simpler interface to information stored in your classes.
For example, consider a class that represents a rectangle, which has both a width
and a height
property. It’d be useful to have an additional property that contains the area, but you don’t want that to be a third stored property. Instead, you can use a computed property, which looks like a regular property from the outside, but on the inside is really a function that figures out the value when needed.
To define a computed property, you declare a variable in the same way as you do for a stored property, but add braces ({
and }
) after it. Inside these braces, you provide a get
section, and optionally a set
section:
class
Rectangle
{
var
width
:
Double
=
0.0
var
height
:
Double
=
0.0
var
area
:
Double
{
// computed getter
get
{
return
width
*
height
}
// computed setter
set
{
// Assume equal dimensions (i.e., a square)
width
=
sqrt
(
newValue
)
height
=
sqrt
(
newValue
)
}
}
}
When creating setters for your computed properties, you are given the new value passed into the setter passed in as a constant called newValue
.
In the previous example, we computed the area by multiplying the width and height. The property is also settable—if you set the area of the rectangle, the code assumes that you want to create a square and updates the width and height to the square root of the area.
Working with computed properties looks identical to working with stored properties:
var
rect
=
Rectangle
()
rect
.
width
=
3.0
rect
.
height
=
4.5
rect
.
area
// = 13.5
rect
.
area
=
9
// width & height now both 3.0
When working with properties, you often may want to run some code whenever a property changes. To support this, Swift lets you add observers to your properties. These are small chunks of code that can run just before or after a property’s value changes. To create a property observer, add braces after your property (much like you do with computed properties), and include willSet
and didSet
blocks. These blocks each get passed a parameter—willSet
, which is called before the property’s value changes, is given the value that is about to be set, and didSet
is given the old value:
class
PropertyObserverExample
{
var
number
:
Int
=
0
{
willSet
(
newNumber
)
{
(
"About to change to \(newNumber)"
)
}
didSet
(
oldNumber
)
{
(
"Just changed from \(oldNumber) to \(self.number)!"
)
}
}
}
Property observers don’t change anything about how you actually work with the property—they just add further behavior before and after the property changes:
var
observer
=
PropertyObserverExample
()
observer
.
number
=
4
// prints "About to change to 4", then "Just changed from 0 to 4!"
You can also make a property lazy. A lazy property is one that doesn’t get set up until the first time it’s accessed. This lets you defer some of the more time-consuming work of setting up a class to later on, when it’s actually needed. To define a property as lazy, you put the lazy
keyword in front of it. Lazy loading is very useful to save on memory for properties that may not be used—there is no point in initializing something that won’t be used!
You can see lazy properties in action in the following example. In this code, there are two properties, both of the same type, but one of them is lazy:
class
SomeExpensiveClass
{
init
(
id
:
Int
)
{
(
"Expensive class \(id) created!"
)
}
}
class
LazyPropertyExample
{
var
expensiveClass1
=
SomeExpensiveClass
(
id
:
1
)
// note that we're actually constructing a class,
// but it's labeled as lazy
lazy
var
expensiveClass2
=
SomeExpensiveClass
(
id
:
2
)
init
()
{
(
"First class created!"
)
}
}
var
lazyExample
=
LazyPropertyExample
()
// prints "Expensive class 1 created", then "First class created!"
lazyExample
.
expensiveClass1
// prints nothing; it's already created
lazyExample
.
expensiveClass2
// prints "Expensive class 2 created!"
In this example, when the lazyExample
variable is created, it immediately creates the first instance of SomeExpensiveClass
. However, the second instance isn’t created until it’s actually used by the code.
A protocol can be thought of as a list of requirements for a class. When you define a protocol, you’re creating a list of properties and methods that classes can declare that they have.
A protocol looks very much like a class, with the exception that you don’t provide any actual code—you just define what kinds of properties and functions exist and how they can be accessed.
For example, if you wanted to create a protocol that describes any object that can blink on and off, you could use this:
protocol
Blinking
{
// This property must be (at least) gettable
var
isBlinking
:
Bool
{
get
}
// This property must be gettable and settable
var
blinkSpeed
:
Double
{
get
set
}
// This function must exist, but what it does is up to the implementor
func
startBlinking
(
blinkSpeed
:
Double
)
->
Void
}
Once you have a protocol, you can create classes that conform to a protocol. When a class conforms to a protocol, it’s effectively promising to the compiler that it implements all of the properties and methods listed in that protocol. It’s allowed to have more stuff besides that, and it’s also allowed to conform to multiple protocols.
To continue this example, you could create a specific class called Light
that implements the Blinking
protocol. Remember, all a protocol does is specify what a class can do—the class itself is responsible for determining how it does it:
class
TrafficLight
:
Blinking
{
var
isBlinking
:
Bool
=
false
var
blinkSpeed
:
Double
=
0.0
func
startBlinking
(
blinkSpeed
:
Double
)
{
(
"I am a traffic light, and I am now blinking"
)
isBlinking
=
true
// We say "self.blinkSpeed" here, as opposed to "blinkSpeed",
// to help the compiler tell the difference between the
// parameter 'blinkSpeed' and the property
self
.
blinkSpeed
=
blinkSpeed
}
}
class
Lighthouse
:
Blinking
{
var
isBlinking
:
Bool
=
false
var
blinkSpeed
:
Double
=
0.0
func
startBlinking
(
blinkSpeed
:
Double
)
{
(
"I am a lighthouse, and I am now blinking"
)
isBlinking
=
true
self
.
blinkSpeed
=
blinkSpeed
}
}
The advantage of using protocols is that you can use Swift’s type system to refer to any object that conforms to a given protocol. This is useful because you get to specify that you only care about whether an object conforms to the protocol—the specific type of the class doesn’t matter since we are using the protocol as a type:
var
aBlinkingThing
:
Blinking
// can be ANY object that has the Blinking protocol
aBlinkingThing
=
TrafficLight
()
aBlinkingThing
.
startBlinking
(
4.0
)
// prints "I am now blinking"
aBlinkingThing
.
blinkSpeed
// = 4.0
aBlinkingThing
=
Lighthouse
()
In Swift, you can extend existing types and add further methods and computed properties. This is very useful in two situations:
You’re working with a type that someone else wrote, and you want to add functionality to it but either don’t have access to its source code or don’t want to mess around with it.
You’re working with a type that you wrote, and you want to divide up its functionality into different sections for readability.
Extensions let you do both with ease. In Swift, you can extend any type—that is, you can extend both classes that you write, as well as built-in types like Int
and String
.
To create an extension, you use the extension
keyword, followed by the name of the type you want to extend. For example, to add methods and properties to the built-in Int
type, you can do this:
extension
Int
{
var
doubled
:
Int
{
return
self
*
2
}
func
multiplyWith
(
anotherNumber
:
Int
)
->
Int
{
return
self
*
anotherNumber
}
}
Once you extend a type, the methods and properties you defined in the extension are available to every instance of that type:
2
.
doubled
// = 4
4
.
multiplyWith
(
32
)
// = 128
You can only add computed properties in an extension. You can’t add your own stored properties.
You can also use extensions to make a type conform to a protocol. For example, you can make the Int
type conform to the Blinking
protocol described earlier:
extension
Int
:
Blinking
{
var
isBlinking
:
Bool
{
return
false
;
}
var
blinkSpeed
:
Double
{
get
{
return
0.0
;
}
set
{
// Do nothing
}
}
func
startBlinking
(
blinkSpeed
:
Double
)
{
(
"I am the integer \(self). I do not blink."
)
}
}
2
.
isBlinking
// = false
2
.
startBlinking
(
2.0
)
// prints "I am the integer 2. I do not blink."
Swift defines three levels of access control, which determines what information is accessible to which parts of the application:
Public classes, methods, and properties are accessible by any part of the app. For example, all of the classes in UIKit that you use to build iOS apps are public.
Internal entities (data and methods) are only accessible to the module in which they’re defined. A module is an application, library, or framework. This is why you can’t access the inner workings of UIKit—it’s defined as internal to the UIKit framework. Internal is the default level of access control: if you don’t specify the access control level, it’s assumed to be internal.
Private entities are only accessible to the file in which it’s declared. This means that you can create classes that hide their inner workings from other classes in the same module, which helps to keep the amount of surface area that those classes expose to each other to a minimum.
The kind of access control that a method or property can have depends on the access level of the class that it’s contained in. You can’t make a method more accessible than the class in which it’s contained. For example, you can’t define a private class that has a public method:
public
class
AccessControl
{
}
By default, all properties and methods are internal
. You can explicitly define a member as internal
if you want, but it isn’t necessary:
// Accessible to this module only
// 'internal' here is the default and can be omitted
internal
var
internalProperty
=
123
The exception is for classes defined as private
—if you don’t declare an access control level for a member, it’s set as private
, not internal
. It is impossible to specify an access level for a member of an entity that is more open than the entity itself.
When you declare a method or property as public
, it becomes visible to everyone in your app:
// Accessible to everyone
public
var
publicProperty
=
123
If you declare a method or property as private
, it’s only accessible from within the source file in which it’s declared:
// Only accessible in this source file
private
var
privateProperty
=
123
Finally, you can render a property as read-only by declaring that its setter is private. This means that you can freely read and write the property’s value within the source file that it’s declared in, but other files can only read its value:
// The setter is private, so other files can't modify it
private
(
set
)
var
privateSetterProperty
=
123
An operator is actually a function that takes one or two values and returns a value. Operators, just like other functions, can be overloaded. For example, you could represent the +
function like this:
func
+
(
left
:
Int
,
right
:
Int
)
->
Int
{
return
left
+
right
}
The preceding example actually calls itself in an infinitely recursive way, which hangs your app. Don’t actually write this code.
Swift lets you define new operators and overload existing ones for your new types, which means that if you have a new type of data, you can operate on that data using both existing operators, as well as new ones you invent yourself.
For example, imagine you have an object called Vector2D
, which stores two floating-point numbers:
class
Vector2D
{
var
x
:
Float
=
0.0
var
y
:
Float
=
0.0
init
(
x
:
Float
,
y
:
Float
)
{
self
.
x
=
x
self
.
y
=
y
}
}
If you want to allow adding instances of this type of object together using the +
operator, all you need to do is provide an implementation of the +
function:
func
+
(
left
:
Vector2D
,
right
:
Vector2D
)
->
Vector2D
{
let
result
=
Vector2D
(
x
:
left
.
x
+
right
.
x
,
y
:
left
.
y
+
right
.
y
)
return
result
}
You can then use it as you’d expect:
let
first
=
Vector2D
(
x
:
2
,
y
:
2
)
let
second
=
Vector2D
(
x
:
4
,
y
:
1
)
let
result
=
first
+
second
// = (x:6, y:3)
For information on how to create your own custom operators, see the “Advanced Operators” section of The Swift Programming Language.
Swift is a statically typed language. This means that the Swift compiler needs to definitively know what type of information your code is dealing with. This means that you can’t pass a string to code that expects to deal with a date (which is something that can happen in Objective-C!).
However, this rigidity means that you lose some flexibility. It’s annoying to have to write a chunk of code that does some work with strings, and another that works with dates.
This is where generics come in. Generics allow you to write code that doesn’t need to know precisely what information it’s dealing with. An example of this kind of use is in arrays: they don’t actually do any work with the data they store, but instead just store it in an ordered collection. Arrays are, in fact, generics.
To create a generic type, you name your object as usual, and then specify any generic types between angle brackets. T
is traditionally the term used, but you can put anything you like. For example, to create a generic Tree
object, which contains a value and any number of child Tree
objects, you’d do the following:
class
Tree
<
T
>
{
// 'T' can now be used as a type inside this class
// 'value' is of type T
var
value
:
T
// 'children' is an array of Tree objects that have
// the same type as this one
private
(
set
)
var
children
:
[
Tree
<
T
>
]
=
[]
// We can initialize this object with a value of type T
init
(
value
:
T
)
{
self
.
value
=
value
}
// And we can add a child node to our list of children
func
addChild
(
value
:
T
)
->
Tree
<
T
>
{
let
newChild
=
Tree
<
T
>
(
value
:
value
)
children
.
append
(
newChild
)
return
newChild
}
}
Once a generic type is defined, you can create a specific, nongeneric type from it. For example, the Tree
generic type just defined can be used to create a version that works with Int
s and one that works with String
s:
// Tree of integers
let
integerTree
=
Tree
<
Int
>
(
value
:
5
)
// Can add children that contain Ints
integerTree
.
addChild
(
10
)
integerTree
.
addChild
(
5
)
// Tree of strings
let
stringTree
=
Tree
<
String
>
(
value
:
"Hello"
)
stringTree
.
addChild
(
"Yes"
)
stringTree
.
addChild
(
"Internets"
)
When you work with arrays and dictionaries, you use square brackets, [
and ]
, to indicate to Swift what part of the array or dictionary you want to work with. The term for this is subscripting, and it’s something that your own classes and types can adopt.
You do this using the subscript
keyword, and define what it means to get and set values via a subscript. For example, let’s say you want to access the individual bits inside an 8-bit integer. You can do this with subscripting, like so:
// Extend the unsigned 8-bit integer type
extension
UInt8
{
// Allow subscripting this type using UInt8s;
subscript
(
bit
:
UInt8
)
->
UInt8
{
// This is run when you do things like "value[x]"
get
{
return
(
self
>>
bit
&
0x07
)
&
1
}
// This is run when you do things like "value[x] = y"
set
{
let
cleanBit
=
bit
&
0x07
let
mask
=
0xFF
^
(
1
<<
cleanBit
)
let
shiftedBit
=
(
newValue
&
1
)
<<
cleanBit
self
=
self
&
mask
|
shiftedBit
}
}
}
With this in place, you can access the individual bits inside the number by reading and writing them.
var
byte
:
UInt8
=
212
byte
[
0
]
// 0
byte
[
2
]
// 1
byte
[
5
]
// 0
byte
[
6
]
// 1
// Change the last bit
byte
[
7
]
=
0
// The number is now changed!
byte
// = 84
For the most part, structures are very similar to classes: you can put properties and methods in them, they have initializers, and they generally behave in an object-like way, just like a class does. However, there are two main things that differentiate them from classes:
Structures do not have inheritance—that is, you cannot make a structure inherit its methods and properties from another.
When you pass a structure around in your code, the structure is always copied.
Structures are declared as follows:
struct
Point
{
var
x
:
Int
var
y
:
Int
}
In Swift, structures are value types, which are always copied when passed around. Some value types in Swift include Int
, String
, Array
, and Dictionary
, all of which are implemented as structures.
Additionally, structures in Swift get a compiler-provided initializer, called the memberwise initializer, if you don’t provide one yourself:
let
p
=
Point
(
x
:
2
,
y
:
3
)
In Swift, just like in Objective-C, code is grouped into modules. When you define a framework or application, all of the code that’s added to it is placed within that target’s module. To get access to the code, you use the import
keyword:
import
AVFoundation
Depending on your programming background, you are probably used to including code to make sure you don’t accidentally include something multiple times. In Swift you don’t have to worry about this. Modules are clever enough to handle potential import conflicts, letting you focus on making great apps!
The different features you work with in Swift come from different places, or libraries, depending on how platform-specific they are. These are the four main libraries you’ll access:
Contains all of the lowest-level types and functions that you’ll be working with, including Int
, String
, and so on, as well as math functions, arrays and dictionaries, and more.
You don’t need to do anything special to access the standard library; all Swift programs have access to it.
A slightly higher-level library that provides more tools and types, such as NSNotificationCenter
, which is used to broadcast application-wide notifications, and NSJSONSerialization
, which allows you to read and write JSON data.
You import the Foundation library with the import Foundation
statement, at the top of your file.
Specific to OS X and includes features like buttons, windows, image views, and menus.
You import Cocoa with the import Cocoa
statement.
On iOS, provides equivalent tools and functionality from Cocoa: views, touch input, sensor capabilities, and so on.
You import Cocoa Touch with the import UIKit
statement.
If you import Cocoa or Cocoa Touch, you’ll also import Foundation. You don’t need to import both.
There are also equivalent libraries for tvOS and watchOS. There are also some third-party tools and libraries for Swift, but these are well beyond the scope of this book.
In Cocoa, you’ll frequently find yourself working with chunks of arbitrary data that you need to save to or load from disk, or that you’ve downloaded from the network. Cocoa represents these as NSData
objects.
You can get an NSData
object in a variety of ways. For example, if you have a string that you want to convert to an NSData
, you can use the string’s dataUsingEncoding
method, like so:
let
stringToConvert
=
"Hello, Swift"
let
data
=
stringToConvert
.
dataUsingEncoding
(
NSUTF8StringEncoding
)
You can also load data from a URL or from a file location on disk. If the file is one of the resources built into your project, you first need to work out where on disk it’s being stored; once you have that, you can load its contents into memory.
To get the location of a built-in file, you first use the NSBundle
class to determine where a given file is being stored on disk using the pathForResource
method. Once you’ve done that, you construct an NSData
object by providing it either a URL or a filepath:
// Loading from URL
if
let
URL
=
NSURL
(
string
:
"https://oreilly.com"
)
{
let
loadedDataFromURL
=
NSData
(
contentsOfURL
:
URL
)
}
// Loading from a file
if
let
filePath
=
NSBundle
.
mainBundle
()
.
pathForResource
(
"SomeFile"
,
ofType
:
"txt"
)
{
let
loadedDataFromPath
=
NSData
(
contentsOfFile
:
filePath
)
}
Using NSData(contentsOfURL:)
this way to get data over the network will cause pauses and slowdowns, because the code will wait for the data to be loaded. If you’re making an app that loads data over the network, consider using a dedicated library that specializes in doing it, like AlamoFire.
A bundle, represented by the NSBundle
class, is an object that bundles up all the resources that your apps can use.
You can use NSBundle
to load and unload code, images, audio, or almost anything imaginable without having to deal directly with the filesystem.
You can also convert an object to data to make it easier to save, load, or send the object. To do this, you first make an object conform to the NSObject
and NSCoding
protocols, and then add two methods—encodeWithCoder
, and an initializer that takes an NSCoder
:
class
SerializableObject
:
NSObject
,
NSCoding
{
var
name
:
String
?
func
encodeWithCoder
(
aCoder
:
NSCoder
)
{
aCoder
.
encodeObject
(
name
!
,
forKey
:
"name"
)
}
override
init
()
{
self
.
name
=
"My Object"
}
required
init
(
coder
aDecoder
:
NSCoder
)
{
self
.
name
=
aDecoder
.
decodeObjectForKey
(
"name"
)
as
?
String
}
}
An object that conforms to NSCoding
can be converted to an NSData
object, and also be loaded from one, via the NSKeyedArchiver
and NSKeyedUnarchiver
classes. The trick to it is in the encodeWithCoder
method, and in the special initializer: in the encodeWithCoder
method, you take the NSCoder
that’s passed in as a parameter and store any values that you want to keep in it. Later, in the initializer, you pull those values out.
Converting these objects to and from data is very straightforward and looks like this:
let
anObject
=
SerializableObject
()
anObject
.
name
=
"My Thing That I'm Saving"
// Converting it to data
let
objectConvertedToData
=
NSKeyedArchiver
.
archivedDataWithRootObject
(
anObject
)
// Converting it back
// Note that the conversion might fail, so 'unarchiveObjectWithData' returns
// an optional value. So, use 'as?' to check to see if it worked.
let
loadedObject
=
NSKeyedUnarchiver
.
unarchiveObjectWithData
(
objectConvertedToData
)
as
?
SerializableObject
loadedObject
?
.
name
// = "My Thing That I'm Saving"
It’s normal for computer programs to generate errors. When that happens, you need to be ready to handle them, and Swift makes this particularly easy and robust.
If you programmed using Objective-C or Swift 1.0, you might be familiar with a different error-handling system. Previously, an NSError
object would be passed as a pointer; when something could fail, you’d pass in an NSError
object as a parameter, and if there was an error you could fill the object with information.
This was powerful, as it allowed the return value of a method to be separated from any potential error information. It was, however, easy to forget to look inside the NSError
object. Swift 2.0 replaces this system, and while it expects a little more from programmers now, it is much clearer to read, gives you greater safety by making sure all errors are caught, and requires less messing around with pointers.
In Swift, objects are represented by ErrorType
. ErrorType
is a protocol, which means that any class, enum, or structure can be an error. When your code encounters an error, you throw an error.
For compatibility in Swift, NSError
is an ErrorType
, which means it can be thrown like every other ErrorType
.
For example, let’s define an enumeration for problems that can relate to a bank account. By making the enumeration an ErrorType
, we can throw it as an error:
enum
BankError
:
ErrorType
{
// Not enough money in the account
case
NotEnoughFunds
// Can't create an account with negative money
case
CannotBeginWithNegativeFunds
// Can't make a negative deposit or withdrawal
case
CannotMakeNegativeTransaction
(
amount
:
Float
)
}
Functions that can throw errors must be marked with the throws
keyword, which goes after the function’s return type:
// A simple bank account class
class
BankAccount
{
// The amount of money in the account
private
(
set
)
var
balance
:
Float
=
0.0
// Initializes the account with an amount of money
// Throws an error if you try to create the account
// with negative funds.
init
(
amount
:
Float
)
throws
{
// Ensure that we have a non-negative amount of money
guard
amount
>
0
else
{
throw
BankError
.
CannotBeginWithNegativeFunds
}
balance
=
amount
}
// Adds some money to the account
func
deposit
(
amount
:
Float
)
throws
{
// Ensure that we're trying to withdraw a non-negative amount
guard
amount
>
0
else
{
throw
BankError
.
CannotMakeNegativeTransaction
(
amount
:
amount
)
}
balance
+=
amount
}
// Withdraws money from the bank account
func
withdraw
(
amount
:
Float
)
throws
{
// Ensure that we're trying to deposit a non-negative amount
guard
amount
>
0
else
{
throw
BankError
.
CannotMakeNegativeTransaction
(
amount
:
amount
)
}
// Ensure that we have enough to withdraw this amount
guard
balance
>=
amount
else
{
throw
BankError
.
NotEnoughFunds
}
balance
-=
amount
}
}
When you call any function, method, or initializer that throws
, you are required to wrap it in a do
-catch
block. In the do
block, you call the methods that may potentially throw errors; each time you do, you preface the potentially throwing call with try
. If the method call throws an error, the do
block stops executing and the catch
clause runs:
do
{
let
vacationFund
=
try
BankAccount
(
amount
:
5
)
try
vacationFund
.
deposit
(
5
)
try
vacationFund
.
withdraw
(
11
)
}
catch
let
error
as
BankError
{
// Catch any BankError that was thrown
switch
(
error
)
{
case
.
NotEnoughFunds
:
(
"Not enough funds in account!"
)
case
.
CannotBeginWithNegativeFunds
:
(
"Tried to start an account with negative money!"
)
case
.
CannotMakeNegativeTransaction
(
let
amount
)
:
(
"Tried to do a transaction with a negative amount of \(amount)!"
)
}
}
catch
let
error
{
// (Optional:) catch other types of errors
}
However, it can sometimes be cumbersome to wrap calls to methods that can throw errors in a do
-catch
block. Sometimes, you may not care about the specifics of the error; you just care if there was an error or not. This is where the try?
statement comes in. If you preface a call to something that can throw an error with try?
, and it does throw an error, the result will be nil
:
let
secretBankAccountOrNot
=
try
?
BankAccount
(
amount
:
-
50
)
// = nil
Finally, there are sometimes cases where your program needs the method call to succeed and guarantee a returned value. If you call a method with try!
, and it throws an error, your program will simply crash. (This has the same effect as using try?
to receive an optional and then using the force-unwrap operator (!
) on that optional.)
let
secretBankAccountOrNot
=
try
!
BankAccount
(
amount
:
-
50
)
// crash!
The try?
and try!
statements do not need to be in a do
-catch
block. If you do put them in one, any errors won’t be caught by the catch
block; they’ll still just either evaluate to nil
or crash.
Objects in Swift are memory managed. When an object is being used, Swift keeps it in memory; when it’s no longer being used, it’s removed from memory.
The technique that Swift uses to keep track of which objects are being used and which are not is called reference counting. When an object is assigned to a variable, a counter called the retain count goes up by 1. When the object is no longer assigned to that variable, the retain count goes down. If the retain count ever reaches 0, that means that no variables are referring to that object, and the object is then removed from memory.
The nice thing about Swift is that this all happens at the compiler level. As the compiler reads your code, it keeps track of when objects get assigned to variables and adds code that increments and decrements the retain count.
However, this automatic memory management has one potential snag that you need to keep an eye out for: retain cycles.
A retain cycle is where you have two objects that refer to each other, but are otherwise not referred to by any other part of the application. Because those objects refer to each other, their retain count is not zero, which means they stay in memory; however, because no variable in the rest of the application refers to them, they’re inaccessible (and consequently useless).
Swift solves this using the concept of weak references. A weak reference is a variable that refers to an object, but doesn’t change the retain count of that object. You use weak references when you don’t particularly care whether an object stays in memory or not (i.e., your code isn’t the owner of that object).
To declare a weak reference in Swift, you use the weak
keyword, like so:
class
Class1
{
init
()
{
(
"Class 1 being created!"
)
}
deinit
{
(
"Class 1 going away!"
)
}
}
class
Class2
{
// Weak vars are implicitly optional
weak
var
weakRef
:
Class1
?
}
The topic of memory management can get complex if you’re doing more advanced things. If you’d like to learn more about it, see the section “Automatic Reference Counting” in The Swift Programming Language.
Cocoa is built around a number of design patterns whose purpose is to make your life as a developer more consistent and (one hopes) more productive. Three key patterns are the model-view-controller (MVC) pattern, upon which most of Cocoa and Cocoa Touch is built; the delegation pattern, which allows both your code and Cocoa to be highly flexible in determining what code gets run by whom; and notifications, which allow your code to watch for important events that happen within your app. We’ll be working with notifications in a very hands-on sense later in the book (in Chapters 14 and 15); at the moment, let’s dive in to model-view-controller and delegation!
The model-view-controller design pattern is one of the fundamental design patterns in Cocoa. Let’s take a look at what each of these parts means:
Objects that contain data or otherwise coordinate the storing, management, and delivery of data to other objects. Models can be as simple as a string or as complicated as an entire database—their purpose is to store data and provide it to other objects. They don’t care what happens to the data once they give it to someone else; their only concern is managing how the data is stored.
Objects that work directly with the user, providing information to them and receiving input back. Views do not manage the data that they display—they only show it to the user. Views are also responsible for informing other objects when the user interacts with them. Like data and models, views do not care what happens next—their responsibility ends with informing the rest of the application.
Objects that mediate between models and views and contain the bulk of what some call the “business logic” of an application—the actual logic that defines what the application is and how it responds to user input. At a minimum, the controller is responsible for retrieving information from the model and providing it to the view; it is also responsible for providing information to the model when it is informed by the view that the user has interacted with it.
For an illustration of the model-view-controller design pattern in action, imagine a simple text editor. In this example, the application loads a text file from disk and presents its contents to the user in a text field. The user makes changes in the text field and saves those changes back to disk.
We can break this application down into model, view, and controller objects:
The model is an object that is responsible for loading the text file from disk and writing it back out to disk. It is also responsible for providing the text as a string to any object that asks for it.
The view is the text field, which asks another object for a string to display and then displays the text. It also accepts keyboard input from the user; whenever the user types, it informs another object that the text has changed. It is also able to tell another object when the user has told it to save changes.
The controller is the object responsible for instructing the model object to load a file from disk, and it passes the text to the view. It receives updates from the view object when the text has changed and passes those changes to the model. Finally, it can be told by the view that the user has asked to save the changes; when that happens, it instructs the model to do the work of actually writing the file out to disk.
Breaking the application into these areas of responsibility enables us to more easily make changes to it.
For example, if the developer decides that the next version of the application should add the ability to upload the text file to the Internet whenever the file is saved, the only thing that must be changed is the model class—the controller can stay the same, and the view never changes.
Likewise, clearly defining which objects are responsible for which features makes it easier to make changes to an application while maintaining a clear structure in the project. If the developer decides to add a spell checking feature to the application, that code should clearly be added to the controller, as it has nothing to do with how the text is presented to the user or stored on disk. (You could, of course, add some features to the view that would allow it to indicate which words are misspelled, but the bulk of the code would need to be added in the controller.)
The majority of the classes described in this chapter, such as NSData
, arrays, and dictionaries, are model classes; all they do is store and present information to other classes. NSKeyedArchiver
is a controller class; it takes information and performs logical operations on it. NSButton
and UITextField
are examples of view objects; they present information to the user and do not care about how the data is managed.
The model-view-controller paradigm becomes very important when you start looking at the more advanced Cocoa features, like the document architecture and bindings, both of which are covered throughout this book.
Delegation is Cocoa’s term for passing off some responsibilities of an object to another. An example of this is the UIApplication
object, which represents an application on iOS. This object needs to know what should happen when the application moves to the background. Many other languages handle this problem by subclassing—for example, in C++, the UIApplication
class would define an empty placeholder method for applicationDidEnterBackground
, and then you as a developer would subclass UIApplication
and override the applicationDidEnterBackground
method.
However, this is a particularly heavy-handed solution and causes additional problems—it increases the complexity of your code, and also means that if you want to override the behavior of two classes, you need separate subclasses for each one.1 Swift’s answer to this problem is built around the fact that an object can determine, at runtime, whether another object is capable of responding to a method.
Let’s say an object, Object A, that wants to let another object, Object B, know that something is going to happen or has happened, stores a reference to Object B as an instance variable. This reference to Object B is known as the delegate. When the event happens, Object A checks to see if the delegate object (Object B) implements a method that suits the event—for delegates of the UIApplication
class, for example, the application delegate is asked if it implements the applicationDidEnterBackground
method. If it does, that method is called.
Because of this loose coupling, it’s possible for an object to be the delegate for multiple objects. For example, an object could become the delegate of both an audio playback object and an image picker, and be notified both when audio playback completes and when an image has been captured by the camera.
Because the model-view-controller pattern is built around a very loose coupling of objects, it helps to have a more rigidly defined interface between objects so that your application can know with more certainty how one object expects others to behave.
The specific messages used by delegates are often listed in protocols. For example, if your object wants to be the delegate of an AVAudioPlayer
object, it should conform to the AVAudioPlayerDelegate
protocol.
Working with delegates in Swift is easy. Imagine you have two classes, and you want one of them to act as the delegate for another:
// Define a protocol that has a function called 'handleIntruder'
protocol
HouseSecurityDelegate
{
// We don't define the function here, but rather
// indicate that any class that is a 'HouseSecurityDelegate'
// is required to have a 'handleIntruder' function
func
handleIntruder
()
}
class
House
{
// The delegate can be any object that conforms to the
// HouseSecurityDelegate protocol
var
delegate
:
HouseSecurityDelegate
?
func
burglarDetected
()
{
// Check to see if the delegate is there, then call it
delegate
?
.
handleIntruder
()
}
}
class
GuardDog
:
HouseSecurityDelegate
{
func
handleIntruder
()
{
(
"Releasing the hounds!"
)
}
}
let
myHouse
=
House
()
myHouse
.
burglarDetected
()
// does nothing
let
theHounds
=
GuardDog
()
myHouse
.
delegate
=
theHounds
myHouse
.
burglarDetected
()
// prints "Releasing the hounds!"
The burglarDetected
method needs to check that a security delegate exists for the house before calling its handleIntruder
method. It does this using a Swift feature called optional chaining, which lets you access something that depends on an optional having a value, without specifically testing the optional first. If the optional has a value, in this case a houseSecurityDelegate
, its handleIntruder
method is called. If the optional is nil
, nothing happens. You can use optional chaining to access the properties, method, or subscripts of your classes, structures, and enumerations in this way.
Before we wrap up this part and begin our long, slow dive into building real-world apps, it’s worth looking at the big picture of how apps are built, both on OS X and iOS.
iOS and OS X are built on the idea of event-driven programming. Anything that your app does is in response to an event of some kind. On OS X, events include the mouse moving or clicking, keyboard input, and the window resizing; on iOS, they include touch input, sensor input. On both iOS and OS X, events can also include timers firing or the screen refreshing.
At their core, apps are about the run loop, an infinite loop that waits for an event to fire, and then takes appropriate actions. Most of those actions are handled by the built-in parts of your app; for example, swiping your finger on a list will cause the list to adjust its position. However, there are several events that your code handles. For example, when a button is clicked, as part of handling this event, the code calls a method that you write.
The delegation pattern is used a lot for event handling. Every time the operating system needs to let your app know that an interesting event has happened, the application delegate object—which you write—has a method called on it. The application delegate object—usually shortened to app delegate—is just an instance of a class that conforms to the NSApplicationDelegate
(on OS X) or UIApplicationDelegate
(on iOS) protocol. When you create a project, Xcode adds a file that contains a class that implements the correct protocol for your platform.
The app delegate contains methods like application(_, didFinishLaunchingWithOptions:)
; these methods are called when events happen (like when the application finishes launching). You provide code in these methods to run when the events occur.
Window controllers are objects that manage a window’s contents on OS X, and view controllers manage a view’s contents on both iOS and OS X. On iOS a view controller is usually fullscreen, but on OS X that may not be the case (although the view that controller manages is usually the full size of the window containing it).
When working with documents, you’ll be provided with a window controller that allows you to keep your logic in the document class. We’ll look at this in more detail in Chapter 4.
View controllers can manage other view controllers; for example, navigation controllers are a view controller that manages multiple child view controllers. View controllers exist on iOS and OS X, while window controllers exist only on OS X (because the concept of windows really only exists on OS X).
Windows in Swift are the individual windows of the application, so the entire box that the application shows on the screen. A view, on the other hand, is contained inside a window and represents and is responsible for the elements within. In iOS you will have a single window per application, and multiple views will be shown inside that window. In OS X your application might have multiple windows, each with its own views.
When an application starts, it loads its interface. The interface is stored inside a file, and there are two types of files: nib files and storyboard files. Both are used in the interface builder to design and lay out an interface.
Nib files contain a collection of objects, and are generally used to represent a single window or view; they also contain nonvisible objects, such as controller objects, when needed.
Storyboards take this idea and extend it by storing multiple interfaces—that is, views and windows—and letting you describe how you get from one interface to another, using connections called segues.
1 C++’s answer to this problem is multiple inheritance, which has its own problems.