> ## Content Index
> Fetch the complete content index at: https://williamboles.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Finding Hope in Custom Alerts
- URL: https://williamboles.com/finding-hope-with-custom-alerts/
- Published: 2020-05-26T08:18:59.000Z
- Updated: 2026-09-26T20:53:32.000Z
- Description: Alerts form the backbone of many user interactions. While there have been changes to alerts over the years, very little has changed about their appearance or our ability to customise that appearance leading to many tear-filled designer outbursts 😱. Here, we look at how to ease that pain.
- Author: William Boles
- Tags: UIKit, Alerts

[UIAlertController](https://developer.apple.com/documentation/uikit/uialertcontroller?ref=williamboles.com) alerts form the backbone of many interactions between our users and our apps. Alerts are often shown at those critical points in our apps where we request confirmation of an action or permission to access a resource. While there have been some changes to alerts over the years, their appearance has remained largely unchanged. This lack of customisation presents *significant* difficulties for app designers 😱. Having an `UIAlertController` alert pop up at the most critical points of our apps with its semi-transparent rectangular layout and standard fonts, breaking the app's theme is enough to make any app designer spill their [flat white coffee](https://en.wikipedia.org/wiki/Flat%5Fwhite?ref=williamboles.com) down their checkered shirt and throw their [Caran d'Ache](https://en.wikipedia.org/wiki/Caran%5Fd%27Ache%5F%28company%29?ref=williamboles.com) coloured pencils across the table with an angst that few could ever comprehend, never mind experience. At this point, as you gaze into their tear-filled eyes, you offer a few comforting words:

**"We can always write our own custom alerts instead"**

Slowly, as your words start to cut through their anguish, they start to nod, and with strength returning to their voice, they say:

**"Sounds good man, I'll get started on those right away"**

And with that, you turn away and start thinking about how you can present those custom alerts.

![A photo of a designer sitting, drinking a flat white and thinking about beard oil](https://storage.ghost.io/c/b9/b4/b9b42214-5933-4584-8e56-9ee8ca21d23b/content/images/size/w1000/2020/05/photo-of-designer-sitting-on-cliff-edge.jpg)

This post will look at how to build a custom alert presenter that adheres to most of the conventions used by `UIAlertController` alerts. Along the way, we will even overcome a particularly tricky and challenging-to-reproduce navigation bug 💪.

> This post will gradually build up to a working example; however, if you are too excited and want to jump ahead, then head on over to the [completed example](https://github.com/wibosco/CustomAlert-Example?ref=williamboles.com) and take a look at `AlertPresenter` and `AlertWindow` to see how things end up.

## Thinking About Being Standard

A standard `UIAlertController` alert has four parts:

1. Foreground view.
2. Background view.
3. Presentation animation.
4. Dismissal animation.

The first thing to decide is:

"What is part of the custom alert, and what is part of the mechanism to show an alert?"

Let's look at how `UIAlertController` handles the four parts of an alert:

1. Foreground view - `UIAlertController` allows some customisation.
2. Background view - `UIAlertController` handles this for us.
3. Presentation animation - `UIAlertController` handles this for us.
4. Dismissal animation - `UIAlertController` handles this for us.

The only customisable part of an `UIAlertController` alert is the foreground view. This lack of control over the other parts may initially feel limiting, but by preventing customisation of three of the four parts, iOS forces us to focus on the most critical part - the message. The message is contained in the foreground view.

Just like `UIAlertController`, the alert presentation layer we will build below will only allow the foreground view to be customisable. This limitation will ensure that presenting and dismissing alerts will happen consistently across the app. Instead of the foreground view being a `UIView` instance, it will be a `UIViewController` instance to provide greater control to our custom alerts. This `UIViewController` instance will be added to the view hierarchy as a child view-controller. This functionality will come together in the following class structure:

![Class diagram showing how custom alerts are presented](https://storage.ghost.io/c/b9/b4/b9b42214-5933-4584-8e56-9ee8ca21d23b/content/images/size/w1000/2020/05/custom-alert-class-diagram-3.jpg)

- `AlertPresenter` is the entry point for presenting and dismissing alerts.
- `AlertWindow`, as we will see shortly, each alert is presented in its own `UIWindow` instance. Used to overcome that tricky navigation issue we spoke about above.
- `HoldingViewController`, the window's root view-controller that is responsible for presenting the `AlertContainerViewController` that will hold the alert view-controller as a child view-controller.
- `AlertContainerViewController`, the parent view-controller that the alert view-controller is embedded in as a child view-controller.
- `CustomAlertPresentAnimationController` is responsible for presenting the `AlertContainerViewController` instance with the same animation as a standard `UIAlertController`.
- `CustomAlertDismissAnimationController` is responsible for dismissing the `AlertContainerViewController` instance with the same animation as a standard `UIAlertController`.

`HoldingViewController`, `AlertContainerViewController`, `CustomAlertPresentAnimationController` and `CustomAlertDismissAnimationController` are private and only known to `AlertWindow`.

> Don't worry if that doesn't all make sense yet; we will look into each class in greater depth below.

Let's start with `AlertPresenter`:

```
class AlertPresenter {
    static let shared = AlertPresenter()

    // MARK: - Present

    func presentAlert(_ viewController: UIViewController) {
        os_log(.info, "Alert being presented")

        //TODO: Present
    }
}
```

`AlertPresenter` is a singleton, so the same instance will be used to present (and dismiss) all alerts. As `AlertPresenter` isn't a `UIViewController` subclass, it's not possible to directly present the alert. Instead, we are going to use a dedicated `UIWindow` instance to present alerts from. Using a dedicated `UIWindow` instance should avoid the situation where multiple simultaneous navigation events (presentation/dismissal) occur at the same time, resulting in one of those events being cancelled, and the following error is generated:

![Screenshot showing a navigation collision between two view-controllers preventing one of the events from occurring.](https://storage.ghost.io/c/b9/b4/b9b42214-5933-4584-8e56-9ee8ca21d23b/content/images/size/w1000/2020/05/screenshot_of_navigation_collision-1.jpg)

The navigation stack in one window is independent of the navigation stack of any other windows. An alert in the process of being presented on window A will not cause a navigation collision with a view-controller being pushed on window B 🥳.

Before delving into how to use a dedicated window to present alerts, let's get to know windows better.

> If you are comfortable with how `UIWindow` works, feel free to [skip ahead](#usingwindows).

## Getting to Know Windows 💭

`UIWindow` is a subclass of `UIView` that acts as the container for an app's visible content - it is the top of the view hierarchy. All views that are displayed to the user need to be added to a window. An app can have multiple windows, but only windows that are *visible* can have their content displayed to the user - by default, windows are not visible. Multiple windows can be visible at once. Each window's UI stack is independent of other windows UI stacks. Where a window is displayed in relation to other visible windows is controlled by setting that window's `windowLevel` property. The higher the `windowLevel`, the *nearer* to the user that window is. `UIWindow` has three default levels:

1. `.normal`
2. `.statusBar`
3. `.alert`

With `.alert` \> `.statusBar` \> `.normal`. If a more fine-grain level of control is needed, it's possible to use a custom level:

```
window.windowLevel = .normal + 25
```

> As of iOS 13: `.alert` has a raw value of 2000, `.statusBar` has a raw value of 1000 and `.normal` has a raw value of 0.

Where two or more windows have the same level, their ordering is determined by the order they were made visible in - the last window made visible is *nearest* one to the user.

It's unusual to add subviews to a window directly; instead, each window should have a root view-controller whose view is used as the window's initial subview.

As well as displaying content, `UIWindow` is also responsible for forwarding any [events](https://developer.apple.com/documentation/uikit/uievent/eventtype?ref=williamboles.com) (touch, motion, remote-control or press) to interested parties in its [responder chain](https://swiftrocks.com/understanding-the-ios-responder-chain.html?ref=williamboles.com). While all touch events are forwarded to the responder chain of the window that the event occurred on, events that are outside of the app's UI, such as motion events or keyboard entry, are forwarded to the *key* window. Only one window can be *key* at any one given time (which window is *key* can change throughout the lifetime of the app).

An iOS app needs at least one window; a reference to this window can be found in the app delegate:

```
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    //Omitted methods
}
```

If you're using storyboards to lay out your interface, then most of the work of setting up this window is happening under the hood. When the app is launched, a `UIWindow` instance is created that fills the screen. This window is then assigned to the `window` property (declared in the `UIApplicationDelegate` protocol), configured with the view-controller declared as the *storyboard entry point* from the project's main storyboard and made **key and visible**.

If you are not using storyboards, you can better see this setup:

```
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    // MARK - AppLifecycle

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        window = UIWindow()
        window?.rootViewController = YourViewController()
        window?.makeKeyAndVisible()

        return true
    }
}
```

## Using a Dedicated Window

As this new window will only display alerts, let's subclass `UIWindow` for this single purpose:

```
class AlertWindow: UIWindow {

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        super.init(frame: UIScreen.main.bounds)

        // 1
        rootViewController = viewController

        // 2
        windowLevel = .alert
    }

    @available(*, unavailable)
    required init?(coder aDecoder: NSCoder) {
        fatalError("Unavailable")
    }

    // MARK: - Present

    // 3
    func present() {
        makeKeyAndVisible()
    }
}
```

In `AlertWindow` we:

1. Set the alert as the window's `rootViewController`.
2. Set the window level value to `.alert`. The `.alert` level will put this window above the app's main window, ensuring that it can be seen.
3. `present()` is a bit of syntactic sugar around making the window key and visible; it will act as a mirror to the soon-to-be-seen dismiss method.

> An `AlertWindow` instance is only meant to show one alert and then be disposed of.

Let's use `AlertWindow` to present alerts:

```
class AlertPresenter {
    // 1
    private var alertWindows = Set()

    //Omitted other properties

    // MARK: - Present

    // 2
    func presentAlert(_ viewController: UIViewController) {
        os_log(.info, "Alert being presented")

        let alertWindow = AlertWindow(withViewController: viewController)
        alertWindow.present()

        alertWindows.insert(alertWindow)
    }
}
```

With the above changes, we:

1. Store all windows that are being presented in a [Set](https://developer.apple.com/documentation/swift/set?ref=williamboles.com). Storing the window inside the set will keep that window alive by increasing its retain count (as making a window key-and-visible doesn't increase the retain count).
2. Create an `AlertWindow` instance using the alert to be presented and then instruct that window to present itself.

If you were to create an instance of `AlertWindow` and make it visible, you would notice that the alert is presented without an animation - it just appears. A window's root view-controller cannot be animated on-screen, so an intermediate view-controller is needed, which can be the window's root view-controller, and then the alert can be presented from that view-controller:

```
class HoldingViewController: UIViewController {
    private let viewController: UIViewController

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        self.viewController = viewController
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - ViewLifecycle

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        present(viewController, animated: true, completion: nil)
    }
}
```

`HoldingViewController` only has one responsibility - presenting an alert once `viewDidAppear(_:)` has been called.

> Trying to present an alert earlier will cause a navigation collision due to the `HoldingViewController` instance having not finished its own "animation" on screen. While (at the time of writing - iOS 13) this doesn't seem to affect the actual presentation of the alert, waiting for `HoldingViewController` to be presented before attempting another presentation ensures that the error isn't produced.

Let's go back to `AlertWindow` and make use of `HoldingViewController`:

```
class AlertWindow: UIWindow {
    private let holdingViewController: HoldingViewController

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        holdingViewController = HoldingViewController(withViewController: viewController)
        super.init(frame: UIScreen.main.bounds)

        rootViewController = holdingViewController

        windowLevel = .alert
    }

    // MARK: - Present

    func present() {
        makeKeyAndVisible()
    }
}
```

If you run the project with the above changes and hook in your own alert, you would notice two issues:

1. Sizing - the alert occupies the full-screen.
2. Presentation - the alert is animated from bottom to top.

## Sizing Alerts

An alert should be shown at the smallest size possible - we can't do this if that alert *is* the modal. Instead, we need to embed that alert into another view controller. `HoldingViewController` can't be used for this as it is being used as the window's root view-controller so it can't be animated on-screen. We need to introduce a new view-controller that will act as a container so that the container can be animated on-screen from the `HoldingViewController` instance:

```
class AlertContainerViewController: UIViewController {
    let childViewController: UIViewController

    // MARK: - Init

    // 1
    init(withChildViewController childViewController: UIViewController) {
        self.childViewController = childViewController
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // MARK: - ViewLifecycle

    override func viewDidLoad() {
        super.viewDidLoad()

        // 2
        addChild(childViewController)
        view.addSubview(childViewController.view)
        childViewController.didMove(toParent: self)

        // 3
        childViewController.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            childViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            childViewController.view.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            childViewController.view.widthAnchor.constraint(lessThanOrEqualTo: view.widthAnchor, multiplier: 1),
            childViewController.view.heightAnchor.constraint(lessThanOrEqualTo: view.heightAnchor, multiplier: 1)
        ])
    }
}
```

In `AlertContainerViewController`, we:

1. Store the alert passed in via the init'er as a property.
2. Embed the alert as a child view-controller.
3. Centre the alert and constrain it to (at maximum) the width and height of the view.

> I don't view an alert that is too large for the container as being the presentation layer's problem to solve - if an alert is too large for a single screen, I'd question if it *really* is an alert.

Let's update `HoldingViewController` to use `AlertContainerViewController`:

```
class HoldingViewController: UIViewController {
    let containerViewController: AlertContainerViewController

    // MARK: - Init

    init(withViewController viewController: UIViewController) {
        // 1
        containerViewController = AlertContainerViewController(withChildViewController: UIViewController)
        super.init(nibName: nil, bundle: nil)
    }

    //Omitted other methods

    // MARK: - ViewLifecycle

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        // 2
        present(containerViewController, animated: true, completion: nil)
    }
}
```

With the changes to `HoldingViewController` we:

1. Create an `AlertContainerViewController` instance.
2. Modally present that `AlertContainerViewController` instance rather than the alert.

Now, if you run the above code with your `UIViewController` subclass, you would notice that your alert is compressed to be as small as it can be and centred on the screen.

Time to address the second point - **Presentation**.

## Presenting Alerts

Let's change the presentation animation to make it feel more like the `UIAlertController`.

As we move from one view-controller to another, we are used to seeing different types of animation, the most common being:

- Push: the new view slides in from the side on top of the current view.
- Pop: the current view slides out to the side to reveal another view underneath.
- Present: the new view slides up from the bottom on top of the current view.
- Dismiss: the current view slides down to reveal another view underneath.

These view transactions are provided free by iOS. However, it is possible to specify our own view transitions by using the [Transitions API](https://developer.apple.com/documentation/uikit/animation%5Fand%5Fhaptics/view%5Fcontroller%5Ftransitions?ref=williamboles.com). The Transitions API is a suite of protocols that determine how custom transitions should behave. There are quite a few protocols in the suite, however only two of them are of interest to us:

1. [UIViewControllerTransitioningDelegate](https://developer.apple.com/documentation/uikit/uiviewcontrollertransitioningdelegate?ref=williamboles.com) \- is a set of methods that vend objects used to manage a fixed-length or interactive transition between view controllers. Every view-controller has a `transitioningDelegate` which has a type of `UIViewControllerTransitioningDelegate`. When a transaction is about to happen, iOS asks the transitioning-delegate for an animator to use. If the `transitioningDelegate` is nil or the necessary `UIViewControllerTransitioningDelegate` method hasn't been implemented, then iOS will fall back to using the default animation for that type of transition.
2. [UIViewControllerAnimatedTransitioning](https://developer.apple.com/documentation/uikit/uiviewcontrolleranimatedtransitioning?ref=williamboles.com) \- is a set of methods for implementing the animations for a custom view controller transition. A class that conforms to `UIViewControllerAnimatedTransitioning` is known as the animator. An animator controls the duration of the transition and allows for manipulating the *from-view-controller's view* and *to-view-controller's view* on the transition canvas.

> I briefly covered the Transitions API above - if you want to read more, I'd recommend this [post](https://www.raywenderlich.com/322-custom-uiviewcontroller-transitions-getting-started?ref=williamboles.com).

Let's create an animator to handle presenting the alert:

```
class CustomAlertPresentAnimationController: NSObject, UIViewControllerAnimatedTransitioning // 1 {

    // MARK: - UIViewControllerAnimatedTransitioning

    // 2
    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.2
    }

    // 3
    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let toViewController = transitionContext.viewController(forKey: .to),
            let snapshot = toViewController.view.snapshotView(afterScreenUpdates: true)
            else {
                return
        }

        let containerView = transitionContext.containerView
        let finalFrame = transitionContext.finalFrame(for: toViewController)

        snapshot.frame = finalFrame
        snapshot.transform = CGAffineTransform(scaleX: 1.1, y: 1.1)
        snapshot.alpha = 0.0

        containerView.addSubview(toViewController.view)
        containerView.addSubview(snapshot)
        toViewController.view.isHidden = true

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, animations: {
            snapshot.alpha = 1.0
            snapshot.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
        }) { _ in
            toViewController.view.isHidden = false
            snapshot.removeFromSuperview()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}
```

`CustomAlertPresentAnimationController` can at first glance look a little intimidating, so let's break it down:

1. `CustomAlertPresentAnimationController` conforms to `UIViewControllerAnimatedTransitioning` and needs to be a subclass of `NSObject` as `UIViewControllerAnimatedTransitioning` extends `NSObjectProtocol`.
2. In `transitionDuration(using:)`, the duration of the animation is specified as `0.2` seconds - we've copied the timing of an `UIAlertController` alert here.
3. `UIAlertController` instances when animated onto the screen, gradually fade in with a slight bounce effect before settling to their actual size. In `animateTransition(using:)` we use the `UIViewControllerContextTransitioning` instance (`transitionContext`) to get the offscreen view of the *to-view-controller* (i.e. the `AlertContainerViewController` instance holding our alert). We take a snapshot (screenshot) of the `AlertContainerViewController` instance's view, add that snapshot to the animator's container view (think of this as a temporary transaction view that is present during the animation) and animate the snapshot on the screen to mimic a `UIAlertController` animation. Taking a snapshot means we avoid having to deal with any constraint issues that may arise from manipulating the actual `AlertContainerViewController` instance's view. Once the animation is finished, we remove the snapshot from the view hierarchy and reveal the `AlertContainerViewController` instance's view occupying the same position.

As `HoldingViewController` is the view-controller that presents the `AlertContainerViewController` instance, it needs to conform to `UIViewControllerTransitioningDelegate`:

```
class HoldingViewController: UIViewController, UIViewControllerTransitioningDelegate {
    //Omitted properties

    init(withViewController viewController: UIViewController) {
        //Omitted start of method

        // 1
        containerViewController.modalPresentationStyle = .custom

        // 2
        containerViewController.transitioningDelegate = self
    }

    //Omitted other methods

    // MARK: - UIViewControllerTransitioningDelegate

    // 3
    func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CustomAlertPresentAnimationController()
    }
}
```

With the changes to `HoldingViewController`, we:

1. Set the `modalPresentationStyle` to `custom` as we will be providing the modal presentation animation.
2. Set this `HoldingViewController` instance as the `transitioningDelegate` of the `AlertContainerViewController` instance.
3. Return an instance of `CustomAlertPresentAnimationController` when presenting an `AlertContainerViewController` instance.

If you add in the above code changes to your project and run it, you would now see your alert being animated onto the screen in the same way as an `UIAlertController` alert.

So we just need to add in a semi-transparent background, and our alert presentation will be complete:

```
class AlertContainerViewController: UIViewController {
    //Omitted properties and other methods

    override func viewDidLoad() {
        super.viewDidLoad()

        let backgroundView = UIView()
        backgroundView.backgroundColor = UIColor.darkGray.withAlphaComponent(0.75)
        backgroundView.translatesAutoresizingMaskIntoConstraints = false

        view.addSubview(backgroundView)

        //Omitted child view-controller setup

        NSLayoutConstraint.activate([
            //Omitted child view-controller layout setup

            backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            backgroundView.topAnchor.constraint(equalTo: view.topAnchor),
            backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
        ])
    }
}
```

Now our alert presentation is complete; let's turn our attention to dismissing that alert.

## Dismissing Alerts

Just like presentation, dismissal will happen inside the `AlertPresenter`:

```
class AlertPresenter {
    //Omitted properties and other methods

    func dismissAlert(_ viewController: UIViewController) {
        // 1
        guard let alertWindow = alertWindows.first(where: { $0.viewController == viewController } )  else {
            return
        }

        os_log(.info, "Alert being dismissed")

        // 2
        alertWindow.dismiss { [weak self] in
            // 3
            self?.alertWindows.remove(alertWindow)
        }
    }
}
```

With the changes to `AlertPresenter`, we:

1. Find the window that is showing the alert.
2. Call `dismiss(completion:)` on that window to begin the dismissal process.
3. Remove the window from `alertWindows` once the dismissal has completed.

> There are a few ways we could have implemented the dismissal logic. In the end, I decided to require all custom alerts to call `dismissAlert(_:)` when they are ready to be dismissed. This direct calling approach has symmetry with how the alert is presented and is very simple.

If you try to run the above method, you will get an error because `AlertWindow` doesn't yet have an `viewController` property or `dismissAlert(_:)` method, so let's add them in:

```
class AlertWindow: UIWindow {
    // 1
    var viewController: UIViewController {
        return holdingViewController.containerViewController.childViewController
    }

    //Omitted other methods and properties

    // MARK: - Dismiss

    func dismiss(completion: @escaping (() -> Void)) {
        // 2
        holdingViewController.dismissAlert { [weak self] in
            // 3
            self?.resignKeyAndHide()
            completion()
        }
    }

    // MARK: - Resign

    private func resignKeyAndHide() {
        resignKey()
        isHidden = true
    }
}
```

With the above changes to `AlertWindow`, we:

1. Added a new `viewController` property that gets the alert being displayed and returns it.
2. Pass the dismissal command onto the `HoldingViewController` instance.
3. Resign the window and hide it once the dismissal has completed.

> Normally, I don't like the level of [method chaining](https://refactoring.guru/smells/message-chains?ref=williamboles.com) shown inside the `viewController` property. However, I feel comfortable doing it here as `HoldingViewController` and `AlertContainerViewController` are private implementation details of `AlertWindow`.

Let's add a `dismissAlert(_:)` method into `HoldingViewController`:

```
class HoldingViewController: UIViewController, UIViewControllerTransitioningDelegate {
    //Omitted properties and other methods

    // MARK: - Dismiss

    func dismissAlert(completion: @escaping (() -> Void)) {
        containerViewController.dismiss(animated: true, completion: {
            completion()
        })
    }
}
```

With the above change, we call the standard UIKit [dismiss(animation:completion:)](https://developer.apple.com/documentation/uikit/uiviewcontroller/1621505-dismiss?ref=williamboles.com) on the `containerViewController` and trigger the completion closure when that dismiss action completes.

If you add the above code changes to your project and run it by calling `AlertPresenter.shared.dismiss(completion:)` when your alert's dismissal button is pressed, then your alert should be dismissed. However, it will still be using the standard modal dismissal animation. Just like with presenting, dismissing will need an animator:

```
class CustomAlertDismissAnimationController: NSObject, UIViewControllerAnimatedTransitioning {

    // MARK: - UIViewControllerAnimatedTransitioning

    func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
        return 0.2
    }

    func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
        guard let fromViewController = transitionContext.viewController(forKey: .from),
            let snapshot = fromViewController.view.snapshotView(afterScreenUpdates: true)
            else {
                return
        }

        let containerView = transitionContext.containerView
        let finalFrame = transitionContext.finalFrame(for: fromViewController)

        snapshot.frame = finalFrame

        containerView.addSubview(snapshot)
        fromViewController.view.isHidden = true

        let duration = transitionDuration(using: transitionContext)

        UIView.animate(withDuration: duration, animations: {
            snapshot.alpha = 0.0
        }) { _ in
            snapshot.removeFromSuperview()
            transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
        }
    }
}
```

`CustomAlertDismissAnimationController` is mostly the opposite of `CustomAlertPresentAnimationController` but without the bounce effect.

Now `HoldingViewController` just has to return an `CustomAlertDismissAnimationController` instance at the appropriate moment:

```
class HoldingViewController: UIViewController, UIViewControllerTransitioningDelegate {
    //Omitted properties and other methods

    func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
        return CustomAlertDismissAnimationController()
    }
}
```

And that completes our custom alert presenter.

## 🍾💃🏻🕺🏻

## Feeling Hopeful? 🌞

To recap, we built a simple custom alert presentation system that takes an alert view-controller, places it in the centre of a semi-transparent container and presents it in a dedicated window, all the while doing so with the same feel as presenting a `UIAlertController` instance.

I spent quite a long time toying with the idea of having `AlertPresenter` accept view-models rather than view-controllers (with the view-model then being transformed into view-controllers inside the presenter). However, the view-model solution always ended up feeling very confused - was `AlertPresenter` part of the app's infrastructure or part of the app's UI? By using view-models `AlertPresenter` had to be both 😧. Each time someone created a new type of alert, `AlertPresenter` would need to be modified to know about the new view-model - breaking the [open/closed principle](https://en.wikipedia.org/wiki/Open%E2%80%93closed%5Fprinciple?ref=williamboles.com) and opening the door to unexpected consequences rippling through the app (you can see this approach on this [branch](https://github.com/wibosco/CustomAlert-Example/tree/using%5Fviewmodels%5Fand%5Fenums%5Fto%5Fpresent%5Falerts?ref=williamboles.com)) via the `AlertPresenter`. By moving the alert view-controller creation from `AlertPresenter` to the component that wanted to use `AlertPresenter`, I was able to give `AlertPresenter` a clear purpose: `AlertPresenter` takes a view-controller and presents it in the same manner as an `UIAlertController` alert. This clear division of responsibility between alert creator and alert presenter, I believe, has meant that `AlertPresenter` is a simple, easy-to-understand component that should very rarely need to be modified.

To see the above code snippets together in a working example, head over to the [repository](https://github.com/wibosco/CustomAlert-Example?ref=williamboles.com) and clone the project.

> With alerts looking this good, one just isn't enough - see [Alert Queuing with Windows](https://williamboles.com/alert-queuing-with-windows/) on how you can elegantly queue alerts to ensure the user never misses one.