56
@greg3z MVC-RS

MVC-RS par Grégoire Lhotelier

Embed Size (px)

Citation preview

Page 1: MVC-RS par Grégoire Lhotelier

@greg3z

MVC-RS

Page 2: MVC-RS par Grégoire Lhotelier

@greg3z

MVC-RS

Page 3: MVC-RS par Grégoire Lhotelier

@greg3z

Vanilla MVC

Page 4: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Swift

Page 5: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Swift

Page 6: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Swift

Page 7: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Swift

Page 8: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable View

Page 9: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable View

import AppKit

Page 10: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable View

import AppKit

Page 11: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Model?

Page 12: MVC-RS par Grégoire Lhotelier

@greg3z

No UIKit classes in the Model

Page 13: MVC-RS par Grégoire Lhotelier

@greg3z

Model layer

• modelize a domain

• talks to the Persistence layer

Page 14: MVC-RS par Grégoire Lhotelier

@greg3z

Persistence compatibility 💾

db A 😀 😀 😀 😀 😀

Page 15: MVC-RS par Grégoire Lhotelier

@greg3z

Persistence compatibility 💾

db A 😀 😀 😀 😀 😀

db B 😀 😞 😩 😭☹

Page 16: MVC-RS par Grégoire Lhotelier

@greg3z

M VC

Page 17: MVC-RS par Grégoire Lhotelier

@greg3z

M VC💾

Page 18: MVC-RS par Grégoire Lhotelier

@greg3z

S

Page 19: MVC-RS par Grégoire Lhotelier

@greg3z

Storage

Page 20: MVC-RS par Grégoire Lhotelier

@greg3z

M VC💾 S

Page 21: MVC-RS par Grégoire Lhotelier

@greg3z

Storage Layer

• talks to the Persistence layer

• convert raw data to Model instances

Page 22: MVC-RS par Grégoire Lhotelier

@greg3z

Persistence 💾

• async

• success

• failure

Page 23: MVC-RS par Grégoire Lhotelier

@greg3z

Convert data

• raw data -> Model entities

• Model entities -> raw data

• might fail

Page 24: MVC-RS par Grégoire Lhotelier

@greg3z

enum Result<T> {

case success(T) case failure(Error)

}

Page 25: MVC-RS par Grégoire Lhotelier

@greg3z

struct Car { let model: String let image: Data }

Page 26: MVC-RS par Grégoire Lhotelier

@greg3z

protocol CarsStorage { func read(onComplete: (Result<[Car]>) -> Void) }

Page 27: MVC-RS par Grégoire Lhotelier

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

Page 28: MVC-RS par Grégoire Lhotelier

@greg3z

Model layer

• modelize a domain

Page 29: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Model

Page 30: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Storage

Page 31: MVC-RS par Grégoire Lhotelier

@greg3z

Reusable Storage

% Persistence

Page 32: MVC-RS par Grégoire Lhotelier

@greg3z

M VC

Page 33: MVC-RS par Grégoire Lhotelier

@greg3z

use case C

use case Duse case A

use case Buse case E

Page 34: MVC-RS par Grégoire Lhotelier

@greg3z

use case C

use case Duse case A

use case Buse case E

Page 35: MVC-RS par Grégoire Lhotelier

@greg3z

use case C

use case Duse case A

use case Buse case E

Page 36: MVC-RS par Grégoire Lhotelier

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

Page 37: MVC-RS par Grégoire Lhotelier

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

Page 38: MVC-RS par Grégoire Lhotelier

@greg3z

Dependency Injection?

Page 39: MVC-RS par Grégoire Lhotelier

@greg3z

final class CarsListController: UITableViewController { var cars: [Car]

init(cars: [Car]) { self.cars = cars super.init() } }

Page 40: MVC-RS par Grégoire Lhotelier

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

}

Page 41: MVC-RS par Grégoire Lhotelier

@greg3z

Controller states

• loading

• error

• empty

• actual

Page 42: MVC-RS par Grégoire Lhotelier

@greg3z

R

Page 43: MVC-RS par Grégoire Lhotelier

@greg3z

Router

Page 44: MVC-RS par Grégoire Lhotelier

@greg3z

M VC💾 S

Page 45: MVC-RS par Grégoire Lhotelier

@greg3z

M VC💾 SR

Page 46: MVC-RS par Grégoire Lhotelier

@greg3z

Router Layer

• talks to the Storage

• coordinates the Controllers

Page 47: MVC-RS par Grégoire Lhotelier

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

Page 48: MVC-RS par Grégoire Lhotelier

@greg3z

Dependency Injection!

Page 49: MVC-RS par Grégoire Lhotelier

@greg3z

Controllers coordination

Page 50: MVC-RS par Grégoire Lhotelier

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

Page 51: MVC-RS par Grégoire Lhotelier

@greg3z

final class CarsListController: UITableViewController { var carTouched: (Car) -> Void func tableView(tv: UITableView, didSelectRowAt indexPath: IndexPath) { let car = cars[indexPath.row] carTouched(car) }

}

Page 52: MVC-RS par Grégoire Lhotelier

@greg3z

let carsListController = CarsListController(cars: cars) show(carsListController)

Page 53: MVC-RS par Grégoire Lhotelier

@greg3z

let carsListController = CarsListController(cars: cars) carsListController.carTouched = { self.showCarDetails(car: $0) } show(carsListController)

Page 54: MVC-RS par Grégoire Lhotelier

@greg3z

let carsListController = CarsListController(cars: cars) carsListController.carTouched = { if User.currentUser.isReadOnly { self.showCarDetails(car: $0) } else { self.showCarForm(car: $0) } } show(carsListController)

Page 55: MVC-RS par Grégoire Lhotelier

@greg3z

Advantages

• MVC-based

• Reuse Models everywhere

• Very light & dumb Controllers

• Easily testable

• Easy deep linking

Page 56: MVC-RS par Grégoire Lhotelier

@greg3z

Thank you!

🧀