Pick a Delegate… Any Delegate… On Clean View Controllers in Swift

The delegation pattern is ubiquitous in iOS development – the pattern is a “core competency” for developing in Cocoa, and if you program with the iOS SDK for any length of time and you’ll end up writing some code that resembles someInstance.delegate = someDelegate.

One of the toughest things that I’ve experienced is choosing what someDelegate is.  All too often, a View Controller ends up being assigned the responsibility of being the delegate for everything in its hierarchy.  My question is:  Is there a cleaner way?

Let’s pick up on the example I proposed in my recent post about sending e-mails in-app.  For “quick and dirty” pragmatism, I just crammed everything into the View Controller with the promise of coming back and (hopefully) showing a cleaner way.  Here is a quick link to example posed before if you’d like to review it before proceeding.

What if…

What if we could make some adjustments so that the View Controller was trimmed down to the example on the right (click for larger view):

Clean View Controller Comparison

I’ve created a fully-working example on GitHub if you’d like to download it and play.

So the question at hand:  Is the class labeled “Clean Example” preferable (ie, better)?  First, let’s explore how I accomplished the “clean” View Controller.  Then I’ll tip my hand on and share what I like about this approach…

EmailComposer

In order to accomplish the self-declared Clean View Controller above, I placed all of the configuration processes and the delegate method for the MFMailComposeViewController in a new class called EmailComposer.  It should look familiar if you recall the previous example:

 1import Foundation
 2import MessageUI
 3
 4class EmailComposer: NSObject, MFMailComposeViewControllerDelegate {
 5    // Did this in order to mitigate needing to import MessageUI in my View Controller
 6    func canSendMail() -> Bool {
 7        return MFMailComposeViewController.canSendMail()
 8    }
 9    
10    func configuredMailComposeViewController() -> MFMailComposeViewController {
11        let mailComposerVC = MFMailComposeViewController()
12        mailComposerVC.mailComposeDelegate = self
13        
14        mailComposerVC.setToRecipients(["[email protected]"])
15        mailComposerVC.setSubject("Sending you an in-app e-mail...")
16        mailComposerVC.setMessageBody("Sending e-mail in-app is not so bad!", isHTML: false)
17        
18        return mailComposerVC
19    }
20    
21    // MARK: MFMailComposeViewControllerDelegate Method
22    func mailComposeController(controller: MFMailComposeViewController!, didFinishWithResult result: MFMailComposeResult, error: NSError!) {
23        controller.dismissViewControllerAnimated(true, completion: nil)
24    }
25}

So literally, the only thing I did is

  • Cut the function definitions for configuredMailComposeViewController, and the MFMailComposeViewControllerDelegate method.
  • Paste them into the new EmailComposer  class, which inherits from NSObject  (a requirement for this particular delegate protocol’s conformity), and conforms to the MFMailComposeViewControllerDelegate  protocol.
  • Adjust my View Controller to create an instance of EmailComposer , obtain a configured MFMailComposeViewController, and present it whenever the user taps on a button in my UI.

Conclusions

  • The View Controller in its final version is focused.  It’s primary concern is presentation and handling of user interaction with the View itself, rather than needing to worry with configuring an MFMailComposeViewController and its delegate callback.
  • EmailComposer is less of a hassle to test, in the sense that I no longer need to instantiate a View Controller in my XCTestCase class just to test my MFMailComposeViewController stuff.  It’s a real pain to test an actual View Controller instance, so I like that I can easily create an instance of EmailComposer and test away without the bulk.
  • No need to import MessageUI in my View Controller.

All in all, this is the cleanest, simplest, most balanced solution (that I could think of) to factoring out some logic to another class, so as to make my View Controller as clean as possible.

The goal was to make sure the appropriate responsibilities are assigned to the right classes.  Presentation logic is all in the View Controller.  Configuration and delegate callback implementation is done in EmailComposer.

I’m thinking through applying this same idea to other more complicated examples (UITableViewDataSource and UITableViewDelegate come to mind), and I think it would do us a lot of good to strategize on how to avoid making the View Controller the “catch-all” delegate / data source class for everything that’s currently on the screen_._

Hopefully these thoughts spark some ideas in the Swift community.  This post has already been revised slightly based on feedback that I’ve received from folks on Twitter.  If you have additional ideas in regards to choosing the right delegate, holler my way!  I’d love to hear from you.

Thanks for reading.

comments powered by Disqus