サーチ…
リアクティブプログラミングのないMVVM
まず、iOSアプリケーションでModel-View-ViewModel(MVVM)デザインパターンを使用する理由とその理由を簡単に説明します。 iOSが登場したとき、AppleはMVC(Model-View-Controller)をデザインパターンとして使用することを提案しました。彼らはすべての例でそれを示しており、最初の開発者はすべて、ビジネスロジックとユーザーインターフェイスの間の懸念をうまく切り分けていたので、それを使用して満足していました。アプリケーションがより大きく複雑になるにつれて、マッシブビューコントローラ(MVC)と呼ばれる新しい問題が適切に現れました。すべてのビジネスロジックがViewControllerに追加されたため、通常は時間がかかりすぎて大量かつ複雑になりました。 MVCの問題を回避するために、iOS(Model-View-ViewModel(MVVM))パターンの世界に新しいデザインパターンが導入されました。
上記の図は、MVVMの外観を示しています。あなたは標準のViewController + View(ストーリーボード、XIBまたはコード)を持っています。これはMVVMのViewとして機能します(後でテキストビューではMVVMのViewを参照します)。ビューには、ビジネスロジックがあるViewModelへの参照があります。 ViewModelはViewについて何も知らず、Viewへの参照もないことに注意することが重要です。 ViewModelにはモデルへの参照があります。
これは、MVVMの理論的な部分で十分です。詳細はこちらを読むことができます 。
MVVMの主な問題の 1つは、ViewModelに参照がなく、Viewについて何も知らないときに、ViewModel経由でViewを更新する方法です。
この例の主要な部分は、リアクティブプログラミング(ReactiveCocoa、ReactiveSwift、またはRxSwif)なしで、MVVM(より正確には、ViewModelとViewをバインドする方法)を使用する方法を示すことです。 Reactiveプログラミングを使いたいのであれば、MVVMバインディングは本当に簡単に使えるので、さらに優れています。しかし、この例は、リアクティブプログラミングなしでMVVMを使用する方法です。
MVVMの使い方を示す簡単な例を作成しましょう。
私たちのMVVMExampleViewController
は、ラベルとボタンを持つ単純なViewControllerです。ボタンを押すと、ラベルテキストは「Hello」に設定されます。ユーザのユーザ対話を何にするかを決定するのはビジネスロジックの一部なので、ViewModelはユーザがボタンを押したときに何をすべきかを決定する必要があります。 MVVMのビューはビジネスロジックを行うべきではありません。
class MVVMExampleViewController: UIViewController {
@IBOutlet weak var helloLabel: UILabel!
var viewModel: MVVMExampleViewModel?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func sayHelloButtonPressed(_ sender: UIButton) {
viewModel?.userTriggeredSayHelloButton()
}
}
MVVMExampleViewModel
は単純なViewModelです。
class MVVMExampleViewModel {
func userTriggeredSayHelloButton() {
// How to update View's label when there is no reference to the View??
}
}
ビューでViewModelのリファレンスを設定する方法が不思議に思うかもしれません。私は通常、ViewControllerが初期化されているとき、またはそれが表示される前にそれを行います。この単純な例では、私はAppDelegate
次のようにしAppDelegate
:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
if let rootVC = window?.rootViewController as? MVVMExampleViewController {
let viewModel = MVVMExampleViewModel()
rootVC.viewModel = viewModel
}
return true
実際の質問は、ViewModelへのViewへの参照を与えずに、ViewModelからViewを更新する方法です。 (Reactive Programming iOSライブラリは使用しません)
あなたはKVOの使用について考えることができますが、それは事をあまりにも複雑にするでしょう。いくつかの賢明な人々がこの問題を考え、 Bondライブラリを思い付いた。図書館は複雑に見えるかもしれませんが、最初は理解するのが少し難しいので、ちょっとした部分を取ってMVVMを完全に機能させてみましょう。
シンプルで完全に機能するMVVMパターンの中核であるDynamic
クラスを紹介しましょう。
class Dynamic<T> {
typealias Listener = (T) -> Void
var listener: Listener?
func bind(_ listener: Listener?) {
self.listener = listener
}
func bindAndFire(_ listener: Listener?) {
self.listener = listener
listener?(value)
}
var value: T {
didSet {
listener?(value)
}
}
init(_ v: T) {
value = v
}
}
Dynamic
クラスはGenericsとClosureを使用してViewModelをViewとバインドしています。私はこのクラスについて詳細には触れませんが、コメントでこれを行うことができます(この例を短くする)。これらのクラスを使用するようにMVVMExampleViewController
とMVVMExampleViewModel
を更新しましょう。
更新されたMVVMExampleViewController
class MVVMExampleViewController: UIViewController {
@IBOutlet weak var helloLabel: UILabel!
var viewModel: MVVMExampleViewModel?
override func viewDidLoad() {
super.viewDidLoad()
bindViewModel()
}
func bindViewModel() {
if let viewModel = viewModel {
viewModel.helloText.bind({ (helloText) in
DispatchQueue.main.async {
// When value of the helloText Dynamic variable
// is set or changed in the ViewModel, this code will
// be executed
self.helloLabel.text = helloText
}
})
}
}
@IBAction func sayHelloButtonPressed(_ sender: UIButton) {
viewModel?.userTriggeredSayHelloButton()
}
}
更新されたMVVMExampleViewModel
:
class MVVMExampleViewModel {
// we have to initialize the Dynamic var with the
// data type we want
var helloText = Dynamic("")
func userTriggeredSayHelloButton() {
// Setting the value of the Dynamic variable
// will trigger the closure we defined in the View
helloText.value = "Hello"
}
}
それだ。あなたViewModel
今更新することができるView
、それは参照せずにView
。
これは本当に簡単な例ですが、これはどれくらい強力なものかという考えがあると思います。私はMVVMの利点について詳細には触れませんが、MVCからMVVMに切り替えると元に戻りません。ただ試してみてください。