Intro to Object-Oriented Design in Swift: Part 2/2

Learn the basics of object-oriented design in Swift. In this second part, you’ll learn about polymorphism, initialization, and some common design patterns for dealing with objects. By Ray Fix.

Leave a rating/review
Save for later
Share

Update 4/11/2015: Updated for Xcode 6.3 / Swift 1.2

Update note: This tutorial was updated for iOS 8 and Swift by Ray Fix. Original post by Tutorial Team member Ellen Shapiro.

In Part 1 of this tutorial, you learned the basics of object-oriented design: objects, inheritance, and the model-view-controller pattern. You created the beginnings of a simple application called Vehicles to help you gain a better understanding of these concepts.

Here in the second part, you’re going to learn about polymorphism and initialization. Along the way, you will learn about a few of Object-Oriented programming patterns including the Decorator, the Adapter, and the Singleton.

If you completed the previous tutorial, great! You can pick up where you left off with your previous project. However, if you want to jump right into things, you can download the completed project from Part 1 to get started.

Polymorphism

The general definition of Polymorphism comes from its Greek roots – “Poly” means many, and “Morph” means forms.

The computer-science specific definition, pulled from the Free Online Dictionary of Computing, is:

A variable that may refer to objects whose class is not known at compile time and which respond at run time according to the actual class of the object to which they refer.

You have already seen polymorphism in action in Part 1. In VehicleDetailViewController’s configureView() method, you used the computed property vehicleDetails that was overridden in each subclass. You know that any kind of vehicle will have that vehicleDetails property, but you’ll get a different value depending on whether the object is a Car, Motorocycle or Truck.

There are several patterns related to polymorphism that can be used within Swift, but two key ones you’ll see often are the Decorator and Adapter patterns. These are implemented using the language keywords extension and protocol respectively.

The Decorator Pattern

From Apple’s Cocoa Fundamentals guide’s section on Cocoa Design Patterns:

The Decorator design pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. As does subclassing, adaptation of the Decorator pattern allows you to incorporate new behavior without modifying existing code. Decorators wrap an object of the class whose behavior they extend.

The primary example of the Decorator pattern in Swift is when you create an extension. In Objective-C, there is a similar mechanism with class categories.

Extensions allow you to add additional methods to classes and structs without having to subclass or alter the original source code. For example, they can be used to add functionality to the stock UIKit components such as additional helper methods on UIView or custom color constructors on UIColor.

The difference between an extension and a subclass is pretty simple: You can add new methods but not new properties with an extension. If you want to add a new property, you’ll probably need to create a subclass and use the power of inheritance to create your additional properties and methods.

But what if you don’t need to add new properties? What if you just want to create a simple way to encapsulate something you have to do repeatedly with a particular UIKit object? In this case, an extension is a possible solution.

To try this out, you’re going to add a convenience method to UIAlertController to do away with performing the setup dance required for simple alerts over and over again in your code.

Implementing the Decorator Pattern

You could put the definition of your UIAlertController extension in any file and it would be visible to the entire app. However, to keep things neat and to facilitate it being used in other future projects, create it in a separate file. Go to File\New\File… and select iOS\Source\Swift File. Then name the file UIAlertController+Convenience. The format [Component]+[Extension Name] borrows a page from Objective-C category file naming conventions. While this is just a convention, it helps communicate your intent. In this case, you are adding some convenience methods to UIAlertController.

Creating a method within an extension is very similar to creating a method on a normal class. Open UIAlertController+Convenience.swift and add the following:

import UIKit

extension UIAlertController {
  class func alertControllerWithTitle(title:String, message:String) -> UIAlertController {
    let controller = UIAlertController(title: title, message: message, preferredStyle: .Alert)
    controller.addAction(UIAlertAction(title: "OK", style: .Default, handler: nil))
    return controller
  }  
}

Rather than defining a new class, you create an extension on the existing UIAlertController class.

You’re not doing anything revolutionary here — just packaging up and returning a UIAlertController that has a single button dismiss action.

Bonus: In addition to using the Decorator pattern by adding functionality to UIAlertController you are also implementing what is called the Factory pattern. The Factory pattern returns an initialized instance configured in a particular way. You might say this is a Decorated Factory. :]

