Swift MVVM - использовать протокол для обработки событий viewModel - PullRequest
1 голос
/ 29 апреля 2019

Я пытаюсь использовать MVVM с протоколами делегатов.Когда что-то меняется в модели представления, я хочу вызвать это в контроллере представления.

Когда я хочу использовать протоколы для обработки события модели представления на контроллере представления, я не могу установить протокол на контроллер представления для своего класса модели представления.Это дает мне ошибку:

Тип аргумента (SecondViewController) -> () -> SecondViewController не соответствует ожидаемому типу SecondViewModelEvents

Как я могу сделать это правильно?

Вот код для моей модели представления:

protocol SecondViewModelEvents {
    func changeBackground()
}

class SecondViewModel:NSObject {

    var events:SecondViewModelEvents?

    init(del:SecondViewModelEvents) {
        self.events = del
    }

    func loadDataFromServer() {
        self.events?.changeBackground()
    }

}

И для моего класса контроллера представления:

class SecondViewController: UIViewController,SecondViewModelEvents {

    let viewModel = SecondViewModel(del: self) //Argument type '(SecondViewController) -> () -> SecondViewController' does not conform to expected type 'SecondViewModelEvents'

    @IBAction func buttonPressed(_ sender: Any) {
        self.viewModel.loadDataFromServer()
    }

    func changeBackground() {
        self.view.backgroundColor = UIColor.red
    }

}

Ответы [ 2 ]

1 голос
/ 29 апреля 2019

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

Попробуйте проверить очень информативное и очень подробное Инициализация *Страница 1004 * в официальном руководстве по языку Swift.

Поскольку этот протокол используется для этой конкретной цели, мы можем смело ограничить его классами (обратите внимание на добавление : class к вашему коду.

protocol SecondViewModelEvents: class {
    func changeBackground()
}

Хорошей практикой является использование более описательного именования, а также использование слабых ссылок для объектов делегатов во избежание циклов сильных ссылок.

class SecondViewModel {

    weak var delegate: SecondViewModelEvents?

    init(delegate: SecondViewModelEvents) {
        self.delegate = delegate
    }

    func loadDataFromServer() {
        delegate?.changeBackground()
    }

}

Вы можете попробовать использовать дополнительную модель представления, которая будетполучить инициализацию в соответствующем месте, например awakeFromNib():

class SecondViewController: UIViewController, SecondViewModelEvents {

    var viewModel: SecondViewModel?

    override func awakeFromNib() {
        super.awakeFromNib()

        viewModel = SecondViewModel(delegate: self)
    }

    @IBAction func buttonPressed(_ sender: Any) {
        viewModel?.loadDataFromServer()
    }

    func changeBackground() {
        view.backgroundColor = UIColor.red
    }

}

Или альтернативный подход может состоять в инициализации не необязательной модели представления в UIViewController необходимом инициализаторе:


    // ...

    var viewModel: SecondViewModel

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)

        self.viewModel = SecondViewModel(delegate: self)
    }

    // ...
1 голос
/ 29 апреля 2019

Вам нужно использовать lazy инициализацию как,

lazy var viewModel =  SecondViewModel(del: self)

OR

lazy var viewModel = { [unowned self] in SecondViewModel(del: self) }()
...