Home iOS & Swift Books Design Patterns by Tutorials

17
Facade Pattern Written by Joshua Greene

Heads up... You're reading this book for free, with parts of this chapter shown beyond this point as scrambled text.

You can unlock the rest of this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

The facade pattern is a structural pattern that provides a simple interface to a complex system. It involves two types:

  1. The facade provides simple methods to interact with the system. This allows consumers to use the facade instead of knowing about and interacting with multiple classes in the system.

  2. The dependencies are objects owned by the facade. Each dependency performs a small part of a complex task.

When should you use it?

Use this pattern whenever you have a system made up of multiple components and want to provide a simple way for users to perform complex tasks.

For example, a product ordering system involves several components: customers and products, inventory in stock, shipping orders and others.

Instead of requiring the consumer to understand each of these components and how they interact, you can provide a facade to expose common tasks such as placing and fulfilling a new order.

Playground example

Open IntermediateDesignPatterns.xcworkspace in the Starter directory, and then open the Facade page.

import Foundation

// MARK: - Dependencies
public struct Customer {
  public let identifier: String
  public var address: String
  public var name: String
}

extension Customer: Hashable {

  public func hash(into hasher: inout Hasher) {
    hasher.combine(identifier)
  }

  public static func ==(lhs: Customer,
                        rhs: Customer) -> Bool {
    return lhs.identifier == rhs.identifier
  }
}

public struct Product {
  public let identifier: String
  public var name: String
  public var cost: Double
}

extension Product: Hashable {

  public func hash(into hasher: inout Hasher) {
    hasher.combine(identifier)
  }

  public static func ==(lhs: Product,
                        rhs: Product) -> Bool {
    return lhs.identifier == rhs.identifier
  }
}
public class InventoryDatabase {
  public var inventory: [Product: Int] = [:]

  public init(inventory: [Product: Int]) {
    self.inventory = inventory
  }
}

public class ShippingDatabase {
  public var pendingShipments: [Customer: [Product]] = [:]
}
// MARK: - Facade
public class OrderFacade {
  public let inventoryDatabase: InventoryDatabase
  public let shippingDatabase: ShippingDatabase

  public init(inventoryDatabase: InventoryDatabase,
              shippingDatabase: ShippingDatabase) {
    self.inventoryDatabase = inventoryDatabase
    self.shippingDatabase = shippingDatabase
  }
}
public func placeOrder(for product: Product,
                       by customer: Customer) {
  // 1
  print("Place order for '\(product.name)' by '\(customer.name)'")

  // 2
  let count = inventoryDatabase.inventory[product, default: 0]
  guard count > 0 else {
    print("'\(product.name)' is out of stock!")
    return
  }

  // 3
  inventoryDatabase.inventory[product] = count - 1

  // 4
  var shipments =
    shippingDatabase.pendingShipments[customer, default: []]
  shipments.append(product)
  shippingDatabase.pendingShipments[customer] = shipments

  // 5
  print("Order placed for '\(product.name)' " +
    "by '\(customer.name)'")
}
// MARK: - Example
// 1
let rayDoodle = Product(
  identifier: "product-001",
  name: "Ray's doodle",
  cost: 0.25)

let vickiPoodle = Product(
  identifier: "product-002",
  name: "Vicki's prized poodle",
  cost: 1000)

// 2
let inventoryDatabase = InventoryDatabase(
  inventory: [rayDoodle: 50, vickiPoodle : 1]
)

// 3
let orderFacade = OrderFacade(
  inventoryDatabase: inventoryDatabase,
  shippingDatabase: ShippingDatabase())

// 4
let customer = Customer(
  identifier: "customer-001",
  address: "1600 Pennsylvania Ave, Washington, DC 20006",
  name: "Johnny Appleseed")

orderFacade.placeOrder(for: vickiPoodle, by: customer)
Place order for 'Vicki's prized poodle' by 'Johnny Appleseed'
Order placed for 'Vicki's prized poodle' by 'Johnny Appleseed'

What should you be careful about?

Be careful about creating a “god” facade that knows about every class in your app.

Tutorial project

You’ll continue the Mirror Pad app from the previous chapter.

import UIKit

public class ShareFacade {

  // MARK: - Instance Properties
  // 1
  public unowned var entireDrawing: UIView
  public unowned var inputDrawing: UIView
  public unowned var parentViewController: UIViewController

  // 2
  private var imageRenderer = ImageRenderer()

  // MARK: - Object Lifecycle
  // 3
  public init(entireDrawing: UIView,
              inputDrawing: UIView,
              parentViewController: UIViewController) {
    self.entireDrawing = entireDrawing
    self.inputDrawing = inputDrawing
    self.parentViewController = parentViewController
  }

  // MARK: - Facade Methods
  // 4
  public func presentShareController() {

  }
}
// MARK: - DrawingSelectionViewControllerDelegate
extension ShareFacade: DrawingSelectionViewControllerDelegate {

  // 1
  public func drawingSelectionViewControllerDidCancel(
    _ viewController: DrawingSelectionViewController) {
    parentViewController.dismiss(animated: true)
  }

  // 2
  public func drawingSelectionViewController(
    _ viewController: DrawingSelectionViewController,
    didSelectView view: UIView) {

    parentViewController.dismiss(animated: false)
    let image = imageRenderer.convertViewToImage(view)

    let activityViewController = UIActivityViewController(
      activityItems: [image],
      applicationActivities: nil)
    parentViewController.present(activityViewController,
                                 animated: true)
  }
}
// 1
let selectionViewController =
  DrawingSelectionViewController.createInstance(
    entireDrawing: entireDrawing,
    inputDrawing: inputDrawing,
    delegate: self)

// 2
parentViewController.present(selectionViewController,
                             animated: true)
// MARK: - Properties
public lazy var shareFacade: ShareFacade =
  ShareFacade(entireDrawing: drawViewContainer,
              inputDrawing: inputDrawView,
              parentViewController: self)
shareFacade.presentShareController()

Key points

You learned about the facade pattern in this chapter. Here are its key points:

Where to go from here?

Congratulations on making it to the end of the Intermediate section! If you’ve worked through all of the chapters so far, you now know the majority of the design patterns used in iOS.

Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum here.

Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:

© 2021 Razeware LLC

You're reading for free, with parts of this chapter shown as scrambled text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.