Talks - pxlshpr/knowledge-base GitHub Wiki

Talks 📣

  1. Practical Protocol-Oriented Programming in Swift | Natasha Murashev
  2. Beyond Crusty: Real-World Protocols | Rob Napier
  3. Blending Cultures: The Best of Functional, Protocol-, & Object-Oriented Programming | Daniel Steinberg

Practical Protocol-Oriented Programming in Swift | Natasha Murashev

Natasha starts the talk by reminding us all that:

"Swift is a Protocol-Oriented Programming Language"

  • Professor of Blowing-Your-Mind

She then points out that the examples demonstrated in the 2015 WWDC talk were not all that practical, as much as she knew that there was a bigger underlying message. The drawing-based examples are in more of a niche area of Swift.

Example: Shaking a UIView subclass

She asks us to imagine a situation where we are asked to write some code that shakes a UIImageView.

Rudimentary Approach

Her initial approach would be to:

  1. Find reusable code from StackOverflow to copy across
  2. Create a subclass of UIImageView
  3. Add a function called shake() with code copied over from StackOverflow
  4. Use imageView.shake() wherever needed

[5:17]**TODO link to YouTube at this timeframe

She then asks us to imagine being asked to write a similar shake function for a UIButton. Her first rudimentary approach would be to follow the same steps while duplicating the shake() function in the new class.

import UIKit
	
class FoodImageView: UIImageView {
	func shake() {
		// implementation code
	}
}
	
class ActionButton: UIButton {
	func shake() {
		// implementation code
	}
}

The Problem

The problem is that this violates the DRY (TODO: link to dry wiki) principle of software engineering in addition to excessively creating a new subclass to accomplish this.

An Obj-C solution

She then describes an alternative (and, arguably, Obj-C style of approach) to this solution would be to create an extension in Swift (or a category in Obj-C) to the UIView class.

The problem with this approach is that all other subclasses of UIView would then redundantly inherit these extensions. Any additional implementation on these extensions would pile up, turning it

… into a big Frankenstein monster.

The Solution: Protocols to the rescue [8:46]

She then gets to the point of the talk, suggesting to replace extensions with protocols – creating one called Shakeable in this instance. She suggests to then create an extension for this protocol so that we can constrain in to UIViews and implement the shake() functions there – so that any non-UIViews wouldn't inherit this behaviour.

The power of protocol extensions come from being able to constrain them to only work for certain classes – making it a very inert protocol

import UIKit
	
protocol Shakeable() { }
	
extension Shakeable where Self: UIView {
	func shake() {
		// implementation code
	}
}
	
class FoodImageView: UIImageView, Shakeable { }
class ActionButton: UIButton, Shakeable { }

An immediate benefit of this is that by looking at the class signature alone – and not the actual implementation – we are able to tell that intention for these views are to shake.

If we then want to add in the dim() functionality – we could simply include this in another (constrained) protocol extension called Dimmable, simply concatenating this to the list of protocols like so: class FoodImageView: UIImageView, Dimmable { }

Another example: Loading a cell from a nib and registering it with a tableView

This example further displays the power of harnessing protocol extensions. She mentions the common practice of using the same string ("FoodTableViewCell" in this case) for both the nib's name and the cell's reuse identifier:

// FoodViewController

override func viewDidLoad() {
	super.viewDidLoad()
		
	let foodCellNib = UINib(nibName: "FoodTableViewCell", bundle: nil)
	tableView.registerNib(foodCellNib, forCellReuseIdentifier: "FoodTableViewCell")
}

De-stringifying

Her rudimentary attempt at de-stringifying this would be the more Obj-c approach of using Swift's version of NSStringFromClass() – removing the need to manually type out the class name each time and risk a typo. This would replace all the instances of "FoodTableViewCell" with String(FoodTableViewCell).

Modularising

Her first step at modularising this would be to create a protocol extension (that only applies to views) with a static variable reuseIdentifier returning the string version of the class:


protocol ReusableIdentifier { }

extension ReusableIdentifier where Self: UIView {
	static var reuseIdentifier: String {
		return String(describing: self)
	}
}

We can then extend every single UITableViewCell without much more work and use the static variable throughout our system – already improving the readability and reusability tremendously.

extension UITableViewCell: ReusableIdentifier { }
class CustomTableViewCell: UITableViewCell { }
let identifier CustomTableViewCell.reuseIdentifier

We can do the same thing with nibName's creating a NibLoadableView protocol that similarly returns the class name as the nib name.

Harnessing the power of Generics

Taking this one step further with generics – we can now extend UITableView and modularize the code that registers a cell which conforms to both NibLoadableView and ReusableView. This will allow use to simply call something like tableView.register(CustomTableViewCell.self).

Abstracting things out to this point stops us from focusing that what we're passing in is actually a UITableViewCell as we now only care about it being both nib-loadable and reusable. This is an example of using protocol extensions to abstract out our understanding of the way Apple's frameworks work while decoupling from its semantics.

This also gives us an additional level of security as there are less points of potential errors, in addition to more readability.

Simplifying how we dequeue cells

She also talks about fixing the somewhat tedious, legacy concept of dequeuing a cell (for a table view) with this new functionality of having an automated reuse identifier, thus being able to call something like:

if indexPath.row == 0 {
	let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as DesertTableViewCell
}
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as FoodTableViewCell

This makes us code in a way that is arguably how we'd rather interact with Apple's interfaces.

Swift and protocols can help us extract that ugliness and annoyances of UIKit, and hide them, and make our code more cleaner and safer and readable.

Credits

She pays homage to the initial idea by @gonzalezreal on Medium

Beyond Crusty: Real-World Protocols | Rob Napier

Blending Cultures: The Best of Functional, Protocol-, & Object-Oriented Programming | Daniel Steinberg