#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
создающие дочерний координатор, и так далее.