In this article, we going to take a look at some common issues with application design. We’ll find out the limitation of classical Object-Oriented Programming and how we can address those using a different approach. In this case, we’ll examine refactoring code with POP approach.

Getting Started

Protocol-Oriented Programming is a new programming paradigm ushered in by Swift 2.0. In the Protocol-Oriented approach, we start designing our system by defining protocols. We rely on new concepts: protocol extensions, protocol inheritance, and protocol compositions. The paradigm also changes how we view semantics. In Swift, value types are preferred over classes. However, objeaoriented concepts don’t work well with structs and enums: a struct cannot inherit from another struct, neither can an enum inherit from another enum. So inheritancefa - one of the fundamental object-oriented concepts - cannot be applied to value types. On the other hand, value types can inherit from protocols, even multiple protocols. Thus, with POP, value types have become first class citizens in Swift.

The aims of this project are to examine the POP paradigm in current project and to compare POP with classical OOP.

Classical Inheritance OOP

Inheritance is a useful technique in OOP but it has some limitation in representing the complexity required by relationships among application entities. To demonstrate, let’s start with class diagram of animal:

In above scenario, our animal don’t seem to be fit well with the inheritance pattern. The signification of this issue are duck and exocoetidae. Duck is not fish but they can swim and exocoetidae is not bird but they can fly. This counterexample to notion that it is not possible to implement swim() method of Fish class inside Duck and the same with fly()** method of Bird class inside exocoetidae*.

A possible solution would be to duplicate the required code for each new subclass. It looks easy, but then we need to deal with a lot of code duplication. In order to avoid that, we put these methods to closest super class. But this will make complex base class.

Better Approach POP

Swift, just like many other programming languages, does not support multiple inheritance. The previous approach, you’d have to keep adding new functionality to your superclass or otherwise create new intermediary class, thereby complicating issue. In new approach, we’re going to use Protocol to solve this issue. Protocols serve as blueprints: they tell us what adopters shall implement, but you can’t provide implementation within a protocol. Luckily, there is another way: protocol extensions are the way to go! In Swift, you can extend a protocol and provide default implementation for methods, computed properties, subscripts and convenience initializers. The following class diagram show the changes in implementation:

Swift does not allow multiple inheritance for classes. However, Swift types can adopt multiple protocols. In below diagram, we use 3 protocols Swimable, Flyable, Eatable. Now, Duck and exocoetidae class adopt all 3 protocols, other class like Tuna only adopts Swimable and Eatable protocols. This design not only is more flexible than squeezing all the required functionality into a monolithic base class but also works for value types.

Refactoring Our Project

In current project, there are 2 files can be refactor using POP : MapAnnotation and FeedViewModel. Both of them have image() method to get the icon image but FeedViewModel is a struct , MapAnnotation is subclass of MKAnnotation. You can not use a superclass in this case. The following diagram show the current structure of 2 files:

In this case, POP and Protocol extension are the best choice. Also, to make feed screen synchronize with map screen, we will display the icon inside a round bubble like map screen. This following class diagram show how to solve above problem with new approach:

You create 2 new protocols GetableIconImage and GetableTintColor and their protocol extension to implement image() and makerTintColor() methods. Then, MapAnnotation and FeedViewModel are going to adopt them.

Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file GetableIconImage and click Create to save the file. Add the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
import UIKit

protocol GetableIconImage {
var type: RestaurantType { get }
}

extension GetableIconImage {
var image: UIImage {
switch type {
case .hawk:
return #imageLiteral(resourceName: "bakery")
case .coffee:
return #imageLiteral(resourceName: "coffee")
case .fastfood:
return #imageLiteral(resourceName: "fastfood")
case .streettea:
return #imageLiteral(resourceName: "water")
default:
return #imageLiteral(resourceName: "food-icon")
}
}
}

Next, Go to File\New\File…, select iOS\Source\Swift File template and click Next. Name the file GetableTintColor and click Create to save the file. Add the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import Foundation
import UIKit

protocol GetableTintColor {
var type: RestaurantType { get }
}

extension GetableTintColor {
var makerTintColor: UIColor {
switch type {
case .hawk:
return .orange
case .coffee:
return .brown
case .fastfood:
return .yellow
case .streettea:
return .green
default:
return .red
}
}
}

Finally, let your MapAnnotation class and FeedViewModel struct adopt both protocol. Your MapAnnotation.swift file should look like following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import UIKit
import MapKit
import Contacts

class MapAnnotation: NSObject, MKAnnotation, GetableIconImage, GetableTintColor {
var title: String?
var subtitle: String?
var type: RestaurantType
var coordinate: CLLocationCoordinate2D

init(restaurant: RestaurantDataModel) {
title = restaurant.name
subtitle = restaurant.bio
type = restaurant.type
coordinate = CLLocationCoordinate2D(latitude: restaurant.latitude, longitude: restaurant.longitude)
super.init()
}
// Annotation right callout accessory opens this mapItem in Maps app
func mapItem() -> MKMapItem {
let addressDict = [CNPostalAddressStreetKey: subtitle!]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDict)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}

}

And, your FeedViewModel.swift file should look like following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Foundation
import UIKit

struct FeedViewModel: GetableIconImage, GetableTintColor {
var name: String
var type: RestaurantType
var rate: Double

init(dataModel: RestaurantDataModel) {
name = dataModel.name
type = dataModel.type
rate = dataModel.rate
}
}

Build and run project, your feed screen should look like this:

Conclusions

In this article, project has been refactored using POP paradigm. You have figured out the common issue with OOP and the new approach with POP. Based on these findings, you can find the better class hierarchy that clean and easy to maintain.

Here is the final project of this part.