Chapters

Hide chapters

Design Patterns by Tutorials

Third Edition · iOS 13 · Swift 5 · Xcode 11

17. Facade Pattern
Written by Joshua Greene

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.

You’ll implement part of the ordering system mentioned above. Specifically, you’ll create an OrderFacade that allows a user to place an order.

Enter the following after Code example:

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

Here you define two simple models: a Customer represents a user that can place an order, and a Product sold by the system. You make both of these types conform to Hashable to enable you to use them as keys within a dictionary.

Next, add the following to the end of the playground:

public class InventoryDatabase {
  public var inventory: [Product: Int] = [:]

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

public class ShippingDatabase {
  public var pendingShipments: [Customer: [Product]] = [:]
}

First, you declare InventoryDatabase. This is a simplified version of a database that stores available inventory, which represents the number of items available for a given Product.

You also declare ShippingDatabase. This is likewise a simplified version of a database that holds onto pendingShipments, which represents products that have been ordered but not yet shipped for a given Customer. In a complex system, you’d also likely define a CustomerDatabase, BillingDatabase and more. To keep this example simple, however, you’ll omit these elements.

Add the following to the end of the playground:

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

Here, you declare OrderFacade and add two properties, inventoryDatabase and shippingDatabase, which you pass into this via its initializer, init(inventoryDatabase:shippingDatabase:).

Next, add the following method to the end of the OrderFacade class you just added:

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

This is a simple method that consumers of the facade will call to place orders for a given Product and Customer.

Here’s what the code does:

  1. You first print the product.name and customer.name to the console.

  2. Before fulfilling the order, you guard that there’s at least one of the given product in the inventoryDatabase.inventory. If there isn’t any, you print that the product is out of stock.

  3. Since there’s at least one of the product available, you can fulfill the order. You thereby reduce the count of the product in inventoryDatabase.inventory by one.

  4. You then add the product to the shippingDatabase.pendingShipments for the given customer.

  5. Finally, you print that the order was successfully placed.

Great, you’re ready to try out the facade! Add the following code at the end of the playground:

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

Here’s what this does:

  1. First, you set up two products. rayDoodle are drawings from Ray, and vickiPoodle is a prized pet poodle by Vicki. Don’t even get me started about the poodle doodles!

  2. Next, you create inventoryDatabase using the products. There are a lot of rayDoodles (he likes to doodle, apparently) and only one vickiPoodle. It’s her prized poodle, after all!

  3. Then, you create the orderFacade using the inventoryDatabase and a new ShippingDatabase.

  4. Finally, you create a customer and call orderFacade.placeOrder(for:by:). Naturally, of course, your order is for Vicki’s prized poodle. It’s expensive, but it’s worth it!

You should see the following printed to the console:

Place order for 'Vicki's prized poodle' by 'Johnny Appleseed'
Order placed for 'Vicki's prized poodle' by 'Johnny Appleseed'

Doodles and poodles aside, you’ve just created a nice start for an ordering system!

What should you be careful about?

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

It’s okay to create more than one facade for different use cases. For example, if you notice a facade has functionality that some classes use and other functionality that other classes use, consider splitting it into two or more facades.

Tutorial project

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

If you skipped the previous chapter, or you want a fresh start, open Finder and navigate to where you downloaded the resources for this chapter. Then, open starter\MirrorPad\MirrorPad.xcodeproj in Xcode.

You’ll implement a share button in this chapter and make use of a facade. To keep the focus on the design pattern, and to save you a lot of typing, the facade’s dependencies have been provided for you.

Open Finder and navigate to wherever you downloaded the resources for this chapter. Alongside the Starter and Final directories, you’ll see a Resources directory that contains DrawingSelectionViewController.swift, DrawingSelectionViewController.xib and ImageRenderer.swift.

Position the Finder window above Xcode and drag and drop DrawingSelectionViewController.swift and ImageRenderer.swift into the app’s Facades group.

When prompted, check the option for Copy items if needed and press Finish to add the files.

Finally, drag and drop DrawingSelectionViewController.xib into the Views group. Again, select Copy items if needed and press Finish to add the file.

You’ll use DrawingSelectionViewController to display an OutlineView over the existing ViewController. It has a button to toggle the selected drawing between the inputDrawView and entireDrawView and a “Share” button to share the selection.

You’ll pass the selected view to ImageRenderer to convert it into a UIImage. You’ll then use the resulting image to create a UIActivityViewController.

UIActivityViewController is actually an Apple-provided facade! It provides a simple interface to share strings, images and other media with iCloud, iMessage, Twitter and other apps that are available on the device.

Your job is to create a new facade called ShareFacade. You’ll use this to provide a simple interface to allow consumers to select which view to share, turn the view into an image and share this via whichever app the user chooses.

Thereby, ShareFacade will coordinate between DrawingSelectionViewController, ImageRenderer and UIActivityViewController to accomplish this goal.

You’ve got the mission brief down, so it’s time to code!

Create a new Swift File called ShareFacade.swift within the app’s Facades group and replace its contents with the following:

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

  }
}

