Chapter 3. Swift for Object-Oriented App Development

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.

Classes and Objects

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() {
          print("Traveling at \(maxSpeed) kph")
      }
  }
Note

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"

Initialization and Deinitialization

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 () {
          print("I've been created!")
      }
      // Convenience initializer, required to call the
      // designated initializer (above)
      convenience init (text: String) {
          self.init() // this is mandatory
          print("I was called with the convenience initializer!")
      }
      // Deinitializer
      deinit {
          print("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

Inheritance

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

  }

Computed properties

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

Protocols

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) {
          print("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) {
          print("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()

Extensions

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 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) {
          print("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."

Access Control

Swift defines three levels of access control, which determines what information is accessible to which parts of the application:

Public

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

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

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

Generics

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 Ints and one that works with Strings:

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

The Swift Standard Library, Foundation, Cocoa, and Cocoa Touch

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:

The Swift Standard Library

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.

Foundation

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.

Cocoa

Specific to OS X and includes features like buttons, windows, image views, and menus.

You import Cocoa with the import Cocoa statement.

Cocoa Touch

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.

Note

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.

Data

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)

Serialization and Deserialization

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"

Error Handling

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.

Note

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:
          print("Not enough funds in account!")
      case .CannotBeginWithNegativeFunds:
          print("Tried to start an account with negative money!")
      case .CannotMakeNegativeTransaction(let amount):
          print("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:

Tip

This means that the return type of any call that you try? will be an optional.

  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.

Memory Management

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() {
          print("Class 1 being created!")
      }

      deinit {
          print("Class 1 going away!")
      }
  }

  class Class2 {
      // Weak vars are implicitly optional
      weak var weakRef : Class1?
  }
Tip

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.

Design Patterns in Cocoa and Cocoa Touch

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!

Model-View-Controller

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:

Models

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.

Views

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.

Controllers

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

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() {
          print("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.

Structuring an App

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.

1 C++’s answer to this problem is multiple inheritance, which has its own problems.