A lot of credit was given recently to new Protocol-Oriented Programming, however, I would like to show my own example and to share the experience.

One fine day I got an amusing task the aim of which was to implement the feature like changing view appearance. It can be shape, shadows or something else, everything depends on design requirements. In my particular case, it had to look like this.

final_appearance_single

Ideally, it would be good to implement it as flexible as possible, therefore I started thinking about the sensible solution. Definitely, inheritance would be really suitable, since the main advantage of this is that we can do all the work in superclass for free. But it seemed this was not exactly what I expected. On the other hand I could implement multiple inheritance of classes like in other programming languages, and you could say: – “Yeah, big deal”, however it isn’t doable for Swift.

Reading articles and seeking for the answer, I came across with the @NatashaTheRobot’s article. I heard about Protocol-Oriented Programming from this WWDC talk too, but never tried. Studying more, we noticed that Protocol Extension allows developers to provide a default implementation to any method or property of the protocol, which was exactly what I was looking for. So I decided to implement it to the given task😈. You can find the full sample here.

To do forthcoming explanation clearer, I decided to provide a simple scheme:

scheme_pop

So, let’s create the protocol. Btw, do not forget about the naming guidelines. Therefore, call them NotebookDisplayable.

/* 
 We shouldn’t declare here the methods because they aren’t required for controls 
 which we are going to extend. Basically, we should to define a default behaviour for a protocol in extension  directly. 
*/
protocol NotebookDisplayable: class {}

Then proceed with the writing extensions for each protocol. Now we are going to describe a default behaviour for a protocol, which will be applied after calling the appropriate method. Note, that you have a capability to set up a constraint for an extension, which in my view is really cool. For example, where Self: UIView means that only UIView can inherit this protocol.

extension NotebookDisplayable where Self: UIView {

  func displayAsNotebook(//...)

    // Add round corners
    let cornerPath = UIBezierPath(//...)
    let cornerLayer = CAShapeLayer()
    // ...
    layer.mask = cornerLayer
      
    // Add shadow ...
  }

  func displayWithPages(//...) {
     // Add pages  ...
  }
}

After this, you need to create a class inherited from this Protocol. When the class is ready we need to change the type of our control to the newly created like in pseudocode below UIImageView -> UIImagePage. And there is no need to change type in .storyboard Inspector, you can just leave the existing one and replace it only for IBOutlet ref.

class ViewController: UIViewController {

   // Create custom class inherited from Protocols
   class UIImagePage: UIImageView, NotebookDisplayable {}
 
   // Setup imageView with new class 
   @IBOutlet weak var imageView: UIImagePage!  
 
   // Call the method of protocol
   override func viewDidAppear(animated: Bool) {
     super.viewDidAppear(animated)
    
     // Call methods in viewDidAppear, becuse here the view constraints has already applied 
     imageView.displayAsNotebook(view)
     imageView.displayWithPages(5)
  }
}

Everything is almost finished, however the difficulty has appeared. The thing that I must create is the computed property in protocol extension which is hard to do as it turned out.
After trying to add this variable directly to the extension

extension NotebookDisplayable where Self: UIView { 
  var shapeLayer: CAShapeLayer?
  // ...
}

I realized that it seemed to be impossible. Nothing worked and I couldn’t come up with an efficient solution.
error

Besides a lot of attempts to fix it, I also hit the solution which implied the use of Objective-C bridge. The proposed idea is to define getter and setter with an associated object. It is supposed that we need to create a global variable which is not favorable in our case, since it is unsafe. Additionally, this decision can impose difficulties in working with NSObject.
So, I decided to reject this idea and just to setup shapeLayer.name with checking if such a layer already exists.

let shapeLayerName = "NotebookShadowLayer"
for l in superview?.layer.sublayers ?? [] {
     if l.name == shapeLayerName {
       return
     }
}

Not an optimal solution, but for that moment I hadn’t come up with any beautiful way to do this 😇. Anyway, we achieved what we had wanted. So now we can add a pretty page effect to the UIImageView controls and not only to them.

The real beauty is that we can develop another protocol with different behavior and easily apply it to the particular element. And the most important is that we can manipulate the code in a really flexible way, without affecting other parts of the project.
Typically involving difficulty or delay, it persuaded me that the POP provides a really powerful tool to extend the functionality. The fact that, Swift does not have abstract classes could cause a lot of difficulties before, but who cares, we have the protocol-oriented approach now 💪.