Let’s take a look at what is going on here:

  1. First you declare instance variables for entireDrawing, inputDrawing and parentViewController. In order to prevent a strong reference cycle, you denote each property as unowned.

  2. Next, you declare a property for imageRenderer, which you’ll use later.

  3. Next, you create an initializer to set each of the unowned properties.

  4. Finally, you stub out a method for presentShareController, which ultimately consumers will call to present the share controller to select which view to share, convert the view to an image and share it.

Before you can implement presentShareController(), you’ll need to make ShareFacade conform to DrawingSelectionViewControllerDelegate, which is required by DrawingSelectionViewController.

Still in ShareFacade.swift, add the following to the bottom of the file after the closing class curly brace:

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

Here’s what this does:

  1. drawingSelectionViewControllerDidCancel is called whenever the user presses the Cancel button to abort sharing. In this case, you tell parentViewController to dismiss its currently displayed view controller with an animation.

  2. drawingSelectionViewController(_:didSelectView:) is called whenever the user presses the Share button to select a view to share. In this case, you first tell parentViewController to dismiss its current view controller without an animation.

    Next, you immediately create an image from the given view by passing this to imageRenderer.

    In turn, you use this view to create a UIActivityViewController, which parentViewController presents using an animation.

    Ultimately, this will have the nice effect of immediately hiding the DrawingSelectionViewController and animating in the new UIActivityViewController.

Next, add the following code inside presentShareController():

// 1
let selectionViewController =
  DrawingSelectionViewController.createInstance(
    entireDrawing: entireDrawing,
    inputDrawing: inputDrawing,
    delegate: self)

// 2
parentViewController.present(selectionViewController,
                             animated: true)

This code is fairly straightforward:

  1. You first create a new DrawingSelectionViewController instance called selectionViewController using a convenience class constructor method, createInstance(entireDrawing:inputDrawing:delegate).

    If you inspect this method within DrawingSelectionViewController.swift, you’ll see it creates a new view controller instance by calling DrawingSelectionViewController(nibName: nil, bundle: nil), sets the modalPresentationStyle and modalTransitionStyle, sets the passed-in variables as properties on the new instance, and returns the view controller.

  2. Finally, you tell parentViewController to present the selectionViewController with an animation.

Your ShareFacade is all set up and ready to be used!

Open ViewController.swift and add the following right after the opening class curly brace:

// MARK: - Properties
public lazy var shareFacade: ShareFacade =
  ShareFacade(entireDrawing: drawViewContainer,
              inputDrawing: inputDrawView,
              parentViewController: self)

Here, you create a new property called shareFacade. Since you pass self as the parentViewController, you make this a lazy property to ensure that the ViewController itself is fully created first.

Lastly, add the following inside sharePressed(_:):

shareFacade.presentShareController()

With this single line, you’ve added sharing capabilities to ViewController. Aren’t facades great?

Build and run the app. Draw several lines in the top-left view, press the Share button, and you’ll be presented with the DrawingSelectionViewController.

Press the red Share button, and you’ll see the UIActivityViewController, where you can pick an app to use to share the image.

If you’re using the simulator, you’ll only see a few apps available. If you use a real device instead, you’ll see several more depending on the apps you have installed.

Key points

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

  • The facade pattern provides a simple interface to a complex system. It involves two types: the facade and its dependencies.

  • The facade provides simple methods to interact with the system. Behind the scenes, it owns and interacts with its dependencies, each of which performs a small part of a complex task.

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.

Mirror Pad has come a long way, and the code is much more maintainable! There’s still a lot of functionality you can add:

  • Color and brush stroke selection
  • Undo and redo functionality
  • Saving and loading drawings in the app

Each of these are possible using the existing patterns you’ve learned in the Intermediate and Fundamental Design Patterns sections. Feel free to continue building out Mirror Pad as much as you like.

But you still have more learning to do. Continue onto the next section to learn about advanced design patterns, including mediator, composite, command and more!

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.
© 2026 Kodeco Inc.