面向协议编程与 Cocoa 的邂逅

318643095c83b914cf80a7f99f247fe6?s=47 Wei Wang
September 24, 2016

面向协议编程与 Cocoa 的邂逅

My talk in MDCC 16. About using Protocol-Oriented Programming language in daily Cocoa life.

318643095c83b914cf80a7f99f247fe6?s=128

Wei Wang

September 24, 2016
Tweet

Transcript

  1. ᶎݻܐᦓᖫᑕӨ Cocoa ጱᮁᭊ MDCC 16 ሴ૛ - 2016 ଙ 9

    ์ 24 ෭
  2. Ԇ᷌ • ᩸ɾॳᦩ • ಥɾ૬஌ • ᫨ɾᅾ௡ • ݳɾജ൐

  3. Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬஌ •

    ᫨ɾᅾ௡ • ݳɾജ൐
  4. Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬஌ -

    ܐᦓಘ઀޾ᶎݻܐᦓᖫᑕ • ᫨ɾᅾ௡ • ݳɾജ൐
  5. Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬஌ -

    ܐᦓಘ઀޾ᶎݻܐᦓᖫᑕ • ᫨ɾᅾ௡ - ࣁ෭ଉ୏ݎӾֵአܐᦓ • ݳɾജ൐
  6. Ԇ᷌ • ᩸ɾॳᦩ - Ջԍฎ Swi' ܐᦓ • ಥɾ૬஌ -

    ܐᦓಘ઀޾ᶎݻܐᦓᖫᑕ • ᫨ɾᅾ௡ - ࣁ෭ଉ୏ݎӾֵአܐᦓ • Model (Networking) • ViewController • ݳɾജ൐
  7. ᩸ɾॳᦩ Ջԍฎ Swi% ܐᦓ Protocol

  8. protocol Greetable { var name: String { get } func

    greet() }
  9. ᶎݻ੒᨝ Object-oriented

  10. ᶎݻ੒᨝ class Animal { var leg: Int { return 2

    } func eat() { print("eat food.") } func run() { print("run with \(leg) legs") } } class Tiger: Animal { override var leg: Int { return 4 } override func eat() { print("eat meat.") } } let tiger = Tiger() tiger.eat() // "eat meat" tiger.run() // "run with 4 legs"
  11. ViewController → UIViewController class ViewCotroller: UIViewController { // ᖀಥ //

    view, isFirstResponder()... // ෛے func myMethod() { } }
  12. AnotherViewController → UITableViewController → UIViewController class AnotherViewController: UITableViewController { //

    ᖀಥ // tableView, isFirstResponder()... // ෛے func myMethod() { } }
  13. ࢯहԏӞ Cross-Cu'ng Concerns ཞڔىဳᅩ

  14. ᥴ٬ොໜ • Copy & Paste • ୚ف BaseViewController • ׁᩢဳف

    • ग़ᖀಥ
  15. Objec&ve-C Message Sending

  16. ViewController *v1 = ... [v1 myMethod]; AnotherViewController *v2 = ...

    [v2 myMethod];
  17. NSArray *array = @[v1, v2]; for (id obj in array)

    { [obj myMethod]; }
  18. NSObject *v3 = [NSObject new] // v3 ဌํਫሿ `myMethod` NSArray

    *array = @[v1, v2, v3]; for (id obj in array) { [obj myMethod]; } // Runtime error: // unrecognized selector sent to instance blabla
  19. ࢯहԏԫ Dynamic Dispatch Safety ۖாၝݎਞق௔

  20. OOP ࢯह • ۖாၝݎਞق௔ • ཞڔىဳᅩ

  21. ܐᦓ Protocol

  22. Java, C# Interface

  23. Swift protocol

  24. Swi$ protocol protocol Greetable { var name: String { get

    } func greet() }
  25. Swi$ protocol protocol Greetable { var name: String { get

    } func greet() } struct Person: Greetable { let name: String func greet() { print("֦অ \(name)") } } Person(name: "Wei Wang").greet()
  26. ಥɾ૬஌ ܐᦓಘ઀޾ᶎݻܐᦓᖫᑕ

  27. OOP ࢯह • ۖாၝݎਞق௔ • ཞڔىဳᅩ

  28. protocol Greetable { var name: String { get } func

    greet() } struct Person: Greetable { let name: String func greet() { print("֦অ \(name)") } }
  29. protocol Greetable { var name: String { get } func

    greet() } struct Cat: Greetable { let name: String func greet() { print("meow~ \(name)") } }
  30. let array: [Greetable] = [ Person(name: "Wei Wang"), Cat(name: "onevcat")]

    for obj in array { obj.greet() } // ֦অ Wei Wang // meow~ onevcat
  31. struct Bug: Greetable { let name: String } // Compiler

    Error: // 'Bug' does not conform to protocol 'Greetable' // protocol requires function 'greet()'
  32. OOP ࢯह • ✅ ۖாၝݎਞق௔ • ཞڔىဳᅩ

  33. ֵአܐᦓوՁդᎱ protocol P { func myMethod() }

  34. // class ViewController: UIViewController extension ViewController: P { func myMethod()

    { doWork() } } // class AnotherViewController: UITableViewController extension AnotherViewController: P { func myMethod() { doWork() } }
  35. Swi$ 2 - ܐᦓಘ઀

  36. protocol P { func myMethod() } extension P { func

    myMethod() { doWork() } } ԅ P ਧԎጱොဩ൉׀ἕᦊਫሿ
  37. extension ViewController: P { } extension AnotherViewController: P { }

    viewController.myMethod() anotherViewController.myMethod()
  38. ਫሿ๚ࣁܐᦓӾ्กጱٖ਻ protocol P { func myMethod() } extension P {

    func myMethod() { doWork() } func anotherMethod() { myMethod() someOtherWork() } } viewController.anotherMethod()
  39. • ܐᦓਧԎ • ൉׀ਫሿጱفݗ • ᭽஗ܐᦓጱᔄࣳᵱᥝ੒ٌᬰᤈਫሿ • ܐᦓಘ઀ • ԅفݗ൉׀ἕᦊਫሿ

    • ໑ഝفݗ൉׀᷐क़ਫሿ
  40. OOP ࢯह • ✅ ۖாၝݎਞق௔ • ✅ ཞڔىဳᅩ

  41. ᫨ɾᅾ௡ ࣁ෭ଉ୏ݎӾֵአܐᦓ

  42. WWDC 15 #408 Protocol-Oriented Programming in Swi3

  43. Model (Networking)

  44. ໜֺ चԭ Protocol ጱᗑᕶ᧗࿢

  45. Demo • चԭܐᦓ • ᔄࣳਞق • ᥴᘠݳ • ݢܔᇿၥᦶ •

    ಘ઀௔
  46. • Networking: • AFNetworking • Alamofire • (ASIHTTPRequest) ! •

    Model Parser • SwiAyJSON • Argo • Himotoki
  47. սضᘍᡤֵአܐᦓ ṛଶܐᦓ۸ํۗԭᥴᘠ҅ၥᦶզ݊ಘ઀

  48. APIKit1 + Himotoki2 2 h$ps:/ /github.com/ikesyo/Himotoki 1 h$ps:/ /github.com/ishkawa/APIKit

  49. Controller

  50. ໜֺ ړᶭے᫹

  51. ړᶭے᫹ጱᗑᕶ᧗࿢ struct Pagination<T> { let items: [T] let hasNext: Bool

    } struct ChannelsResquest: Request { typealias Response = Pagination<Channel> let lastId: Int? }
  52. class ChannelsTableViewController: UITableViewController { private var lastId: Int? = nil

    private var hasNext = true override func viewDidLoad() { super.viewDidLoad() load() } func load() { if hasNext { client.send(ChannelsResquest(lastId: lastId)) { result in } } } }
  53. class ChannelsTableViewController: UITableViewController { private var lastId: Int? = nil

    private var hasNext = true private var data: [Channel] = [] override func viewDidLoad() { super.viewDidLoad() load() } func load() { if hasNext { client.send(ChannelsResquest(lastId: lastId)) { result in self.lastId = result!.items.last?.id self.hasNext = result!.hasNext self.data = result.items self.tableView.reloadData() } } } }
  54. extension ChannelsTableViewController: UITableViewDelegate { override func tableView(tableView: UITableView, willDisplayCell cell:

    UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { if indexPath.row == data.count - 1 { load() } }
  55. ඳԪᬮဌํᕮ๳...

  56. ඳԪᬮဌํᕮ๳... private var isLoading = false func load() { if

    isLoading { return } if hasNext { isLoading = true client.send(ChannelsResquest(lastId: lastId)) { result in //... self.isLoading = false } } }
  57. ChannelsTableViewController Pagina&on<Channel>

  58. FriendsTableViewController Pagina&on<Friend>

  59. ොໜӞғ॔ګᔌᩂ

  60. ොໜӞғ॔ګᔌᩂ

  61. ොໜԫғᆿᔄ

  62. BaseTableViewController

  63. class BaseTableViewController: UITableViewController { var lastId: Int? = nil var

    hasNext = true var isLoading = false func loadNext() { if isLoading { return } if hasNext { isLoading = true doLoad {result in self.lastId = //... self.hasNext = //... } } } func doLoad(handler: (Any?)->Void) { // ?? } }
  64. class BaseTableViewController: UITableViewController { var lastId: Int? = nil var

    hasNext = true var isLoading = false func loadNext() { if isLoading { return } if hasNext { isLoading = true doLoad {result in self.lastId = //... self.hasNext = //... } } } func doLoad(handler: (Any?)->Void) { fatalError("You should implement it in subclass!") } }
  65. class FriendsTableViewController: BaseTableViewController { private var data: [Friend] = []

    override func viewDidLoad() { super.viewDidLoad() loadNext() } override func doLoad(handler: (Any?)->Void) { client.send(FriendsRequest(lastId: lastId)) { result in handler(result) // ... self.data = result!.items self.tableView.reloadData() } } }
  66. None
  67. None
  68. WTF?

  69. • FriendsTableViewController → FriendsCollec5onViewController

  70. • FriendsTableViewController → FriendsCollec5onViewController • BaseTableViewController → BaseCollec5onViewController

  71. • FriendsTableViewController → FriendsCollec5onViewController • BaseTableViewController → BaseCollec5onViewController ॔ګᔌᩂ?

  72. • FriendsTableViewController → FriendsCollec5onViewController • BaseTableViewController → BaseCollec5onViewController ॔ګᔌᩂ

  73. ොໜӣғܐᦓ

  74. struct NextPageState<T> { private(set) var hasNext: Bool private(set) var isLoading:

    Bool private(set) var lastId: T? init() { hasNext = true isLoading = false lastId = nil } mutating func reset() { hasNext = true isLoading = false lastId = nil } mutating func update(hasNext: Bool, isLoading: Bool, lastId: T?) { self.hasNext = hasNext self.isLoading = isLoading self.lastId = lastId } }
  75. protocol NextPageLoadable: class { associatedtype DataType associatedtype LastIdType var data:

    [DataType] { get set } var nextPageState: NextPageState<LastIdType> { get set } func performLoad( successHandler: (_ rows: [DataType], _ hasNext: Bool, _ lastId: LastIdType?) -> (), failHandler: () -> () ) }
  76. protocol NextPageLoadable: class { associatedtype DataType associatedtype LastIdType var data:

    [DataType] { get set } var nextPageState: NextPageState<LastIdType> { get set } func performLoad( successHandler: (_ rows: [DataType], _ hasNext: Bool, _ lastId: LastIdType?) -> (), failHandler: () -> () ) } extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard nextPageState.hasNext else { return } if nextPageState.isLoading { return }
  77. extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard

    nextPageState.hasNext else { return } if nextPageState.isLoading { return } nextPageState.isLoading = true performLoad(successHandler: { rows, hasNext, lastId in self.data += rows self.nextPageState.update(hasNext: hasNext, isLoading: false, lastId: lastId)
  78. extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard

    nextPageState.hasNext else { return } if nextPageState.isLoading { return } nextPageState.isLoading = true performLoad(successHandler: { rows, hasNext, lastId in self.data += rows self.nextPageState.update(hasNext: hasNext, isLoading: false, lastId: lastId) self.tableView.reloadData() }, failHandler: { //.. }) } }
  79. class FriendTableViewController: UITableViewController { var nextPageState = NextPageState<Int>() var data:

    [Friend] = [] } extension FriendTableViewController: NextPageLoadable { func performLoad( successHandler: ([String], Bool, Int?) -> (), failHandler: () -> ()) { client.send(FriendsRequest()) { result in if let result = result { successHandler(result.items, result.hasNext, result.items.last.id) } else { failHandler() } } } }
  80. extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...

    } }
  81. extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...

    } } extension FriendTableViewController: NextPageLoadable { ... } class FriendTableViewController: UITableViewController { //... override func viewDidLoad() { super.viewDidLoad() loadNext() } override func tableView(tableView: UITableView, willDisplayCell cell: UITableViewCell, forRowAtIndexPath indexPath: NSIndexPath) { if indexPath.row == data.count - 1 { loadNext() } } }
  82. extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...

    } } UICollec(onView ெԍېҘ
  83. extension NextPageLoadable where Self: UITableViewController { func loadNext() { ...

    } } UICollec(onView ெԍېҘ ॔ګᔌᩂҘ extension NextPageLoadable where Self: UICollectionViewController { func loadNext() { ... } }
  84. extension NextPageLoadable where Self: UITableViewController { func loadNext() { guard

    nextPageState.hasNext else { return } if nextPageState.isLoading { return } nextPageState.isLoading = true performLoad(successHandler: { rows, hasNext, lastId in self.data += rows self.nextPageState.update(hasNext: hasNext, isLoading: false, lastId: lastId) self.tableView.reloadData() }, failHandler: { // Failed when first loading if self.nextPageState.lastId == nil { self.data = [] self.nextPageState.reset() } }) } }
  85. tableView.reloadData() colletionView.reloadData()

  86. tableView.reloadData() colletionView.reloadData() protocol ReloadableType { func reloadData() } extension UITableView:

    ReloadableType {} extension UICollectionView: ReloadableType {}
  87. extension NextPageLoadable where Self: UITableViewController { func loadNext() { //...

    self.tableView.reloadData() //... } }
  88. extension NextPageLoadable { func loadNext(view: ReloadableType) { //... view.reloadData() //...

    } }
  89. extension NextPageLoadable where Self: UITableViewController { func loadNext() { loadNext(reloadView:

    tableView) } }
  90. extension NextPageLoadable where Self: UITableViewController { func loadNext() { loadNext(reloadView:

    tableView) } } extension NextPageLoadable where Self: UICollectionViewController { func loadNext() { loadNext(reloadView: collectionView) } }
  91. • FriendsTableViewController → FriendsCollec5onViewController

  92. class FriendTableViewController: UITableViewController { var nextPageState = NextPageState<Int>() var data:

    [Friend] = [] } extension FriendTableViewController: NextPageLoadable { func performLoad( successHandler: ([String], Bool, Int?) -> (), failHandler: () -> ()) { client.send(FriendsRequest()) { result in if let result = result { successHandler(result.items, result.hasNext, result.items.last.id) } else { failHandler() } } } }
  93. class FriendCollectionViewController: UITableViewController { var nextPageState = NextPageState<Int>() var data:

    [Friend] = [] } extension FriendCollectionViewController: NextPageLoadable { func performLoad( successHandler: ([String], Bool, Int?) -> (), failHandler: () -> ()) { client.send(FriendsRequest()) { result in if let result = result { successHandler(result.items, result.hasNext, result.items.last.id) } else { failHandler() } } } }
  94. ViewController ၥᦶ

  95. ֵአܐᦓ๶ᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺ੝ᖀಥ)

  96. ֵአܐᦓ๶ᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺ੝ᖀಥ) • ֵአܐᦓ๶̿೪ᤰ̀ViewController ಅᵱᥝጱۑᚆ

  97. ֵአܐᦓ๶ᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺ੝ᖀಥ) • ֵአܐᦓ๶̿೪ᤰ̀ViewController ಅᵱᥝጱۑᚆ

    • ਖ਼դᎱ޾ᨱձړᐶڊ ViewController
  98. ֵአܐᦓ๶ᕟᕢ ViewController • כ೮ᓌܔጱ ViewController ᖀಥ (ٺ੝ᖀಥ) • ֵአܐᦓ๶̿೪ᤰ̀ViewController ಅᵱᥝጱۑᚆ

    • ਖ਼դᎱ޾ᨱձړᐶڊ ViewController • ֵአ᯿຅ጱොဩਖ਼ܐᦓ᭑Ⴙ᭗አ۸
  99. ݳɾജ൐ ֵአܐᦓଆۗද࠺դᎱᦡᦇ

  100. വគᩒා • Protocol-Oriented Programming in Swi4 - WWDC 15 #408

    • Protocols with Associated Types - @alexisgallagher • Protocol Oriented Programming in the Real World - @_maHhewpalmer • PracIcal Protocol-Oriented-Programming - @natashatherobot
  101. ᨀᨀᘰލ FAQ Email: onev@onevcat.com, GitHub: onevcat