Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between extending a class conforming to a protocol VS extending a protocol based on similar condition?

I was going through this link.

But I dont really get the logical difference between the below two code snippets:

1. Extending ONLY those UIViewControllers which conform to protocol ErrorPopoverRenderer .

protocol ErrorPopoverRenderer {
    func presentError(message: String, withArrow shouldShowArrow: Bool, backgroundColor: UIColor, withSize size: CGSize, canDismissByTappingAnywhere canDismiss: Bool)
} 

extension UIViewController: ErrorPopoverRenderer { //Make all the UIViewControllers that conform to ErrorPopoverRenderer have a default implementation of presentError
    func presentError(message: String, withArrow shouldShowArrow: Bool, backgroundColor: UIColor, withSize size: CGSize, canDismissByTappingAnywhere canDismiss: Bool) 
{}
} 

2. Extending the protocol for ONLY those UIViewControllers which conform to it.

extension ErrorPopoverRenderer where Self: UIViewController {
func presentError() {
}
}

Either ways, Any UIViewController subclass which will conform to the protocol will have default method implementation but in UIviewcontroller extension OR protocol extension. What is the logical diffference? Please correct me if I am wrong

like image 385
Abhishek Bedi Avatar asked Dec 26 '15 15:12

Abhishek Bedi


2 Answers

The difference is that the first:

extension UIViewController: ErrorPopoverRenderer { }

actually extends UIViewController class, so every instance of UIViewController has now access to methods & properties from the protocol. It doesn't mean that it will extend the class that implements the protocol, it means that right now you are implementing the protocol for this class. Usually you have to implement some of the methods & properties in that extension.

Where in second one:

extension ErrorPopoverRenderer where Self: UIViewController {}

you actually add methods & properties for UIViewController that implements the protocol ErrorPopoverRenderer.

Basically in first you extend the class with whole protocol implementation, and in the second one you extend class, but only if this class or subclass implements the protocol ErrorPopoverRenderer.

like image 178
sunshinejr Avatar answered Sep 26 '22 19:09

sunshinejr


1

First of all, you create a protocol ErrorPopoverRenderer with a blueprint for method presentError(...). You thereafter extend the class UIViewController to conform to this protocol, by implementing the (compulsory) blueprint for method presentError(...).

This means that you can subclass UIViewController) with additional protocol constraint ErrorPopoverRenderer for the subclass. If UIViewController had not been extended to conform to protocol ErrorPopoverRenderer, the subsequent code in the example you linked would case compile time error (... does not comply to protocol ErrorPopoverRenderer)

class KrakenViewController: UIViewController, ErrorPopoverRenderer {
    func failedToEatHuman() {
        //…
        //Throw error because the Kraken sucks at eating Humans today.
        presentError(ErrorOptions(message: "Oh noes! I didn't get to eat the Human!", size: CGSize(width: 1000.0, height: 200.0))) //Woohoo! We can provide whatever parameters we want, or no parameters at all!
    }
}

However, there is a possible issue with this method, as presented in your link:

Now we have to implement every parameter every time we want to present an ErrorView. This kind of sucks because we can’t provide default values to protocol function declarations.

So of the protocol ErrorPopoverRenderer is not intended solely for use by UIViewController:s (or subclasses thereof), then the solution above isn't very generic.

2

If we want to have a more broad use of ErrorPopoverRenderer, we place the specific blueprints for each class type that might make use of the protocol in protocol extensions. This is really neat as the more specifics parts of ErrorPopoverRenderer blueprints for method presentError() can be specified differently for different classes that are to possibly conform to the protocol, and the method presentError() can be made more minimalistic.

I quote, from the example:

Using Self here indicates that that extension will only ever take place if and only if the conformer inherits from UIViewController. This gives us the ability to assume that the ErrorPopoverRenderer is indeed a UIViewController without even extending UIViewController.

In this method, since the code now knows (we did, already in 1.) that it is a view controller that will call presentError(), we can place the specific UIViewController stuff directly in the blueprint implementation, and needn't send it as a long list of arguments.

Hence, 2. is, for this specific use, a kind of more "generic" approach, in the sense that we slightly minimise code duplication (calling presentError() vs presentError(... lots of args ...) from several different UIViewController:s).

like image 34
dfrib Avatar answered Sep 26 '22 19:09

dfrib