13

I was working through a Ray Wenderlich tutorial and noticed that the author uses class extensions to hold delegate callbacks rather than having them be handled in the class itself i.e.:

delegate callbacks inside class extension:

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

as opposed to having it be contained within the class:

delegate callbacks inside class:

class LogsViewController : UITableViewController, UIPopoverPresentationControllerDelegate {
    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
        ...
    }
}

I found this strange and interesting at the same time. He has a file dedicated just to extensions on the LogsViewController class named "LogsViewControllerExtension.swift" and has a different extension for each delegate protocol: UITableViewDataSource, UISplitViewDelegate, etc i.e.:

multiple class extensions each w/ delegate callbacks inside its own file:

extension LogsViewController: UISplitViewControllerDelegate {
    ... callbacks
}

extension LogsViewController : UIPopoverPresentationControllerDelegate {
    ... callbacks
}

Why?

What advantages are there to doing this? I can see where it might be a little more readable to separate this out but at the same time it is a level of indirection. Are there OO principles that are supportive of or against doing this?

morbidhawk
  • 362
  • 2
  • 11
  • 1
    You can write lots of structure like this that is fundamentally supported by OO principles but still be guilty of overengineering. – Robert Harvey Nov 03 '15 at 21:26
  • 1
    @RobertHarvey true, but are you implying that the example code I've shown here is a form of overengineering? Delegation is a common pattern in iOS development. Also, whether you use class extensions or not doesn't change the code within it, so it would be more like code restructuring rather than re(/over)-engineering – morbidhawk Nov 03 '15 at 22:13
  • 1
    I am seeing this pattern of throwing a bunch of extensions into the same file, and I don't know where this is coming from. It seems some people are already abusing it and just arbitrarily throwing every little bit of code into an extension within the same file. I'm sure there is a good reason to do this on occasion, but I get the impression some programmers are just doing it without understanding why. I keep looking for an advantage of this over using MARK: - and would love to find some definitive source on why extensions should be used in this way. – David Lari Mar 23 '16 at 13:39

3 Answers3

15

I don't know why you said it adds a level of indirection. Maybe you mean something different by that than the traditional meaning, because there is no extra indirection created by doing this. But why do it?

I do it because it is more modular. All the code that is required by the interface is grouped in a single place (except the actual properties.) If I later choose to make a separate class to implement that protocol (and thus introduce an actual level of indirection,) all I need to do is change the extension into a class of it's own (passing the needed properties in through an init function,) and create a property on the ViewController to instantiate the object into.

I also put any private functions which are only used by the functions of that protocol into the extension. I haven't gone so far as to create a completely separate file for the extension, but doing so makes it clear that those private functions are only for that protocol.

And at any rate, people often complain about fat view controllers and breaking a view controller up this way helps keep it better organized, even if it doesn't actually make the view controller thinner.

Daniel T.
  • 3,013
  • 19
  • 24
  • I see what you mean by modularity. Once I understood what was going on it did look like a clean way of separating concerns. The level of indirection I think is for a new set of eyes (and I'm biased coming from the Obj-c way). I feel the same level of indirection when subclassing where code is being referenced that is defined somewhere else in the base class and its a little more difficult to find it. I agree that organizationally it adds benefit (with a little indirection cost) – morbidhawk Dec 02 '15 at 14:13
  • I guess this has been done before with class categories in Obj-C. I was never much of a fan of those but I think they required a separate file. Since now in Swift you can keep the extension in the same file that's what I'll probably do if I decide to break them up like that – morbidhawk Dec 02 '15 at 14:20
2

As Daniel said in regards of indirection, there is no level of it.
I agree him and I would like to add extra powerful feature of Protocol Extensions that I knew recently.

Say that you have a protocol didCopyText for example. You will implement that as:

protocol didCopyText {
  var charachtersCount: Int {get}
  func addToClipboardWith(text: String)
}

In Swift, properties and methods are not implemented in the Protocol declaration, you would want to write the implementation in every class conforms to the didCopyText, with incremental number of classes conforming to this protocol with the same implementation, it would end up with just a messy repeated code. That's where Protocol Extensions comes in handy.

protocol didCopyText {
var charachtersCount: Int {
    get {
     // implementation
    }
}
func addToClipboardWith(text: String) {
      // implementation
 }
}

With the implementation of the protocol's properties & methods. Now, any class conforms to this protocol, will use the same implementation.

Ennabah
  • 121
  • 3
  • thanks for sharing this. At the time I asked this question I did not realize about some of the drawbacks of OO inheritance, and what you are describing here is a great way to get the same functions reused by all implementing classes that conform to that interface rather than being forced to inherit everything from an object. And if you follow the Interface Segregation principle you can break apart the protocols as needed to ensure the implementing classes never have properties/methods from the interface that they don't need. – morbidhawk Apr 24 '17 at 12:18
2

Let's say your class supports three protocols, and therefore you have to add three sets of functions. The only purpose of these functions is to support a protocol, so you need some documentation.

However, if you add an extension for each protocol, and in each extension you implement exactly the functions needed for that one protocol, that makes your code a lot more readable.

I would most likely not put them into separate files unless these extensions are truly large.

gnasher729
  • 42,090
  • 4
  • 59
  • 119