Advanced usage of the factory method pattern

The factory method pattern is particularly suited to configuring the objects provided by SDKs in a reliable manner. For example, we may want to have a common way to instantiate alerts for our users when there's an error in the app.

Let's see how we can apply this with the popular iOS view controller UIAlertController. This controller is used to display a message to the user, either in the form of an alert or an action sheet.

In object-oriented languages, we'd create a factory object, and issue the objects from a factory, as follows:

class UIAlertControllerFactory {
static let sharedFactory = UIAlertControllerFactory()

private init() {}

func alertControllerFor(error: Error,
onOK handler: @escaping (UIAlertAction) -> Void)
-> UIAlertController {
/* implementation goes here */
}
}


let alertController = UIAlertControllerFactory.shared.alertControllerFor(error: error) {

}

However, this is not very Swiftyit's very long, verbose, and impractical. Discovering the method on UIAlertControllerFactory is quite painful for developers who have just joined your project, and even yourselfafter a while, you may even forget about this factory.

In Swift, we can leverage extensions to provide additional initializers, methods, and static methods. This is the preferred way when you want to implement factory methods that help you define additional configuration for your objects, as follows:

extension UIAlertController {
static func forError(_ error: Error,
onOK handler: @escaping (UIAlertAction) -> Void) -> UIAlertController {

let title = NSLocalizedString("There was an error", comment: "Error alert title")
let message = error.localizedDescription

let OK = NSLocalizedString("OK", comment: "Error alert OK button title")

let controller = UIAlertController(title: title,
message: message,
preferredStyle: .alert)
let okAction = UIAlertAction(title: OK, style: .cancel, handler: handler)

controller.addAction(okAction)

return controller
}
}

And we can use it anywhere else in the code, having a standard error AlertController to display our errors, as follows:

do {
// Call something that may fail...
} catch let e {
present(
UIAlertController.forError(e) { (action) in
// user tapped OK
},
animated: true,
completion: nil)
}

Alternatively, Swift provides the powerful ability to declare convenience initializers in class extensions. Instead of a static method, it is possible to refine the previous implementation with a convenience init:

extension UIAlertController {
convenience init(forError error: Error, onOK handler: @escaping (UIAlertAction) -> Void) {
let title = NSLocalizedString("There was an error", comment: "Error alert title")
let message = error.localizedDescription
let OK = NSLocalizedString("OK", comment: "Error alert OK button title")

self.init(title: title, message: message, preferredStyle: .alert)
let okAction = UIAlertAction(title: OK, style: .cancel, handler: handler)
ddAction(okAction)
}
}

Using the following implementation is even simpler:

do {
// Call something that may fail...
} catch let e {
present(
UIAlertController(forError: e) { (action) in
// user tapped OK
},
animated: true,
completion: nil)
}

While all these implementations are very similar, they all carry a different weight. If you find yourself using the static or class method to create new instances of a particular object, always ponder whether a convenience initializer would be more fitting.