Передача данных между ViewModels в MVVM-C

#ios #swift #mvvm #dependency-injection #coordinator-pattern

#iOS #swift #mvvm #внедрение зависимостей #координатор-шаблон

Вопрос:

Я использую MVVM с координатором для разработки приложения. Одна вещь, в которой у меня возникают сомнения, заключается в том, как передавать данные между разными ViewModels . Обычно предыдущая ViewModel просто создает следующую ViewModel и просто выполняет внедрение зависимостей метода prepareforsegue . Однако теперь, когда я отвечаю за всю навигацию, как мне этого добиться?

 Class AppCoordinator : NSObject, Coordinator, UINavigationControllerDelegate {

    var childCoordinators = [Coordinator]()
    var navigationController: UINavigationController
    var dependencyContainer : MainDependencyContainer
    
    func start() {
        let vc = ViewController.instantiate()
        vc.coordinator = self
        vc.viewModel = dependencyContainer.makeMainViewModel()
        navigationController.delegate = self
        navigationController.pushViewController(vc, animated: true)
    }

    func createAccount() {
        let vc = CreateAccountViewController.instantiate()
        vc.coordinator = self
        navigationController.pushViewController(vc, animated: true)
    }

}
  

Я мог бы, конечно, создать ViewModel for CreateAccountViewController в MainViewModel и передать ViewModel как createAccount параметр в методе, но правильно ли это сделать здесь? Каковы будут последствия модульного тестирования здесь?

Комментарии:

1. Насколько я понимаю, модель представления должна быть только посредником между моделью приложения и представлением. Если модель приложения является гарантированным источником достоверности, двум моделям представления не нужно знать друг о друге.

Ответ №1:

В идеале вы не хотите, чтобы обе ViewModels взаимодействовали друг с другом и разделяли оба элемента.

Один из способов справиться с этим — передать минимальные данные, необходимые для навигации.

 class AppCoordinator : NSObject, Coordinator, UINavigationControllerDelegate {

    var childCoordinators = [Coordinator]()
    var navigationController: UINavigationController
    var dependencyContainer : MainDependencyContainer

    func start() {
        let vc = ViewController.instantiate()
        vc.coordinator = self
        let viewModel = dependencyContainer.makeMainViewModel()

        // for specific events from viewModel, define next navigation
        viewModel.performAction = { [weak self] essentialData in
            guard let strongSelf = self else { return }
            strongSelf.showAccount(essentialData)
        }

        vc.viewModel = viewModel
        navigationController.delegate = self
        navigationController.pushViewController(vc, animated: true)
    }

    // we can go further in our flow if we need to
    func showAccount(_ data: AnyObject) {
        let vc = CreateAccountViewController.instantiate()
        vc.viewModel = CreateAccountViewController(with: data)
        vc.coordinator = self
        navigationController.pushViewController(vc, animated: true)
    }
}
  

Идя дальше, вы можете создать конкретный координатор CreateAccountViewController , который будет инициализирован этими данными. start() Метод создаст все, что необходимо для его ViewController.

 // we can go further in our flow if we need to
func showAccount(_ data: AnyObject) {
    let coordinator = CreateAccountCoordinator(data: data, navigationController: navigationController)
    coordinator.start()
    childCoordinators.append(coordinator)
}
  

В этом последнем примере координатор отвечает только за создание своего представления и передачу важной информации следующему координатору, когда это необходимо. ViewModel доступна только для своего представления, и в конечном итоге представление не знает об обоих. В вашем случае это может быть хорошей альтернативой.

Наконец, вы можете протестировать, используя абстракцию протокола, чтобы убедиться performAction , что триггеры showAccount , showAccount создающие дочерний координатор, и так далее.