How to refactor without hurting yourself

Refactoring from a UIAlertController to a Custom Alert ViewController

… and passing a closure

During this article, we’ll learn:

  • A bit about refactoring thoughtfully
  • How to make a custom Alert View
  • How to get a result that happens at an unknown time wherever you need it (such as a button press or from a network call).

We’ll also talk about what a closure is, use them, and pass them around our code like a hot potato.

I was recently working on a project that we had taken great care and time in customizing the UI. But when an alert would pop up on the screen, the difference between our custom UI with bright colors, and this grey box on the screen with a systemBlue button or 2… well… 🤢

What we wanted was a way to give our users the feeling they were still having the our app name here experience. But subclassing UIAlertController isn’t an option.

According to Apple’s Developer site:
“The UIAlertController class is intended to be used as-is and does not support subclassing. The view hierarchy for this class is private and must not be modified.”

This isn’t a particularly difficult task or anything, but we were already 99% of the way finished with the app before it dawned on me that this wasn’t great UX. We’re using alerts to present some of the most important information to users — yet doing it in a lackluster way. In some apps it works, because it stands out, and it’s familiar to users. But in our case, it was just making things look dull.

We were using alerts all throughout our code to display errors and important messages to users. To do this in a clean way, we were using an extension on UIViewController (since that’s the only place we care to display alerts). This small extension keeps us from recreating procedural/boilerplate code throughout our app, and as you’ll see greatly aids in refactoring.

and at the call site…

For fun, when I start refactoring, I always like to see what the worst-case looks like. To do this, I usually comment out the method(s) I’m refactoring, build the project, and see how many errors there are. This simulates me completely refactoring the method names and parameters in order to see how much work I’ll have at the call site(s) if I use this approach.

When I build the project in question after commenting-out these methods, I’m left with 52 errors. Obviously, I’m going to aim not to change the parameters and what the methods are doing — I essentially just want to display a message in a custom view with my own style. To do this, I’ll need to create my own ViewController to handle the presentation and logic and subclass UIView to handle the styling. The alert will also need to somehow take a closure in order to be fully compatible with the current API.

Even if you’re not currently using such an extension or alerts in a project, feel free to start from scratch and follow along.

What I want my AlertViewController to do is be presented modally, but fullscreen with an overlay for a background, and a view representing the title, message, and button(s). To achieve this, I’ll set the backgroundColor in viewDidLoad and have a method that presents the alert.

Ready? Follow along 😃

Part 1: The AlertViewController

Part 2: The AlertView

This will be the interactable view in our View/Controller implementation. My idea here is to create a resizable view using auto-layout that always has an ok button, so let’s start with that.

Later, we’ll add functionality to include yes/no buttons, a method to dismiss the viewController when the button is pressed, and another method to send the result of the button press to the caller.

Where I use custom methods for colors, feel free to replace them with your own values. i.e. backgroundColor = .systemRed

Now, in our presentmethod, we can create an AlertView in place, add it to the AlertController’s view, and constrain it.

If you create an instance of AlertViewController now, present it on the ViewController, and call the AlertViewController’s present method, you should see a stylized alert box with whatever title and message you provide.

In fact, let’s just go ahead and change our extension:

Now, in your ViewController’s viewDidLoad method, you can test out your alert using present(title: "Test", message: "Test") . I like cheese, so this is obviously what mine looks like 😉

But try as you might… I don't think you can dismiss it short of closing the app. It’s stuck on the screen like glue. Let’s unglue this together, now using a protocol that our ViewController can conform to.

Part 3: The Protocol and Delegate

Note: `class` conformance may be deprecated soon in favor of Any conformance

Now, let’s conform our AlertViewController to this protocol and provide implementation:

… and add a delegate property to our AlertView. This should be weak to avoid retain cycles.

To finish the delegate pattern, let’s assign the delegate in the present method. This will make it so we can use the delegate property to signal to the ViewController that it needs to dismiss.

But alas, if you ran it and were hoping the okButton would dismiss the AlertViewController… you’ll be sadly disappointed. But don’t sue me just yet… we’re almost out of the woods.

We just need a method the button can use. This method will need to be exposed to Objective-C using @objc so it can be used in a Selector. Sounds complicated, but all we have to do is make a method like this:

… and attach the method to our button using the addTarget method. Let’s do this in the button’s property initializer just before returning the button.

Finally, after all that hard work, you have a stylized UIAlert that has a title, message, and ok button that dismisses the AlertViewController… with or without cheese, as you please.

… …

Did I promise a closure and yes/no button? Of course, I did. Go grab a cup of coffee and put your thinking cap on. This is one of those things that can be really difficult to grasp.

Part 4: The Closure

