In this blog post we are going to look into how to share UI code between view controllers using protocol extension.
Problem setup
Let’s say we have two view controllers: one showing a list of items and another one showing items in a collection view. In iOS development it is common to use UITableViewController and UICollectionViewController subclasses for implementing the functionality.
final class TableViewController: UITableViewController {} | |
final class CollectionViewController: UICollectionViewController {} |
What if both views do not have any items, then it would be nice to show a label indicating there is no content to show instead of a blank view. How to share code with both view controllers? Although there are multiple ways, in this case, let’s go for protocol extension.
Protocols and protocol extensions
Protocol is a set of methods, properties, and other requirements for enabling a particular functionality. In Swift, it is possible to provide a default implementation for methods and properties in a protocol. Meaning, whenever an object conforms to a protocol, it will gain an implementation of that method automatically. Otherwise the object itself must implement the method or property. This difference is presented in two code snippets where in the first one Car
and Truck
conform to InteriorLighting
protocol and implement the interiorLightColor
property themselves. In the second example the protocol is extended to have a default implementation of the property. It should be noted that both Car
and Truck
can still override default implementation themselves if needed.
import UIKit | |
protocol InteriorLighting { | |
var interiorLightColor: UIColor { get } | |
} | |
struct Car: InteriorLighting { | |
var interiorLightColor: UIColor { | |
return .white | |
} | |
} | |
struct Truck: InteriorLighting { | |
var interiorLightColor: UIColor { | |
return .white | |
} | |
} | |
print(Car().interiorLightColor) // UIExtendedGrayColorSpace 1 1 | |
print(Truck().interiorLightColor) // UIExtendedGrayColorSpace 1 1 |
import UIKit | |
protocol InteriorLighting { | |
var interiorLightColor: UIColor { get } | |
} | |
extension InteriorLighting { | |
var interiorLightColor: UIColor { | |
return .white | |
} | |
} | |
struct Car: InteriorLighting {} | |
struct Truck: InteriorLighting {} | |
print(Car().interiorLightColor) // UIExtendedGrayColorSpace 1 1 | |
print(Truck().interiorLightColor) // UIExtendedGrayColorSpace 1 1 |
Adding banner view when there is no content
Let’s now go back to TableViewController and CollectionViewController and add a banner view for both of those view controllers with protocol extension. Firstly, it is needed to define the protocol. Let’s name it as EmptyViewBanner
. It has two properties: one for defining the string what to show and the other one for providing a container view for the banner as the protocol itself does not know anything about the view controllers. This protocol has a setter what mutates the conforming object. Therefore, let’s restrict the protocol to class only for requiring the conforming type to have reference semantics.
Next step is to provide a default implementation for the emptyViewText
property. Its implementation will add a label to view hierarchy for showing the text. As protocol can’t have stored properties, it needs to use some other way for keeping a reference to the label. Having a tag and looking up by it will suffice.
Lastly, both view controllers need to conform to EmptyViewBanner
protocol and provide container views for the banner.
Now it is possible to set text to both of the view controllers and protocol extension will take care of managing the label.
let contactsViewController: CollectionViewController = … | |
let historyViewController: TableViewController = … | |
contactsViewController.emptyViewText = "There are not any contacts" | |
historyViewController.emptyViewText = "There is no history available" |
Summary
In this blog post we looked into how to use protocol extension in Swift for sharing UI code. How to add a label to view hierarchy for showing a banner and how to keep a reference to it without using stored property.
Thank you for reading.
Playground
ProtocolExtensionForAddingUIElements (GitHub)
Xcode 10.0, Swift 4.2