MVC-RS par Grégoire Lhotelier

Preview:

Citation preview

@greg3z

MVC-RS

@greg3z

MVC-RS

@greg3z

Vanilla MVC

@greg3z

Reusable Swift

@greg3z

Reusable Swift

@greg3z

Reusable Swift

@greg3z

Reusable Swift

@greg3z

Reusable View

@greg3z

Reusable View

import AppKit

@greg3z

Reusable View

import AppKit

@greg3z

Reusable Model?

@greg3z

No UIKit classes in the Model

@greg3z

Model layer

• modelize a domain

• talks to the Persistence layer

@greg3z

Persistence compatibility 💾

db A 😀 😀 😀 😀 😀

@greg3z

Persistence compatibility 💾

db A 😀 😀 😀 😀 😀

db B 😀 😞 😩 😭☹

@greg3z

M VC

@greg3z

M VC💾

@greg3z

S

@greg3z

Storage

@greg3z

M VC💾 S

@greg3z

Storage Layer

• talks to the Persistence layer

• convert raw data to Model instances

@greg3z

Persistence 💾

• async

• success

• failure

@greg3z

Convert data

• raw data -> Model entities

• Model entities -> raw data

• might fail

@greg3z

enum Result<T> {

case success(T) case failure(Error)

}

@greg3z

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

@greg3z

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

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

@greg3z

Model layer

• modelize a domain

@greg3z

Reusable Model

@greg3z

Reusable Storage

@greg3z

Reusable Storage

% Persistence

@greg3z

M VC

@greg3z

use case C

use case Duse case A

use case Buse case E

@greg3z

use case C

use case Duse case A

use case Buse case E

@greg3z

use case C

use case Duse case A

use case Buse case E

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

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

@greg3z

Dependency Injection?

@greg3z

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

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

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

}

@greg3z

Controller states

• loading

• error

• empty

• actual

@greg3z

R

@greg3z

Router

@greg3z

M VC💾 S

@greg3z

M VC💾 SR

@greg3z

Router Layer

• talks to the Storage

• coordinates the Controllers

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

@greg3z

Dependency Injection!

@greg3z

Controllers coordination

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

@greg3z

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

}

@greg3z

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

@greg3z

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

@greg3z

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

@greg3z

Advantages

• MVC-based

• Reuse Models everywhere

• Very light & dumb Controllers

• Easily testable

• Easy deep linking

@greg3z

Thank you!

🧀

Recommended