Как взаимодействуют модель представления и представление?

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

protocol ViewControllerProtocol {
    func saveButtonTapped()
}

struct ViewModel : ViewControllerProtocol{
    func saveButtonTapped() {
        //save data...
        print("forward save request")
    }

     func statusArrived(){
       // inform viewcontroller about updating UI
    }
}


class ViewController: UIViewController {

    private var  vm : ViewControllerProtocol?

    required init(viewmodel : ViewModel){
        self.vm = viewmodel
        super.init(nibName: nil, bundle:nil)
    }

    @IBAction func btnSaveTapped(_ sender: Any) {
        vm?.saveButtonTapped()
    }


}

следуя принципу инверсии зависимостей, я могу общаться между контроллером представления -> модель представления.

мой вопрос, как общаться в обратном направлении? т.е. модель представления формы для просмотра контроллера? я думаю, создав другой протокол, и viewcontroller реализует этот протокол....

как общаться в обратном направлении? т.е. модель представления формы для просмотра контроллера?


person Matrix    schedule 08.11.2019    source источник


Ответы (3)


Вы используете замыкания для возврата вызова.

class ViewController: UIViewController {

    private var  vm : ViewControllerProtocol?

    required init(viewmodel : ViewModel){
        self.vm = viewmodel
        super.init(nibName: nil, bundle:nil)

        self.vm.savedSuccessfully = {
            // do stuff
        }
    }

    @IBAction func btnSaveTapped(_ sender: Any) {
        vm?.saveButtonTapped()
    }


}

struct ViewModel : ViewControllerProtocol{

    var savedSuccesfully: (() -> ())?
    func saveButtonTapped() {
        //save data...
        print("forward save request")
    }

     func statusArrived(){
       // inform viewcontroller about updating UI
       savedSuccesfully?()
    }
}
person Vollan    schedule 08.11.2019

Использование шаблона делегата для связи между ViewModel и View допустимо, если ViewModel не владеет представлением, т. е. нет строгой ссылки на представление.

В качестве альтернативного решения вы также можете посмотреть шаблон Observer для передачи сообщений из ViewModel в View. На самом деле это то, что вам предоставляют такие библиотеки, как RxSwift.

Если вы когда-нибудь посмотрите на диаграмму классов, показывающую инверсию зависимостей, в случае MVVM связь между ViewModel и View всегда показана пунктирной линией со стрелкой, указывающей на View. Это просто означает, что ViewModel знает о представлении, но не имеет на него строгой ссылки.

person Ankur Kesharwani    schedule 08.11.2019
comment
да, я думаю, что инверсия зависимостей включает: оба должны зависеть от абстракции. поэтому я думаю, что протокол лучше (поскольку он не имеет сильной ссылки на VC). твои мысли ? - person Matrix; 08.11.2019
comment
но тогда это будет похоже на цикл, VM реализует протокол на основе VC, VC реализует протокол на основе VM. хотя у них обоих слабые ссылки. - person Matrix; 08.11.2019
comment
В вашей реализации цикл неплох, если в нем есть одна слабая ссылка. - person Ankur Kesharwani; 12.11.2019
comment
На самом деле ваш ViewController/View будет содержать сильную ссылку на ViewModel, а ViewModel будет содержать слабую ссылку на ViewController. ViewController должен реализовать протоколы ViewModel для получения сообщений от ViewModel. Если ViewController нужно отправить какое-то сообщение во ViewModel, он может просто вызвать один из своих методов. Если вы решите создать абстракции для обоих, то очевидно, что оба должны будут реализовать протоколы друг друга. Здесь важно то, как вы будете предоставлять зависимости. Мое предложение состоит в том, чтобы использовать Construction Injection с использованием неудачных инициализаторов. - person Ankur Kesharwani; 12.11.2019

Добавить замыкание в качестве обработчика завершения

protocol ViewControllerProtocol {
     func saveButtonTapped(completionHandler: ()->())
 }

Реализация

struct ViewModel : ViewControllerProtocol{
    func saveButtonTapped(completionHandler: () -> ()) {
        print("forward save request")

        completionHandler()
    }
}

Вот как вы можете использовать сейчас:-

@IBAction func btnSaveTapped(_ sender: Any) {
     vm?.saveButtonTapped { [weak self] in

         /// From ViewModel -> ViewController
     }
 }

Не забудьте добавить список захвата, чтобы избежать цикла сохранения, если вы будете использовать viewController внутри этого замыкания.

person ranjit.x.singh    schedule 08.11.2019
comment
но что, если VM захочет сообщить VC внутри func statusArrived()? - person Matrix; 08.11.2019
comment
Пожалуйста, проверьте отредактированный пост, вы можете использовать ту же реализацию и для statusArrived(). - person ranjit.x.singh; 08.11.2019