I use guard
statement and fatalError()
a lot in my app to make sure data are in consistent state. They help to catch bugs in development phase. Now I'm in the late phase of the project and start to consider how to deal with those fatalError()
calls in release build.
I don't want to remove them because they help to expose unknown bugs. I don't want to just leave them as is in the product release either because they would just abort the App, which doesn't provide user any helpful information about what went wrong. What I'd like to achieve is to show an error message on the screen and then abort when user press "OK". I think there may be two approaches to do it:
1) Don't call fatalError()
. Throw error instead. Let the top level code handles the error (e.g., showing an alert). The issue with the approach is that it requires to change many functions to become throwable, which I think is inconvenient.
2) The second approach is that from what I read on the net, it's possible for code to create alert without access to the current view controller on screen. The trick is to create a new window. I haven't investigated the details yet.
My concern is that I think both approaches have same inherent limitation and are not applicable in all situations. For example, suppose there is something goes wrong in a UITableViewControler
's data source delegate method, does it work to present an alert from within the delegate method? I doubt it.
So I wonder what's the common practice to present the fatal error message to user? Thanks for any suggestions.
{
let view = UIViewController.current.view
Alert.show(on: view, message: errorMsg)
//or just: Alert.show(error), handle it in Alert class
}
extension UIViewController {
class func current(base: UIViewController? = UIApplication.shared.keyWindow?.rootViewController) -> UIViewController? {
if let nav = base as? UINavigationController {
return current(base: nav.visibleViewController)
}
if let tab = base as? UITabBarController {
return current(base: tab.selectedViewController)
}
if let presented = base?.presentedViewController {
return current(base: presented)
}
return base
}
}