Slide 1

Slide 1 text

MIXINS OVER INHERITANCE Leveraging Mixins and Traits in Swift Olivier Halligon September 2016

Slide 2

Slide 2 text

• iOS Architect
 Niji • Teaching, Sharing, Speaking
 http://alisoftware.github.io
 https://twitter.com/aligatr • OpenSource enthusiast
 http://github.com/AliSoftware
 OHHTTPStubs, SwiftGen, Dip, Reusable, CocoaPods… HELLO WORLD! ✍ %

Slide 3

Slide 3 text

THE PROBLEM class BurgerViewController: UIViewController { func setupBurgerMenu() { … } override func viewDidLoad() { super.viewDidLoad() setupBurgerMenu() } } class MyViewController1: BurgerViewController { … } class MyViewController2: BurgerViewController { … } class MyViewController3: UITableViewController + BurgerViewController { … }

Slide 4

Slide 4 text

COMPOSITION! class BurgerMenuManager { func setupBurgerMenu() { … } func onBurgerMenuTapped() { … } var burgerMenuIsOpen = false { didSet { … } } } class MyViewController1: UIViewController { let menuManager = BurgerMenuManager() override func viewDidLoad() { super.viewDidLoad() menuManager.setupBurgerMenu() } } class MyViewController3: UITableViewController { let menuManager = BurgerMenuManager() override func viewDidLoad() { super.viewDidLoad() menuManager.setupBurgerMenu() } } class MyViewController2: UIViewController { let menuManager = BurgerMenuManager() override func viewDidLoad() { super.viewDidLoad() menuManager.setupBurgerMenu() } }

Slide 5

Slide 5 text

MIXINS & TRAITS

Slide 6

Slide 6 text

MIXINS & TRAITS Doc Emmett Brown Doctor Who Iron Man Superman Time Traveler Flyer Human Alien Identities Abilities Inheritance let you describe what an object is. Traits let you describe what an object can do.

Slide 7

Slide 7 text

PROTOCOLS
 WITH DEFAULT IMPLEMENTATION protocol TimeTraveler { func travel(to: NSDate) }__ extension TimeTraveler { func travel(to: NSDate) { print(" \(date) ") }__ }__ class TimeLord: TimeTraveler { // we don't need to implement travel(to:) there } let doctorWho = TimeLord() doctorWho.travel(to: NSDate(timeIntervalSince1970: 1303484520)) // prints " Apr 22, 2011, 5:02 PM " let docBrown = DocEmmettBrown() docBrown.travel(to: NSDate(timeIntervalSince1970: 499161600)) // prints " Oct 26, 1985, 9:00 AM " class DocEmmettBrown: TimeTraveler { // we don't need to implement travel(to:) there }

Slide 8

Slide 8 text

ONE IDENTITY, MULTIPLE ABILITIES protocol Flyer { func fly() } extension Flyer { func fly() { print("I believe I can flyyyyy ⽄") } } class Character { let name: String init(name: String) { … } } class Human: Character { let country: String init(name: String, country: String = "") { … } } class Alien: Character { let planet: String init(name: String, planet: String) { … } } Identities Abilities protocol TimeTraveler { func travel(to: NSDate) }__ extension TimeTraveler { func travel(to: NSDate) { print(" \(date) ") }__ }__

Slide 9

Slide 9 text

ONE IDENTITY, MULTIPLE ABILITIES protocol Flyer { func fly() } protocol TimeTraveler { func travel(to: NSDate) } class Character { let name: String } class Human: Character { let country: String } class Alien: Character { let planet: String } class TimeLord: Alien, TimeTraveler { init() { super.init(name: "I'm the Doctor", planet: "Gallifrey") } } class DocEmmettBrown: Human, TimeTraveler { init() { super.init(name: "Emmett Brown", country: "USA") } } class Superman: Alien, Flyer { init() { super.init(name: "Clark Kent", planet: "Krypton") } } class IronMan: Human, Flyer { init() { super.init(name: "Tony Stark", country: "USA") } }

Slide 10

Slide 10 text

ADDING ABILITIES extension TimeLord: SpaceTraveler {} extension Superman: SpaceTraveler {} protocol SpaceTraveler { func travel(to: String) } extension SpaceTraveler { func travel(to: String) { print("Let's go to \(location)!") } } doctorWho.travel(to: "Trenzalore") // prints "Let's go to Trenzalore!" class TimeLord: Alien, TimeTraveler class DocEmmettBrown: Human, TimeTraveler class Superman: Alien, Flyer class IronMan: Human, Flyer class Character class Human: Character class Alien: Character protocol Flyer { func fly() } protocol TimeTraveler { func travel(to: NSDate) }

Slide 11

Slide 11 text

REAL-LIFE EXAMPLE class CustomCellOne: UITableViewCell { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell { func fill(withIndex: Int) { } }

Slide 12

Slide 12 text

REAL-LIFE EXAMPLE protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } } let type: Reusable.Type = CustomCellOne.self tableView.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } }

Slide 13

Slide 13 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: Reusable.Type) -> UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath, type: CustomCellOne.self) class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } UITableViewCell protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } }

Slide 14

Slide 14 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: T.Type) -> UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath, type: CustomCellOne.self) protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } }

Slide 15

Slide 15 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: T.Type) -> T where T: UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath, type: CustomCellOne.self) cell1.fill(withText: "Hello there!") protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } }

Slide 16

Slide 16 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: T.Type = T.self) -> T where T: UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath, type: CustomCellOne.self) cell1.fill(withText: "Hello there!") protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } }

Slide 17

Slide 17 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: T.Type = T.self) -> T where T: UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath) cell1.fill(withText: "Hello there!") protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } }

Slide 18

Slide 18 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: T.Type = T.self) -> T where T: UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath) as CustomCellOne cell1.fill(withText: "Hello there!") protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(reflecting: Self.self) } }

Slide 19

Slide 19 text

REAL-LIFE EXAMPLE extension UITableView { func dequeueReusableCell(indexPath: IndexPath, type: T.Type = T.self) -> T where T: UITableViewCell { return self.dequeueReusableCell(withIdentifier: type.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath) as CustomCellOne cell1.fill(withText: "Hello there!") let cell2: CustomCellTwo = tableView.dequeueReusableCell(indexPath: indexPath) cell2.fill(withIndex: indexPath.row) tableView.register(class: CustomCellOne.self) ✨

Slide 20

Slide 20 text

REAL-LIFE EXAMPLE class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { … } } class CustomXIBCell: UICollectionViewCell, NibReusable { func fill(indexPath: NSIndexPath) { … } } class InfoViewController: UIViewController, StoryboardBased { … } let infoVC = InfoViewController.instantiate() infoVC.setInfo(self.info) class MyCustomWidget: UIView, NibOwnerLoadable { @IBInspectable var rectColor: UIColor? required init?(coder aDecoder: NSCoder) { super.init(coder: aDecoder) MyCustomWidget.loadFromNib(owner: self) } }

Slide 21

Slide 21 text

THANK YOU protocol Talk { func ask(question: String) -> String } extension Talk { func ask(question: String) -> String { return "ya tu sabes" } } • http://github.com/AliSoftware/Reusable • http://alisoftware.github.io • https://twitter.com/aligatr ✍ September 2016 Olivier Halligon