First, you may be wondering what a closure (also known as completion handler) is, or what a closure does. I look at a closure as a way of passing around a block of code. Another thing we call a block of code is a function. One way I’ve heard closures put simply that made sense to me is this:

“A closure is a function that returns a function”.
- (if you said this, please let me know — I can’t remember for the life of me who you are, though you’ve given me gold!)

Closures are written in the syntax of (parameters) -> ReturnType . Our typical closure returns Void which just gives us a nice place to write code using trailing closure syntax. The code in the trailing closure runs when the completion is triggered. You’ll often see closures used as parameters in asynchronous methods when you need to return something that’s running on another thread… and that’s exactly how we’ll use them here.

Where we have (parameters) in our closure is where we get data of a certain type back. In the case of our Alert, we’re interested in a true/false for our yes/no buttons that we’ll be adding shortly. For now, we’ll use it to always return a value of true from our ok button press.

So adding on to our presentAlert method in our UIViewController extension, we can bubble the handler up the chain.

This is where things get a bit tricky… if they weren’t tricky for you yet. We have to pass this whole completion handler up the chain from the extension, to the ViewController, to the view. The cleanest way to do this isn’t immediately clear to me, but let’s take the path I’ve taken so far, and see how it works for us. It’s working great for me so far, but I haven’t thought too deeply on any possible complications from passing the reference to the closure this way.

You may notice that you have an error at the call site (in viewDidLoad). This is because presentAlert is now expecting you to use that closure. To use it, we’ll do this:

namedBool can be named anything you’d like. This is the result that our completion handler is giving us (remember, we’ll set this to always be true in the case of the okButton). For now, that code would never run because the completion is never being satisfied by being called with a value on “the other end”.

Let’s hit pause for a second and think about this chain of events.

So far, we’ve passed the completion from the extension to the AlertViewController. But where we have our result is at the button press, and that’s inside of the view. The view‘s methods are all private, as they should be. To complicate matters a little further, we will eventually have 2 different buttons that need to use the same closure. What do we do?

Well, we already have a point of entry where we can inject dependencies via the initializer, and we can make a class property designed to hold a closure by using the closure’s type as the type of the property such asprivate var closure: (Bool) -> Void). Then we can use this class property to signal that we’ve returned a value via the button press, or wherever else we may need to!

Now that closure is sitting there, just waiting to be called with a value. Let’s give it one:

Now if you run your code, you should get the result in your completion handler!

But this result is pretty useless right now… let’s make it useful by providing a second use case for our AlertView.

Part 5: More buttons, same closure

Let’s create a noButton. DRY dictates that we not do what I’m about to tell you to do — but I’m too tired for DRY… this was supposed to be a simple article 😜

Normally, we’d create a method that creates a button with the parameters of the method being the things we want to be dynamic in the button (such as text, target, etc) — but just copy the okButton, paste it, change the name to noButton, set the title to “No”, and keep this between us 🙊.

I’m not saying to also copy the okTapped method, change the name to cancelTapped, and change the closure’s value to false, because I definitely wouldn’t do something of this nature twice in one article… but uh… go ahead and do that 😉

Lastly, change the noButton’s targeted action to cancelTapped … I mean who said that???

Alright, now let’s give our AlertView a yesNoMode constant, and use it to setup our views.

In the layout method, let’s change things so that we’re:

  1. Always creating an okButton
  2. Changing the okButton's title to “Yes” when yesNoMode is true
  3. Creating a horizontal UIStackView
  4. Always putting the okButton in the UIStackView
  5. Adding the noButton when yesNoMode is true
Remember, the custom colors and anchor methods won’t work for you, just anchor it using auto layout and provide whatever colors you’d like 😃

Lastly, you’ll want to change (or create) your alertWithYesNoButton method to use AlertViewController like so:

By refactoring this way, I did have to change one parameter in presentAlert — I changed the parameter from UIAlertAction to Bool in the completion handler. What I didn’t have to do was touch any of my call sites. This was very important to me in this project, because of the pace we did it at; we didn’t have time to do it in the cleanest way possible — so changing things at the call site could potentially break things we didn’t foresee or intend on breaking.

Bonus: default value for completion handlers/closures

Don’t want the result from the completion handler at every call site, such as with our okButton? Pardon the pun… but that’s “OK”! Simply provide a closure as a value.

How, you ask? Just do this in the parameter list:

( closure: @escaping (Bool) -> Void = { _ in } )

_ is used as an unnamed variable, meaning we’re just discarding it. It’ll still trigger, but we don’t get the benefit of seeing the result — we also don’t have to call it now.

What considerations do you take when refactoring?
How would you have handled this situation?
How would you refactor my code?

Let me know in the comments ⏬

I’m a Full Stack iOS Developer with a passion for writing clean, reusable code.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store