Download - MVC-RS par Grégoire Lhotelier
@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!
🧀