#MVVM入門
※以下はobjc.io, Issue #13 Architecture, June 2014 By Ash Furrowの日本語訳です。
私は2011年に500pxで私の最初のiOS仕事を得た。私は、大学で2、3年の間請負iOSをしていました しかし、これが最初の本当iOSの仕事でした。
私は美しく設計iPadアプリを作るための唯一のiOS開発者として雇われた。
わずか7週間で、我々は1.0を出荷し、多くの機能と、コードベースに本質的に、より多くの複雑さを追加し、イテレーションし続けた。
自分が何をしているかについて分からなくなることを時々感じました。私は、デザイン・パターンを知っていました – 良いコーダーのように –しかし、私は客観的に構造上の決定の有効性を計るにはあまりに作っていたプロダクトに近すぎたのです。
今までMVCのことを聞いたことはありますか?大規模なビューコントローラと時々呼ばれる。つまり、その当時どう感じたかは確かだ。私は恥ずかしい詳細には触れませんが、それは、私はすべて再びそれを行う必要があった場合、別の意思決定を行うだろうと言うことで十分である。
私が作るだろう重要なアーキテクチャ変更の一つ、
私はそれ以来、開発したアプリで作成した、Model-View-ViewModelと呼ばれるModel-View-Controllerに代わるものを使用することであろう。
MVVMは、まさに何であるか?それがどこから来たのか歴史的文脈に焦点を当てる代わりに、典型的なiOSアプリがどのようなものかを見て、そこからMVVMを導き出してみましょう:
ここで、一般的なMVCの仕組みを参照してください。
Modelはデータを表し、Viewはユーザインタフェースを表し、ViewControllerは、二者間の相互作用を媒介する。クール。
少し考えてみましょう。ViewとViewControllerは、技術的に異なる成分があるが、それらはほとんど常に対になり、一緒に動く。Viewが別のViewControllerとペアにすることができること最後の時間はありますか?またはその逆?では、なぜそれらの接続を形式化しないのですか?
これは、より正確に、あなたはおそらくすでに書いているMVCのコードを記述します。
しかし、iOSアプリケーションにおいて成長する傾向がある大規模なViewControllerに対処を行いません。
典型的なMVCアプリケーションでは、ロジックの多くはViewControllerに配置されます。それのいくつかは確かに、ViewControllerに属しているが、それの多くはMVVMの用語で'プレゼンテーションロジック'と呼ばれ
- NSDateを取得して、それをフォーマットされたNSStringに変えることように、Viewが提示できるものにModelから値を変換すること。
である。
図に何かが、欠けています。プレゼンテーションロジックに配置できるもの。私たちは、これを「ViewModel」と呼び、View/ViewControllerとModelの間に配置します。
良くなった!
この図は、正確にMVVMがMVCの増強版でViewとControllerを繋ぎ、ViewControllerからプレゼンテーションロジックを取り出し、この新しいオブジェクト にプレゼンテーションロジックをViewModel移すことであることを説明します。
MVVMは、複雑なようだが、本質的には、あなたが慣れているMVCアーキテクチャのドレスアップ版である。
なぜそれを使用するのでしょうか?
iOSの上のMVVMの背後にある動機は、私にとっては、とにかく、それは自分のViewControllerの複雑さを低減することです。そして、そのプレゼンテーションロジックのテストが容易になります。それはいくつかの例でこれらの目標を達成する方法を見ることができます。
あなたにこの記事から学んで欲しい3つの本当に重要なポイントがあります。
- MVVMは、存在しているMVCアーキテクチャと互換性がある。
- MVVMは、アプリをテスト可能にする。
- MVVMは、バインディング機構とベストマッチである。
MVVMは基本的にMVCを整えたバージョンであるので、理解するのは容易である。
Personモデルと関連があるViewControllerを取り上げてみよう。
@interface Person : NSObject
- (instancetype)initWithSalutation:(NSString *)salutation firstName:(NSString *)firstName lastName:(NSStirng *)lastName birthDate:(NSDate *)birthDate;
@property (nonatomic, readonly) NSString *salutation;
@property (nonatomic, readonly) NSString *firstName;
@property (nonatomic, readonly) NSString *lastName;
@property (nonatomic, readonly) NSDate *birthDate;
@end
クールです。今、我々は、PersonViewControllerのviewDidLoadでModelのプロパティに基づきラベルにセットしている。
- (void)viewDidLoad {
[super viewDidLoad];
if (self.model.salutation.length > 0) {
self.nameLabel.text = [NSString stringWithFormat:@"%@ %@ %@", self.model.salutation, self.model.firstName, self.model.lastName];
} else {
self.nameLabel.text = [NSString stringWithFormat:@"%@ %@", self.model.firstName, self.model.lastName];
}
}
それは、かなり単純な、ありきたりのMVCです。では、view modelでいかに増強できるか見てみましょう。
@interface PersonViewModel : NSObject
- (instancetype)initWithPerson:(Person *)person;
@property (nonatomic, readonly) Person *person;
@property (nonatomic, readonly) NSString *nameText;
@property (nonatomic, readonly) NSString *birthdateText;
@end
view modelの実装は、次になります。
@implementation PersonViewModel
- (instancetype)initWithPerson:(Person *)person {
self = [super init];
if (!self) return nil;
_person = person;
if (person.salutaion.length > 0) {
_nameText = [NSString stringWithFormat:@"%@ %@ %@", self.person.salutaion, self.person.firstName, self.person.lastname];
} else {
_nameText = [NSString stringWithFormat:@"%@ %@", self.perosn.firstName, self.person.lastName];
}
NSDateFormatter *dateFormatter = [NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"EEE MMMM d, yyyy"];
_birthdateText = [dateFormatter stringForDate:person.birthdate];
return self;
}
@end
クールだ。viewDidLoadのプレゼンテーションロジックをView Modelに移動した。新しいviewDidLoadメソッドは今とてもLightweightである。
- (void)viewDidLoad {
[super viewDidLoad];
self.nameLabel.text = self.viewModel.nameText;
self.birthdateLabel.text = self.viewModel.birthdateText;
}
MVCアーキテクチャから大きく変更していないことがわかる。
それはちょうど周りに移動しただけの同じコードです。
これは、 MVCと互換性のあるライトなViewControllerにつながり、よりテストしやすくなります。
ViewControllerは多くのことを行うので、周知のごとくテストが困難です。MVVMでは、できるだけコードの多くをView Modelへ移動する。
ViewControllerは多くを行わず、View Modelはとてもテストが容易であるので
ViewControllerをテストすることは非常に簡単になる。
見てみましょう。
SpecBegin(Person)
NSString *salutation = @"Dr.";
NSString *firstName = @"first";
NSString *lastName = @"last";
NSDate *birthdate = [NSDate dateWithTimeIntervalSince1970:0];
it (@"should use the salutation available. ", ^{
Person *person = [[Person alloc] initWithSalutation:salutation firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.nameText).to.equal(@"Dr. first last");
});
it (@"should not use an unavailable salutation. ", ^{
Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.nameText).to.equal(@"first last");
});
it (@"should use the correct date format. ", ^{
Person *person = [[Person alloc] initWithSalutation:nil firstName:firstName lastName:lastName birthdate:birthdate];
PersonViewModel *viewModel = [[PersonViewModel alloc] initWithPerson:person];
expect(viewModel.birthdateText).to.equal(@"Thursday January 1, 1970");
});
SpecEnd
このロジックをView Modelに移動していなかった場合は、Viewのラベルの内での値を比較する完全なViewConrollerと付随するViewをインスタンス化しなければならなかったと思います。
不便なレベルだけでなく深刻で脆弱なテストを表しているであろう。
今、私たちは私たちのユニットテストを壊すことを恐れず、好きなようにビュー階層を自由に変更できる。
MVVMを使用してのテストのメリットも、この簡単な例から明確であり、それらは、より複雑なプレゼンテーション·ロジックに於いて、より明白になります。
この単純な例では、Modelが不変であることに注意してください.
私たちは、初期化時にViewModelのプロパティを割り当てることができます。
ミュータブルなModelの場合、
modelのプロパティの変更時、ViewModelが、そのプロパティを更新できるように、結合機構を使用する必要があるだろう。
一度ViewModelのModelが変わったら、Viewのプロパティも同様に更新する必要があります。Modelからの変更は、ViewModelからViewにカスケード配置する必要があります。
OS Xでは、一つはCocoaバインディングを使用することができますが、iOS上で、その豪華さを持っていません、KVOが頭に浮かぶし、それは良い仕事をする。しかしながら、特にもしたくさん結合するプロパティがないそれは単純な結合に対しては模範表現である。
代わりに、ReactiveCocoaを使う。しかしMVVMでReactiveCocoaを使用を強制するものではない。MVVMは、独自の上に立つ大きなパラダイムであり、素敵なバインディング·フレームワークとのみ役割を果たす。
私たちは多くのことを学習した。:
プレーンなMVCからMVVMを導出すこと、互換性のあるパラダイムをしているかを理解すること、テスト容易性の観点からMVVMを見ること、そして結合機構を組み合わせた場合MVVMは最も効果的に機能することを理解すること。
MVVMについての詳細を学ぶことに興味があるなら、より詳細にMVVMの利点を説明するこのブログの記事、あるいは我々は偉大な成功への私の最近のプロジェクトでMVVMを使用する方法については、この記事をチェックできます。
また、十分にテストされ MVVMベースのC-41と呼ばれるアプリを持っています。それはオープンソース化されています。
それをチェックアウトし、ご質問があれば私に知らせてください。