Upgrade to Pro — share decks privately, control downloads, hide ads and more …

MVC-RS

greg3z
September 07, 2016

 MVC-RS

MVC-RS is an evolution of the MVC pattern to help with some of its limitations thanks to 2 new layers : the Router and the Storage.

greg3z

September 07, 2016
Tweet

More Decks by greg3z

Other Decks in Programming

Transcript

  1. @greg3z import UIKit class Post: NSObject { var author: String

    var authorUrl: String var postUrl: String var timestamp: Int }
  2. @greg3z import UIKit struct Post { let author: String let

    authorUrl: URL let postUrl: URL let date: Date let avatar: UIImage }
  3. @greg3z import UIKit struct Post { let author: String let

    authorUrl: URL let postUrl: URL let date: Date let avatar: UIImage }
  4. @greg3z struct Post { let author: String let authorUrl: URL

    let postUrl: URL let date: Date let avatar: Data }
  5. @greg3z struct Post { let author: String let authorUrl: URL

    let postUrl: URL let date: Date let avatar: String }
  6. @greg3z struct Post { let author: String let authorUrl: URL

    let postUrl: URL let date: Date let avatar: URL }
  7. @greg3z Convert data • raw data -> Model entities •

    Model entities -> raw data • might fail
  8. @greg3z protocol CarsStorage { func read(onComplete: (Result<[Car]>) -> Void) func

    read(carId: CarId, onComplete: (Result<Car>) -> Void) }
  9. @greg3z protocol CarsStorage { func read(onComplete: (Result<[Car]>) -> Void) func

    read(carId: CarId, onComplete: (Result<Car>) -> Void) func read(parameters: (CarBrand?, CarEngine?), onComplete: (Result<[Car]>) -> Void) }
  10. @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) }
  11. @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) }
  12. @greg3z final class CarsSqliteStorage: CarsStorage { } final class CarsCouchbaseStorage:

    CarsStorage { } final class CarsRestApiStorage: CarsStorage { }
  13. @greg3z final class CarsSqliteStorage: CarsStorage { } final class CarsCouchbaseStorage:

    CarsStorage { } final class CarsRestApiStorage: CarsStorage { } final class MockingCarsStorage: CarsStorage { }
  14. @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) } }
  15. @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) } }
  16. @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() } } }
  17. @greg3z Router Layer • talks to the Storage • coordinates

    the Controllers • manages the chrome
  18. @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) } }
  19. @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) } }
  20. @greg3z final class CarsListController: UITableViewController { var cars: [Car] var

    carTouched: (Car) -> Void func tableView(tv: UITableView, didSelectRowAt indexPath: IndexPath) { let car = cars[indexPath.row] carTouched(car) } }
  21. @greg3z let carsListController = CarsListController(cars: cars) carsListController.carTouched = { if

    User.currentUser.isReadOnly { self.showCarDetails(car: $0) } else { self.showCarForm(car: $0) } } show(carsListController)
  22. @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) } }
  23. @greg3z final class CarsListController: UITableViewController { override func viewDidLoad() {

    super.viewDidLoad() navigationItem.rightBarButtonItem = UIBarButtonItem(…) } }
  24. @greg3z let carsListController = CarsListController(cars: cars) let addButton = UIBarButtonItem(title:

    "Add") { let carFormController = CarFormController(car: nil) self.show(carFormController) } carsListController.navigationItem.rightBarButtonItem = addButton show(carsListController)
  25. @greg3z let carsListController = CarsListController(cars: cars) let tabBarItem = UITabBarItem(title:

    "Cars", image: carsIcon) carsListController.tabBarItem = tabBarItem show(carsListController)
  26. @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) } }
  27. @greg3z final class CarRouter: Router { let navigationController: UINavigationController func

    showCarsList() { } func showCarDetails(car: Car) { } func showCarDetails(carId: CarId) { } }
  28. @greg3z Advantages • MVC-based • Reuse Models everywhere • Plug

    & Play Persistence • Very light & dumb Controllers • Easily testable • Easy deep linking