Slide 1

Slide 1 text

Mixins over Inheritance Leveraging Mixins and Traits in Swift Olivier Halligon March 2017 aligatr

Slide 2

Slide 2 text

aligatr Bonjour! • iOS Architect & Lead Dev @ Niji • Teaching, Sharing, Speaking
 http://alisoftware.github.io 
 @aligatr • OpenSource enthusiast
 http://github.com/AliSoftware
 OHHTTPStubs, SwiftGen, Reusable, CocoaPods, Fastlane !" #✍ %& '(

Slide 3

Slide 3 text

aligatr Sci-Fi!

Slide 4

Slide 4 text

aligatr Sci-Fi! Doc Emmett Brown Doctor Who Iron Man Superman Time Traveler Flyer Human Alien

Slide 5

Slide 5 text

aligatr Inheritance? Doc Emmett Brown Doctor Who Iron Man Superman Human Alien Character

Slide 6

Slide 6 text

aligatr Inheritance? Doc Emmett Brown Doctor Who Iron Man Superman Time Traveler Flyer Human Alien Character

Slide 7

Slide 7 text

aligatr Composition! Doc Emmett Brown Doctor Who Iron Man Superman Time Machine Human Traits Alien Traits Flying Engine Flying Engine Time Machine Human Traits Alien Traits

Slide 8

Slide 8 text

aligatr Composition! class DoctorWho { let traits: AlienTraits let tardis: TimeMachine let name: String init(name: String, planet: String = "Gallifrey") { self.traits = AlienTraits(planet: planet, hearts: 2) self.tardis = TimeMachine() self.name = name } } class TimeMachine { func travel(to date: Date) { print(")* \(date) +,") } } struct AlienTraits { let planet: String let hearts: Int }

Slide 9

Slide 9 text

aligatr Composition! let david = DoctorWho() david.tardis.travel(to: Date(timeIntervalSince1970: 1303484520)) // )* Apr 22, 2011, 5:02 PM +, print(david.traits.hearts) // 2 let clark = SuperMan() clark.flyingEngine.fly() // flyingEngine?! ! - .

Slide 10

Slide 10 text

aligatr Mixins? Doc Emmett Brown Doctor Who Iron Man Superman Time Traveler Flyer Human Alien Identity / 0 Abilities

Slide 11

Slide 11 text

aligatr Protocols with Default Implementation protocol Flyer { func fly() }__ extension Flyer { func fly() { print("I believe I can flyyyyy ⽄") }__ }__

Slide 12

Slide 12 text

aligatr Protocols with Default Implementation protocol Flyer { func fly() }__ extension Flyer { func fly() { print("I believe I can flyyyyy ⽄") }__ }__ class SuperMan: Flyer { // let Clark sing! } class Bird: Flyer { // we get the default implementation of fly() here } class Plane: Flyer { // we get the default implementation of fly() here } I believe I can flyyyyy ⽄

Slide 13

Slide 13 text

aligatr Protocols with Default Implementation protocol Flyer { func fly() }__ extension Flyer { func fly() { print("I believe I can flyyyyy ⽄") }__ }__ class IronMan: Flyer { let thrusters = Thrusters() // specific implementation when needed func fly() { thrusters.start() } }

Slide 14

Slide 14 text

aligatr Unlimited Abilities 0 protocol TimeTraveler { func travel(to date: Date) } extension TimeTraveler { func travel(to date: Date) { print(")* \(date) +,") } } protocol Flyer { func fly() }__ extension Flyer { func fly() { print("I believe I can flyyyyy ⽄") } } • • •

Slide 15

Slide 15 text

aligatr One Identity, Multiple Abilities class TimeLord: Alien, TimeTraveler, Flyer { init() { super.init(name: "I'm the Doctor", planet: "Gallifrey") } } let doctorWho = TimeLord() doctorWho.travel(to: Date(timeIntervalSince1970: 1303484520)) // )* Apr 22, 2011, 5:02 PM +, doctorWho.fly()

Slide 16

Slide 16 text

aligatr Adding Abilities extension TimeLord: SpaceTraveler {} extension Superman: SpaceTraveler {} protocol SpaceTraveler { func travel(to location: String) } extension SpaceTraveler { func travel(to location: String) { print("Let's go to \(location)!") } } doctorWho.travel(to: "Trenzalore") // prints "Let's go to Trenzalore!"

Slide 17

Slide 17 text

aligatr Protocol All the Things! struct TimeLord: Alien, TimeTraveler, Flyer, SpaceTraveler { let name = "I'm the Doctor" let planet = "Gallifrey" } protocol Named { var name: String { get } } protocol Alien: Named { var planet: String { get } } class Character { let name: String init(name: String) { … } } class Alien: Character { let planet: String init(name: String, planet: String) { … } } « Protocol Oriented Programming is Not a Silver Bullet » — Chris Eidhof

Slide 18

Slide 18 text

aligatr Back to Boring Reality class SuperSmartViewController: UIViewController, BurgerMenuProvider, Animatable, CustomTitleViewProvider { … } extension SuperSmartViewController: Themable { } let vc = SuperSmartViewController() vc.apply(theme: .dark)

Slide 19

Slide 19 text

aligatr Creating type-safe APIs

Slide 20

Slide 20 text

aligatr Creating type-safe APIs let cell1 = tableView.dequeueReusableCell(withIdentifier: "CustomCellOne", for: indexPath) as! CustomCellOne cell1.fill(withText: "Model One, \(indexPath.row)") class CustomCellOne: UITableViewCell { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell { func fill(withIndex: Int) { } }

Slide 21

Slide 21 text

aligatr Creating type-safe APIs protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(describing: Self.self) } } let cell1 = tableView.dequeueReusableCell(withIdentifier: CustomCellOne.reuseIdentifier, for: indexPath) as! CustomCellOne cell1.fill(withText: "Model One, \(indexPath.row)") class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } }

