Routage léger sur les microservices



Les applications mobiles sont devenues vraiment importantes ces derniers temps - non seulement dans le sens de leur importance pour vous et moi, mais aussi au sens littéral.



. , . , , MVVM : - â€” , - â€” ,  â€” ,  â€” .



, ? : - , , MVVM .  â€” iOS-.





, : . : , (, ), . , . :





, .



, :



,
  1. Ne vous montrez pas. Un code stupide et compréhensible est dans la plupart des cas meilleur qu'un code intelligent et incompréhensible.
  2. Soit bref. Le code doit ĂȘtre si petit qu'il ne serait pas dommage de le jeter Ă  tout moment et de le rĂ©Ă©crire en un jour.
  3. . , SOLID, SOLID.
  4. . , .


.







?



MVVM , ( ). OrdersVC, - â€” OrdersVM. , :





, , - :



final class OrderDetailsVM: IPerRequest {
    typealias Arguments = Order

    let title: String

    required init(container: IContainer, args: Order) {
        self.title = "Details of \(args.name) #\(args.id)"
    }
}


IPerRequest ( â€” DI), , DI-. , . :



final class OrderDetailsVC: UIViewController, IHaveViewModel {
    typealias ViewModel = OrderDetailsVM

    private lazy var titleLabel = UILabel()

    override func viewDidLoad() {
        super.viewDidLoad()

        view.backgroundColor = .white
        view.addSubview(titleLabel)
        titleLabel.translatesAutoresizingMaskIntoConstraints = false
        titleLabel.centerXAnchor
            .constraint(equalTo: view.centerXAnchor)
            .isActive = true
        titleLabel.topAnchor
            .constraint(equalTo: view.topAnchor, constant: 24)
            .isActive = true
    }

    func viewModelChanged(_ viewModel: OrderDetailsVM) {
        titleLabel.text = viewModel.title
    }
}


OrderDetailsVC IHaveViewModel ( â€” MVVM) , -. .



OrdersVC , :



extension OrdersVC: UITableViewDelegate {
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        viewModel?.showOrderDetails(forOrderIndex: indexPath.row)
    }
}


, , MVC, iOS, MVVM . ( ) , iOS - . , , .



, OrdersVM, , :



final class OrdersVM: IPerRequest, INotifyOnChanged {
    typealias Arguments = Void

    var orders: [OrderVM] = []
    private let ordersProvider: OrdersProvider

    required init(container: IContainer, args: Void) {
        self.ordersProvider = container.resolve()
    }

    func loadOrders() {
        ordersProvider.loadOrders() { [weak self] model in
            self?.orders = model.map { OrderVM(order: $0) }
            self?.changed.raise()
        }
    }

    func showOrderDetails(forOrderIndex index: Int) {
        let order = orders[index].order
        //   ?
        // ...
    }
}


IPerRequest, , . OrdersProvider, . orders, - changed.raise().



showOrderDetails(forOrderIndex:) , . iOS, present(_:animated:completion:), .



MVC , MVVM : - . , - , - - .  â€” , .



, ?



, , OrdersVM , OrdersProvider.



 â€” , - . - .



, , . , .





, OrdersProvider, . ,  â€” , -. - : , . . : , -, .



 â€” . DI-, . , , DI- , .



, , :



  1. () .
  2. .
  3. DI- .


, - .



?



, , , , present(_:animated:completion:). , :



  1.  â€” . , VC - , -.
  2. , , . -, , - .
  3. , , . , .
  4.  â€” . , .


, . , , PresenterService.



, , ?



:



  1. UIViewController, .
  2. - - .
  3. .


, :



final class PresenterService: ISingleton {

    private unowned let container: IContainer

    public required init(container: IContainer, args: Void) {
        self.container = container
    }
}


, . , , - - , : , , ,  â€” . , PresenterService ,  â€” .



 â€”  â€” :



var topViewController: UIViewController? {
   let keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
   return findTopViewController(in: keyWindow?.rootViewController)
}

func findTopViewController(in controller: UIViewController?) -> UIViewController? {
   if let navigationController = controller as? UINavigationController {
       return findTopViewController(in: navigationController.topViewController)
   } else if let tabController = controller as? UITabBarController,
       let selected = tabController.selectedViewController {
       return findTopViewController(in: selected)
   } else if let presented = controller?.presentedViewController {
       return findTopViewController(in: presented)
   }
   return controller
}


findTopViewController(in:) , , , . , , , , , , .



, . , - , . , , , , :



func present<VC: UIViewController & IHaveViewModel>(
    _ viewController: VC.Type,
    args: VC.ViewModel.Arguments) where VC.ViewModel: IResolvable {

    let vc = VC()
    vc.viewModel = container.resolve(args: args) //   
    topViewController?.present(vc, animated: true, completion: nil)
}


