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 { … } @aligatr

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() } } @aligatr

Slide 5

Slide 5 text

MIXINS & TRAITS @aligatr

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. @aligatr

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 } @aligatr

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) ") }__ }__ @aligatr

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") } } @aligatr

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) } @aligatr

Slide 11

Slide 11 text

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

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) { } } @aligatr

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) } } @aligatr

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) } } @aligatr

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) } } @aligatr

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) } } @aligatr

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) } } @aligatr

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) } } @aligatr

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) ✨ @aligatr

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) } } @aligatr

Slide 21

Slide 21 text

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