Slide 22

Slide 22 text

aligatr Creating type-safe APIs protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(describing: Self.self) } } extension UITableView { func dequeueReusableCell(indexPath: IndexPath) -> T { return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } }

Slide 23

Slide 23 text

aligatr Creating type-safe APIs protocol Reusable: class { static var reuseIdentifier: String { get } } extension Reusable { static var reuseIdentifier: String { return String(describing: Self.self) } } extension UITableView { func dequeueReusableCell(indexPath: IndexPath) -> T where T: UITableViewCell, T: Reusable { return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T } } class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } }

Slide 24

Slide 24 text

aligatr Creating type-safe APIs extension UITableView { func dequeueReusableCell(indexPath: IndexPath) -> T where T: UITableViewCell, T: Reusable { return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T } } let cell1 = tableView.dequeueReusableCell(indexPath: indexPath) cell1.fill(withText: "Model One, \(indexPath.row)") error: generic parameter 'T' could not be inferred

Slide 25

Slide 25 text

aligatr Creating type-safe APIs extension UITableView { func dequeueReusableCell(indexPath: IndexPath) -> T where T: UITableViewCell, T: Reusable { return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T } } let cell1: CustomCellOne = tableView.dequeueReusableCell(indexPath: indexPath) cell1.fill(withText: "Model One, \(indexPath.row)")

Slide 26

Slide 26 text

aligatr Creating type-safe APIs extension UITableView { func dequeueReusableCell(indexPath: IndexPath) -> T where T: UITableViewCell, T: Reusable { return self.dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T } } let cell1: CustomCellOne = tableView.dequeueReusableCell(indexPath: indexPath) cell1.fill(withText: "Model One, \(indexPath.row)") let cell2 = tableView.dequeueReusableCell(indexPath: indexPath) as CustomCellTwo cell2.fill(withIndex: indexPath.row)

Slide 27

Slide 27 text

aligatr Creating type-safe APIs class CustomCellOne: UITableViewCell, Reusable { func fill(withText: String) { } } class CustomCellTwo: UITableViewCell, Reusable { func fill(withIndex: Int) { } } let cell1: CustomCellOne = tableView.dequeueReusableCell(indexPath: indexPath) cell1.fill(withText: "Model One, \(indexPath.row)") let cell2 = tableView.dequeueReusableCell(indexPath: indexPath) as CustomCellTwo cell2.fill(withIndex: indexPath.row)

Slide 28

Slide 28 text

aligatr Thank You! protocol Talk { func ask(question: String) -> String } extension Talk { func ask(question: String) -> String { return "ya tu sabes" } } http://alisoftware.github.io https://twitter.com/aligatr http://github.com/AliSoftware/Reusable ✍ March 2017 Olivier Halligon I believe I can flyyyyy ⽄