. MVVM DI- , , .



  1. , , , .
  2. - . - , IResolvable ( DI). , . - , - viewModel IHaveViewModel ( MVVM). , VC.ViewModel.Arguments . - DI- . : DI-, MVVM , — . !
  3. , , , , - , present(_:animated:completion:).


, PresenterService, :



final class PresenterService: ISingleton {

    private unowned let container: IContainer

    private var topViewController: UIViewController? {
        let keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
        return findTopViewController(in: keyWindow?.rootViewController)
    }

    required init(container: IContainer, args: Void) {
        self.container = container
    }

    func present<VC: UIViewController & IHaveViewModel>(
        _ viewController: VC.Type,
        args: VC.ViewModel.Arguments) where VC.ViewModel: IResolvable {

        let vc = VC()
        vc.viewModel = container.resolve(args: args)
        topViewController?.present(vc, animated: true, completion: nil)
    }

    func dismiss() {
        topViewController?.dismiss(animated: true, completion: nil)
    }

    private func findTopViewController(
        in controller: UIViewController?) -> UIViewController? {

        if let navigationController = controller as? UINavigationController {
            return findTopViewController(in: navigationController.topViewController)
        } else if let tabController = controller as? UITabBarController,
            let selected = tabController.selectedViewController {
            return findTopViewController(in: selected)
        } else if let presented = controller?.presentedViewController {
            return findTopViewController(in: presented)
        }
        return controller
    }
}


, , â€” dismiss(), . OrdersVM, PresenterService , :



final class OrdersVM: IPerRequest, INotifyOnChanged {
    typealias Arguments = Void

    var orders: [OrderVM] = []

    private let ordersProvider: OrdersProvider
    private let presenter: PresenterService

    required init(container: IContainer, args: Void) {
        self.ordersProvider = container.resolve()
        self.presenter = container.resolve()
    }

    func loadOrders() {
        ordersProvider.loadOrders() { [weak self] model in
            self?.orders = model.map { OrderVM(order: $0) }
            self?.changed.raise()
        }
    }

    func showOrderDetails(forOrderIndex index: Int) {
        let order = orders[index].order
        //     
        presenter.present(OrderDetailsVC.self, args: order)
    }
}


, PresenterService showOrderDetails(forOrderIndex:).



, . ?



UINavigationController . , , NavigationService. , , :



  1. UINavigationController, .
  2. - - .
  3. .


, PresenterService, , . , .



NavigationService
final class NavigationService: ISingleton {

    private unowned let container: IContainer

    private var topNavigationController: UINavigationController? {
        let keyWindow = UIApplication.shared.windows.first { $0.isKeyWindow }
        let root = keyWindow?.rootViewController
        let topViewController = findTopViewController(in: root)
        return findNavigationController(in: topViewController)
    }

    required init(container: IContainer, args: Void) {
        self.container = container
    }

    func pushViewController<VC: UIViewController & IHaveViewModel>(
        _ viewController: VC.Type,
        args: VC.ViewModel.Arguments) where VC.ViewModel: IResolvable {

        let vc = VC()
        vc.viewModel = container.resolve(args: args)
        topNavigationController?.pushViewController(vc, animated: true)
    }

    func popViewController() {
        topNavigationController?.popViewController(animated: true)
    }

    private func findTopViewController(
        in controller: UIViewController?) -> UIViewController? {

        if let navigationController = controller as? UINavigationController {
            return findTopViewController(in: navigationController.topViewController)
        } else if let tabController = controller as? UITabBarController,
            let selected = tabController.selectedViewController {
            return findTopViewController(in: selected)
        } else if let presented = controller?.presentedViewController {
            return findTopViewController(in: presented)
        }
        return controller
    }

    private func findNavigationController(
        in controller: UIViewController?) -> UINavigationController? {

        if let navigationController = controller as? UINavigationController {
            return navigationController
        } else if let navigationController = controller?.navigationController {
            return navigationController
        } else {
            for child in controller?.children ?? [] {
                if let navigationController = findNavigationController(in: child) {
                    return navigationController
                }
            }
        }
        return nil
    }
}


, NavigationService PresenterService, ,  â€” UITabBarController, . .



. ?



 â€” , , . MVVM, , DI- â€” . , :





() - . - - . - ,  â€” . .  â€” , . « â€” -» , . DI-.



. , , , .  â€” . , . .





() .  â€” MVC MVVM. - DI- « â€” -».



PresenterService, , â€” , MVVM . PresenterService MVVM DI-, , .






Swift Playground.




All Articles