Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

MVC-RS par Grégoire Lhotelier

2,837 views

Published on

MVC-RS, l’évolution du pattern présenté à SwiftConf

Published in: Software
  • Be the first to comment

  • Be the first to like this

MVC-RS par Grégoire Lhotelier

  1. 1. @greg3z MVC-RS
  2. 2. @greg3z MVC-RS
  3. 3. @greg3z Vanilla MVC
  4. 4. @greg3z Reusable Swift
  5. 5. @greg3z Reusable Swift
  6. 6. @greg3z Reusable Swift
  7. 7. @greg3z Reusable Swift
  8. 8. @greg3z Reusable View
  9. 9. @greg3z Reusable View import AppKit
  10. 10. @greg3z Reusable View import AppKit
  11. 11. @greg3z Reusable Model?
  12. 12. @greg3z No UIKit classes in the Model
  13. 13. @greg3z Model layer • modelize a domain • talks to the Persistence layer
  14. 14. @greg3z Persistence compatibility 💾 db A 😀 😀 😀 😀 😀
  15. 15. @greg3z Persistence compatibility 💾 db A 😀 😀 😀 😀 😀 db B 😀 😞 😩 😭☹
  16. 16. @greg3z M VC
  17. 17. @greg3z M VC💾
  18. 18. @greg3z S
  19. 19. @greg3z Storage
  20. 20. @greg3z M VC💾 S
  21. 21. @greg3z Storage Layer • talks to the Persistence layer • convert raw data to Model instances
  22. 22. @greg3z Persistence 💾 • async • success • failure
  23. 23. @greg3z Convert data • raw data -> Model entities • Model entities -> raw data • might fail
  24. 24. @greg3z enum Result<T> { case success(T) case failure(Error) }
  25. 25. @greg3z struct Car { let model: String let image: Data }
  26. 26. @greg3z protocol CarsStorage { func read(onComplete: (Result<[Car]>) -> Void) }
  27. 27. @greg3z protocol CarsStorage { func read(onComplete: (Result<[Car]>) -> Void) func create(car: Car, onComplete: (Error?) -> Void) func update(car: Car, onComplete: (Error?) -> Void) func delete(car: Car, onComplete: (Error?) -> Void) }
  28. 28. @greg3z Model layer • modelize a domain
  29. 29. @greg3z Reusable Model
  30. 30. @greg3z Reusable Storage
  31. 31. @greg3z Reusable Storage % Persistence
  32. 32. @greg3z M VC
  33. 33. @greg3z use case C use case D use case A use case B use case E
  34. 34. @greg3z use case C use case D use case A use case B use case E
  35. 35. @greg3z use case C use case D use case A use case B use case E
  36. 36. @greg3z final class CarsListController: UITableViewController { func tableView(tv: UITableView, didSelectRowAt indexPath: IndexPath) { let car = cars[indexPath.row] let next = CarDetailsController(car: car) navigationController?.pushViewController(next, animated: true) } }
  37. 37. @greg3z final class CarsListController: UITableViewController { func tableView(tv: UITableView, didSelectRowAt indexPath: IndexPath) { let car = cars[indexPath.row] let next: UIViewController if User.currentUser.isReadOnly { next = CarDetailsController(car: car) } else { next = CarFormController(car: car) } navigationController?.pushViewController(next, animated: true) } }
  38. 38. @greg3z Dependency Injection?
  39. 39. @greg3z final class CarsListController: UITableViewController { var cars: [Car] init(cars: [Car]) { self.cars = cars super.init() } }
  40. 40. @greg3z final class CarsListController: UITableViewController { var cars: [Car]? // nil until the data is loaded init() { super.init() } override func viewDidLoad() { super.viewDidLoad() CarsModel.getCars { cars in self.cars = cars self.tableView.reloadData() } } }
  41. 41. @greg3z Controller states • loading • error • empty • actual
  42. 42. @greg3z R
  43. 43. @greg3z Router
  44. 44. @greg3z M VC💾 S
  45. 45. @greg3z M VC💾 S R
  46. 46. @greg3z Router Layer • talks to the Storage • coordinates the Controllers
  47. 47. @greg3z showLoading() carsStorage.read { switch $0 { case .success(let cars): if cars.isEmpty { self.showEmpty() } else { self.show(CarsListController(cars: cars)) } case .failure(let error): self.showError(error) } }
  48. 48. @greg3z Dependency Injection!
  49. 49. @greg3z Controllers coordination
  50. 50. @greg3z final class CarsListController: UITableViewController { func tableView(tv: UITableView, didSelectRowAt indexPath: IndexPath) { let car = cars[indexPath.row] let next = CarDetailsController(car: car) navigationController?.pushViewController(next, animated: true) } }
  51. 51. @greg3z final class CarsListController: UITableViewController { var carTouched: (Car) -> Void func tableView(tv: UITableView, didSelectRowAt indexPath: IndexPath) { let car = cars[indexPath.row] carTouched(car) } }
  52. 52. @greg3z let carsListController = CarsListController(cars: cars) show(carsListController)
  53. 53. @greg3z let carsListController = CarsListController(cars: cars) carsListController.carTouched = { self.showCarDetails(car: $0) } show(carsListController)
  54. 54. @greg3z let carsListController = CarsListController(cars: cars) carsListController.carTouched = { if User.currentUser.isReadOnly { self.showCarDetails(car: $0) } else { self.showCarForm(car: $0) } } show(carsListController)
  55. 55. @greg3z Advantages • MVC-based • Reuse Models everywhere • Very light & dumb Controllers • Easily testable • Easy deep linking
  56. 56. @greg3z Thank you! 🧀

×