swift - 如何更新MVVM中的变量?

标签 swift mvvm

我正在尝试使用 MVVM。我要去VC2来自VC1 。我正在更新viewModel.fromVC = 1 ,但 VC2 中的值未更新.

这就是我的意思:

有一个viewModel ,其中有一个var fromVC = Int() 。现在,在 vc1 ,我调用viewModel

let viewModel = viewModel()

现在,点击按钮,我正在更新 viewModel.fromVC = 8 。并且,移动到下一个屏幕。在下一个屏幕中,当我打印 fromVC 时然后我得到的值为 0 而不是 8。

这就是VC2的方式看起来像

class VC2 {

    let viewModel = viewModel()

    func abc() {
        print(viewModel.fromVC)
    }

}

现在,我正在调用abc()viewDidLoadfromVC打印为 0 而不是 8。有帮助吗?

最佳答案

对于 MVVM 模式,您需要了解它是一个分为两个不同部分的层:输入和输出。

就输入而言,您的 viewModel 需要捕获来自 viewController 的每个事件,对于输出,这是 viewModel 将数据(格式正确)发送到 viewController 的方式。

所以基本上,如果我们有一个像这样的 viewController:

final class HomeViewController: UIViewController { 

    // MARK: - Outlets

    @IBOutlet private weak var titleLabel: UILabel!

    // MARK: - View life cycle

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    // MARK: - Actions

    @IBAction func buttonTouchUp(_ sender: Any) {
         titleLabel.text = "toto"
    }
}

我们需要将职责提取到 viewModel,因为 viewController 正在处理 touchUp 事件,并拥有要传递给标签的数据。

通过提取此内容,您将正确决定责任,毕竟,您将能够正确测试您的 viewModel 🙌

那么该怎么做呢?简单,让我们看看 future 的 viewModel:

final class HomeViewModel {

    // MARK: - Private properties

    private let title: String

    // MARK: -  Initializer

    init(title: String) {
        self.title = title
    }

    // MARK: - Outputs

    var titleText: ((String) -> Void)?

    // MARK: - Inputs

    func viewDidLoad() {
        titleText?("")
    }

    func buttonDidPress() {
        titleText?(title)
    }
}

现在,通过这样做,您可以保证不同职责的安全,让我们看看如何将 viewModel 绑定(bind)到之前的 viewController :

final class HomeViewController: UIViewController { 

    // MARK: - public var

    var viewModel: HomeViewModel!

    // MARK: - Outlets

    @IBOutlet private weak var titleLabel: UILabel!

    // MARK: - View life cycle

    override func viewDidLoad() {
        super.viewDidLoad()

        bind(to: viewModel)

        viewModel.viewDidLoad()
    }

    // MARK: - Private func

    private func bind(to viewModel: HomeViewModel) {
        viewModel.titleText = { [weak self] title in
            self?.titleLabel.text = title
        }
    }

    // MARK: - Actions

    @IBAction func buttonTouchUp(_ sender: Any) {
         viewModel.buttonDidPress()
    }
}

所以缺少一件事,你会问我“但是如何在 viewController 中初始化我们的 viewModel?”

基本上,您应该再次提取职责,您可以有一个 Screens 层,该层负责创建如下 View :

final class Screens {

    // MARK: - Properties

    private let storyboard = UIStoryboard(name: StoryboardName, bundle: Bundle(for: Screens.self))


    // MARK: - Home View Controller

   func createHomeViewController(with title: String) -> HomeViewController {
        let viewModel = HomeViewModel(title: title)
        let viewController = storyboard.instantiateViewController(withIdentifier: "Home") as! HomeViewController
        viewController.viewModel = viewModel
        return viewController
    }
}

最后做这样的事情:

let screens = Screens()
let homeViewController = screens.createHomeViewController(with: "Toto")

但是主要的课题是带来正确测试的可能性,那么怎么做呢?非常简单!

import XCTest
@testable import mvvmApp

final class HomeViewModelTests: XCTestCase {

    func testGivenAHomeViewModel_WhenViewDidLoad_titleLabelTextIsEmpty() {
        let viewModel = HomeViewModel(title: "toto")
        let expectation = self.expectation("Returned title")

        viewModel.titleText = { title in
            XCTAssertEqual(title, "")
            expectation.fulfill()
        }

        viewModel.viewDidLoad()

        waitForExpectations(timeout: 1.0, handler: nil)
    }

    func testGivenAHomeViewModel_WhenButtonDidPress_titleLabelTextIsCorrectlyReturned() {
        let viewModel = HomeViewModel(title: "toto")
        let expectation = self.expectation("Returned title")

        var counter = 0
        viewModel.titleText = { title in
            if counter == 1 {
                XCTAssertEqual(title, "toto")
                expectation.fulfill()
            }
            counter += 1
        }

        viewModel.viewDidLoad()

        viewModel.buttonDidPress()

        waitForExpectations(timeout: 1.0, handler: nil)
    }
}

就这样了 💪

关于swift - 如何更新MVVM中的变量?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53553410/

相关文章:

ios - Swift:我无法将 Firebase FIRAuth 函数中的变量传回父函数中声明的变量

ios - Swift:对泛型类型 Range 的引用需要参数

objective-c - 使用命名参数实现委托(delegate)

c# - PropertyChanged 事件未正确处理

c# - Caliburn.Micro 将 VM 设置为继承屏幕时,会覆盖 View 中的标题字段

wpf - Validation.ErrorTemplate 未显示

ios - 如何快速增加 TabBar 项目标题的底部偏移量

arrays - 在 Swift 中进行 Web 请求后获取数组中的数据

c# - MVVM灯下的异步命令执行

c# - Caliburn.Micro 的保护子句出错