我正在尝试使用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()
中呼叫viewDidLoad
,并且fromVC
打印为0而不是8。有帮助吗?
答案 0 :(得分:0)
对于MVVM
模式,您需要了解它是一个分为两个不同部分的层:输入和输出。
在输入方面,您的viewModel需要捕获viewController中的每个事件,对于Outputs,这是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"
}
}
由于viewController正在处理touchUp事件并拥有要带到标签的数据,因此我们需要提取对viewModel的职责。
通过提取此内容,您将正确确定职责,毕竟,您将能够正确测试viewModel
那怎么办呢?简单,让我们看一下我们的未来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绑定到先前的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
层,该层负责创建如下视图:
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)
}
}
就是这样