Time to use your new extension. Open VehicleDetailViewController.swift. Towards the bottom of the file, you’ll find several methods marked @IBAction that are empty. Update goForward(), goBackward(), stopMoving() and makeNoise() as shown below to use your new extension:

@IBAction func goForward(sender: AnyObject) {
  if let vehicle = detailVehicle {
    let controller = UIAlertController.alertControllerWithTitle("Go Forward", message: vehicle.goForward())
    presentViewController(controller, animated: true) {}
  }
}

@IBAction func goBackward(sender: AnyObject) {
  if let vehicle = detailVehicle {
    let controller = UIAlertController.alertControllerWithTitle("Go Backward", message: vehicle.goBackward())
    presentViewController(controller, animated: true) {}
  }
}

@IBAction func stopMoving(sender: AnyObject) {
  if let vehicle = detailVehicle {
    let controller = UIAlertController.alertControllerWithTitle("Stop Moving", message: vehicle.stopMoving())
    presentViewController(controller, animated: true) {}
  }
}

@IBAction func makeNoise(sender: AnyObject) {
  if let vehicle = detailVehicle {
    let controller = UIAlertController.alertControllerWithTitle("Make Some Noise!", message: vehicle.makeNoise())
    presentViewController(controller, animated: true) {}
  }
}

The if let statement makes sure that vehicle exists, and if it does, creates an alert controller using your extension and presents it.

Build and run your application; after selecting a vehicle, press any button except the “Turn…” button, and you’ll see the appropriate message for each instance of a Vehicle. For example, if you press the “Make Some Noise!” button for various Vehicles, you’ll see the following:

Make some noise!

To implement the turn() method, you’ll need to pass some information from the UIAlertController back to your view controller. You can do this with Swift closures. You can think of a closure as an unnamed function similar to a block in Objective-C. First, go back to UIAlertController+Convenience.swift and add the following method:

class func alertControllerWithNumberInput(#title:String, message:String, buttonTitle:String, handler:(Int?)->Void) -> UIAlertController {
  let controller = UIAlertController(title: title, message: message, preferredStyle: .Alert)
  
  controller.addTextFieldWithConfigurationHandler { $0.keyboardType = .NumberPad }
  
  controller.addAction(UIAlertAction(title: "Cancel", style: .Cancel, handler: nil))
  
  controller.addAction(UIAlertAction(title: buttonTitle, style: .Default) { action in
    let textFields = controller.textFields as? [UITextField]
    let value = textFields?[0].text.toInt()
    handler(value)
    } )
  
  return controller
}

This method configures a UIAlertController with a text field that takes numeric input from the keypad and returns it. When the button is pressed, it attempts to parse the text into an Int. The value is an optional type because parsing might fail. This value is passed to a handler function supplied as an argument to the function.

To use the extension, open VehicleDetailsViewController.swift file and add the implementation for turn()

@IBAction func turn(sender: AnyObject) {
  if let vehicle = detailVehicle {
    let controller = UIAlertController.alertControllerWithNumberInput(title: "Turn", message: "Enter number of degrees to turn:", buttonTitle: "Go!") {
      integerValue in
      if let value = integerValue {
        let controller = UIAlertController.alertControllerWithTitle("Turn", message: vehicle.turn(value))
        self.presentViewController(controller, animated: true, completion: nil)
      }
    }
    presentViewController(controller, animated: true) {}
  }
}

This sets a controller with numeric input is created via your extension. Your handler checks if you have a valid value and then throws up another alert view with the turn message.

Build and run your project; select a Vehicle from the list, tap the Turn button and enter a number of degrees to turn, like so:

Turn 90 degrees

If you hit Cancel, nothing will happen. However, if you hit Go!, the first UIAlertController disappears and the following UIAlertController will appear:

Turn complete!

Your application is now functionally complete. However, what if you want to make your code a little more elegant so it’s easier to maintain and add to it later? It’s time to refactor how your objects are initialized and learn about some more object-oriented design patterns to make your coding life easier!

Contributors

Ray Fix

Author

Over 300 content creators. Join our team.