Debuggable Alerts in iOS
Quite often in iOS projects, it happens that QA people find issues like this one:
Usually, they report the bug, and, when it’s your turn to fix it, the first thing you think is “Okay, I’ll put a breakpoint where the alert is created and see what’s going on”. Next step, you’re doing something like this:
To find out that the reason could just be:
A server error! Your code might look different, though. Maybe you are not even parsing server errors as Error
objects in your code (you should), but follow me on this one… When this is the case, couldn’t we all save a lot of time if we knew beforehand that the issue was caused by the server, and not by the client app?
What if the alert view itself could describe what's going on?
This way, testers would be able to see that information without needing any developer debug it.
Of course, this debug information shouldn't reach final users. But that is not difficult to achieve, as we'll see next… Introducing: debuggable alerts.
UIAlertController+Debug
We can introduce a simple mechanism to append debug information to our alerts in an extension of UIAlertController
:
extension UIAlertController {convenience init(title: String?, message: String?, error: Error? = nil, enableDebug: Bool = Settings.shared.enableAlertLogs) {
var finalMessage = message ?? ""
if enableDebug, let theError = error {
finalMessage += "\n\n⬇ DEBUG INFO ⬇\n\n\(theError)"
}
self.init(title: title, message: finalMessage, preferredStyle: .alert)
let ok = UIAlertAction(title: "OK", style: .default) { _ in
self.dismiss(animated: true, completion: nil)
}
addAction(ok)
}}
Notice that I distinguished between the scenario where we want debug messages vs. the one where we don't. We should not display any debug information in AppStore builds: these messages are meant to be internal and should never reach final users.
In this example, the default value for the enableDebug
boolean is loaded from our application's shared settings, and from there, it can be automatically set depending on the build configuration, as I explain in this article about how to switch our app's settings in an efficient way.
Also, an Error
instance should always be passed to the alert's initializer, so that the debug information can be obtained from there when needed.
Said that, this is how you would initialize the alert:
case .failure(let error):
let alert = UIAlertController(title: "Oops", message: "Feed could not be loaded.\nTry again later.", error: error)
present(alert, animated: true, completion: nil)
As you can see, since the enableDebug
parameter takes a default value, you don't even need to bother passing it through the initializer's call, that's done behind the scenes for you.
Of course, any iOS dev involved in the project should always leverage this custom initializer instead of the default one when creating an alert. But that’s about a practice that is not difficult to get used to.
The Outcome
Now, let's compare what the process was like without debuggable alerts…
- QA can’t load feed, error says “Could not load feed. Try again later”
- QA reports bug to PM
- PM creates associated ticket and assigns to an iOS dev
- iOS dev takes the ticket and starts debugging
- iOS dev finds out the cause was actually the server
- iOS dev notifies PM about it
- PM reassigns ticket to backend dev
- Backend dev grabs ticket and fixes issue
… to what it is with debuggable alerts:
- QA can’t load feed, error says “Could not load feed. Try again later. 500 server error”
- QA reports bug to PM
- PM creates associated ticket and assigns it directly to a backend dev
- Backend dev fixes the issue
See the difference? The debug message is something really small, but it has a huge impact on the process. No iOS devs were required to take a look at the issue in the latter scenario. No time and effort wasted. Lots of context switches avoided. This kind of situation happens more often than what you think. So, why not using something that simple to our projects that could benefit us like this?
In Conclusion
By introducing this subtle practice, we can make a big difference by saving time and effort in our everyday work. Also, the whole team becomes more efficient and in consequence is able to deliver results faster.
In this article, I just exposed the example of the “500 server error” because I consider it being maybe one of the most annoying to mobile devs. However, the benefits of using debuggable alerts also apply to several other scenarios where having some little debug information about the error can help a lot by pointing out where the problems could be, without having to manually debug.
Happy coding!