LoginSignup
38
36

More than 5 years have passed since last update.

Dependency Injection in MVVM Architecture with ReactiveCocoa Part 2: プロジェクトの設定

Last updated at Posted at 2015-08-31

以下のブログ記事の翻訳です1

Dependency Injection in MVVM Architecture with ReactiveCocoa Part 2: Project Setup


前回のブログ記事では、MVVM (Model-View-ViewModel) とReactiveCocoaの基本的な概念を説明しました。今回の記事からは、実際にMVVMアーキテクチャのアプリを開発し、Swinjectを用いたdependency injectionを行っていきます。MVVMの各コンポーネント間で受け渡されるイベントを扱うためにReactiveCocoaを利用します。このブログ記事では、Model、View、ViewModelをフレームワークとして構成するXcodeプロジェクトの設定方法を学びます。

例題のアプリは、以下のGIFアニメのように、PixabayAPIを通して非同期で画像を検索・ダウンロード・表示します。ソースコードはGitHubのリポジトリからダウンロードできます。

SwinjectMVVMExample ScreenRecord

要求事項

Carthageはインストーラ (Carthage.pkg)でインストールできます。もしCarthageを使うのが初めてであればこちらのチュートリアルを参考にしてください。

PixabayのAPI keyは無料で入手できます。Pixabayでアカウントを作成してログインしてから、APIドキュメントのページにアクセスしてください。あなた固有のAPI keyが "Request parameters" のセクションに表示されます。

プロジェクトの設定の概要

前回の記事で、MVVMアーキテクチャではViewがViewModelに依存し、ViewModelがModelに依存することを学びましたが、実際にModel、View、ViewModelを表すプロトコル、クラス、構造体を作るときにどのようにすれば依存性の方向を強制することができるでしょうか?もしすべての型がひとつのディレクトリの下、あるいはアプリケーションターゲットの下であっても、ひとつの場所に置かれるとすぐにカオスになってしまいます。

Javaや.NETでは、複数のJARDLLファイルでアプリケーションを構成し、各コンポーネントの責任を明確にすることがよく行われます。iOS 8でダイナミックフレームワークが導入されたので、iOSアプリでも複数のダイナミックフレームワークから構成されるように設定するこることが簡単になりました。以下の図のように、Model、View、ViewModelのフレームワークからなるiOSアプリのアーキテクチャは、依存性の方向をViewからViewModelへ、ViewModelからModelへと強制することができ、アプリケーションがそれらの依存性を注入するようにできます。たとえば、ある型をViewModelフレームワークに定義したら、その型からModelフレームワークにある型を参照することはできますが、Viewフレームワークにある型を参照することはできません。

このアーキテクチャにより依存性の向きの一貫性を保つことができ、アプリの開発・テスト・メンテナンスが容易になります。

Diagram-MVVMAppStructure.png

MVVMのフレームワークからなるプロジェクトの設定

それでは、MVVMフレームワークで構成されるXcodeプロジェクトを作成していきましょう。File > New > Project...メニューを選択し、iOS > Application > Single View Applicationの項目を選択してください。Product NameをSwinjectMVVMExampleにし、.SwinjectMVVMExampleをBundle Identifierの末尾に追加してください。LanguageをSwiftにし、DevicesをiPhoneにします。Include Unit Testsだけチェックをつけてください2。その後、どこかローカルな場所にプロジェクトを保存してください。

SwinjectMVVMExampleScreenshotNewProject.png

次に、Model、View、ViewModelフレームワークを追加していきます。今回の例題アプリでは、それぞれExampleModelExampleViewExampleViewModelという名前にします。もしあなた自身のアプリを作成する場合、あなたのアプリ名 + ModelViewViewModelと命名することがお薦めです。たとえば、Foobookという名前のアプリであれば、FoobookModelFoobookViewFoobookViewModelという名前にします。

File > New > Target...メニューを選択し、iOS > Framework & Library > Cocoa Touch Frameworkを選択し、Nextボタンを押してください。次のページでProduct NameをExampleModelに、LanguageをSwiftに設定します。Include Unit Testはチェックされた状態にしてFinishをクリックしてください。同様にしてExampleViewModelExampleViewのフレームワークターゲットを追加してください。

SwinjectMVVMExampleScreenshotNewTarget.png

Project Navigator (Xcodeの左側にあるペイン) のSwinjectMVVMExampleを右クリックし、New Groupを選択してください。グループ名をTestsにします。Project NavigatorでExampleModelExampleViewModelExampleViewExampleModelTestsExampleViewModelTestsExampleViewTestsSwinjectMVVMExampleTestsをドラッグして、下の画像のように整理してください。Project NavigatorでSwinjectMVVMExampleを選択し、画像のようにターゲットの順番を並び替えてください。

SwinjectMVVMExampleScreenshotProjectHierarchy.png

ターゲットのDependencyとLinkの設定をしていきます。Project NavigatorでSwinjectMVVMExampleを選択し、TARGETSからExampleViewModelを選択してください。Build PhasesタブのTarget Dependenciesの下にある+ボタンをクリックし、ExampleModelを追加してください。Link Binary with Librariesの下にある+ボタンをクリックし、ExampleModel.frameworkを追加してください。同様にして、ExampleViewModelExampleViewModel.frameworkExampleViewターゲットの各所に追加します。ExampleModelSwinjectMVVMExampleのターゲット設定はそのままにします。ExampleModelは依存性がなく、SwinjectMVVMExampleはデフォルトで設定が追加されるためです。

SwinjectMVVMExampleScreenshotTargetSettingsExampleModel.png

SwinjectMVVMExampleScreenshotTargetSettingsExampleViewModel.png

SwinjectMVVMExampleScreenshotTargetSettingsExampleView.png

SwinjectMVVMExampleScreenshotTargetSettingsSwinjectMVVMExample.png

ユニットテストのターゲット設定も追加していきます。Project NavigatorでSwinjectMVVMExampleを選択し、TARGETSからExampleModelTestsを選択します。GeneralタブでHost ApplicationをNoneに設定します。同様にしてExampleViewModelTestsExampleViewTestsのHost ApplicationをNoneにします。

再度TARGETSからExampleModelTestsを選択してください。Build PhasesタブでTarget DependenciesからSwinjectMVVMExampleを削除してください。その項目の選択後に-ボタンをクリックすれば削除できます。同様にして、ExampleViewModelTestsターゲットとExampleViewTestsターゲットからSwinjectMVVMExampleのTarget Dependencyを削除します。

TARGETSからExampleViewModelTestsを選択してください。Build PhasesタブでLink Binary with LibrariesにExampleModel.frameworkを追加してください。同様にして、ExampleViewTestsの同じ場所にExampleViewModel.frameworkを追加してください。リンク先のフレームワークに存在する型のスタブやモックを作成するため、これらのリンク設定が必要になります。

SwinjectMVVMExampleScreenshotTargetSettingsExampleModelTests.png

SwinjectMVVMExampleScreenshotTargetSettingsExampleViewModelTests.png

SwinjectMVVMExampleScreenshotTargetSettingsExampleViewTests.png

SwinjectMVVMExampleScreenshotTargetSettingsSwinjectMVVMExampleTests.png

ビルドスキームも設定しましょう。Xcodeのツールバーにあるスキームのボタンをクリックし、Manage Schemes...を選択します。もしExampleModelExampleViewModelExampleViewという名前のスキームがあれば、それらを選択して-ボタンをクリックし削除します。その後、SwinjectMVVMExampleスキームを選択し、Editボタンをクリックします。BuildとTestの設定で、ExampleModelTestsExampleViewModelTestsExampleViewTestsSwinjectMVVMExampleTestsがチェックされていることを確認してください。

SwinjectMVVMExampleScreenshotBuildScheme.png

SwinjectMVVMExampleScreenshotTestScheme.png

これでプロジェクト設定が完了しました。Command-Rでアプリを実行し、その後Command-Uでユニットテストを実行してください。プロジェクトが正しく設定されたか確認できます。もしUmbrella Headerに関するエラーが出たら、以下の画像のように、そのヘッダーファイルのTarget Membershipを修正し、アクセシビリティをPublicに設定してください。XcodeはたまにUmbrella HeaderのTarget Membershipを間違えて設定してしまうことがあります。

SwinjectMVVMExampleScreenshotUmbrellaHeader.png

Carthageによる外部フレームワークのインストール

今回の例題アプリでは、ReactiveCocoa, Himotoki, Alamofire, Swinject, Quick, Nimbleをインストールします。Alamofire, Quick, Nimbleは前回の例題アプリで使用しましたね。今回は型安全なJSONデコードライブラリであるHimotokiをSwiftyJSONの代わりに使用します。Himotokiの詳細については、次回の記事で実際に使用するときに説明します。

上記のフレームワークをCarthageでインストールするため、以下の内容でCartfileという名前のテキストファイルを作成してください。

Cartfile

github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-RC.1"
github "ikesyo/Himotoki" ~> 1.3.0
github "Alamofire/Alamofire" ~> 3.1.2
github "Swinject/Swinject" ~> 1.0.0

github "Quick/Quick" == 0.8.0
github "Quick/Nimble" == 3.0.0

Terminalでcarthage update --no-use-binariesを実行してください3。Carthageがフレームワークのビルドを完了するまで数分 (あるいはしばらく) 待ちます4。もしGitを使用しているなら、Carthageがビルドしたフレームワークを除外するように設定したSwift用の.gitignoreここにあります。

ビルドが完了したらProject NavigatorのSwinjectMVVMExampleを右クリックし、New Groupを選択してください。新しいGroupをFrameworksという名前にします。Carthageがビルドしたフレームワークを、FinderのCarthage/Build/iOSディレクトリからXcodeのFrameworksグループまでドラッグ&ドロップしてください。追加するターゲットを選択するシートが表示されたら、すべてのターゲットのチェックを外してFinishボタンをクリックします。

SwinjectMVVMExampleScreenshotUmbrellaHeaderFrameworksGroup.png

Project NavigatorでSwinjectMVVMExampleを選択し、TARGETSからExampleModelを選択してBuild Phasesタブを開いてください。FrameworksグループにあるAlamofire.framework, Himotoki.framework, ReactiveCocoa.framework, Result.framework5をBuild PhasesタブにあるLink Binary with Librariesまでドラッグ&ドロップしてください。同様にして、ExampleViewModelExampleViewの同じ場所にReactiveCocoa.frameworkResult.frameworkを追加してください。SwinjectMVVMExampleの同じ場所にはSwinject.frameworkを追加してください。ExampleModelTests, ExampleViewModelTests, ExampleViewTestsの同じ場所にReactiveCocoa.framework, Result.framework, Quick.framework, Nimble.frameworkを追加してください。SwinjectMVVMExampleTestsの同じ場所にはSwinject.framework, Quick.framework, Nimble.frameworkを追加してください。AlamofireとHimotokiはModelのみで使用します。Swinjectはアプリケーションとそのユニットテストのみで使用し、QuickとNimbleはユニットテストのみで使用します。

TARGETSでExampleModelTestsを選択し、Build Phasesタブの下の+ボタンをクリックし、New Copy Files Phaseを選択してください。そのPhaseをCopy Frameworksにリネームします。そのPhaseの中で、DestinationをFrameworksに設定してください。Project NavigatorのFrameworksにあるAlamofire.framework, Himotoki.framework, ReactiveCocoa.framework, Result.framework, Quick.framework, Nimble.frameworkCopy Frameworks Phaseにドラッグ&ドロップして追加します。同様にExampleViewModelTestsExampleViewTestsCopy Frameworks Phaseを追加し、上記6つのフレームワークをそのPhaseにドラッグ&ドロップしてください。同様にSwinjectMVVMExampleTestsCopy Frameworks Phaseを追加し、そのPhaseにQuick.frameworkNimble.frameworkを登録してください。SwinjectMVVMExampleTestsはアプリにホストされるので、アプリに入っていないQuickとNimbleだけCopy Frameworks Phaseに追加しています。

SwinjectMVVMExampleScreenshotLinkSettingsExampleModel.png

SwinjectMVVMExampleScreenshotLinkSettingsExampleViewModel.png

SwinjectMVVMExampleScreenshotLinkSettingsExampleView.png

SwinjectMVVMExampleScreenshotLinkSettingsSwinjectMVVMExample.png

SwinjectMVVMExampleScreenshotLinkSettingsExampleModelTests.png

SwinjectMVVMExampleScreenshotLinkSettingsExampleViewModelTests.png

SwinjectMVVMExampleScreenshotLinkSettingsExampleViewTests.png

SwinjectMVVMExampleScreenshotLinkSettingsSwinjectMVVMExampleTests.png

最後の設定はCarthageのREADMEに書かれているとおりです。Project NavigatorでSwinjectMVVMExampleを選択し、TARGETSからSwinjectMVVMExampleを選択してBuild Phasesタブを開いてください。そのタブの下の+ボタンをクリックし、New Run Script Phaseを選択してください。そのPhaseをRun Script for Frameworks by Carthageにリネームし、スクリプトを書くテキストフィールドに以下の行を追加してください。

/usr/local/bin/carthage copy-frameworks

その後、Input Filesの+ボタンを押して以下のパスを追加してください。

$(SRCROOT)/Carthage/Build/iOS/Alamofire.framework
$(SRCROOT)/Carthage/Build/iOS/Himotoki.framework
$(SRCROOT)/Carthage/Build/iOS/ReactiveCocoa.framework
$(SRCROOT)/Carthage/Build/iOS/Result.framework
$(SRCROOT)/Carthage/Build/iOS/Swinject.framework

もう一度Build Phasesタブの下の+ボタンをクリックし、今度はNew Copy Files Phaseを選択してください。そのPhaseの名前はCopy dSYMsとします。そのDestinationをProducts Directoryに変更します。FinderのCarthage/Build/iOS/ディレクトリからXcodeのCopy dSYMs PhaseにAlamofire.framework.dSYM, Himotoki.framework.dSYM, ReactiveCocoa.framework.dSYM, Result.framework.dSYM, Swinject.framework.dSYMをドラッグ&ドロップしてください。Project NavigatorのSwinjectMVVMExampleを右クリックし、New Groupを選択してください。その名前をdSYMsとし、Project Navigatorに自動的に追加されたdSYMファイルをdSYMsグループに入れて整理してください。

SwinjectMVVMExampleScreenshotCarthageScriptAnddSYMs.png

SwinjectMVVMExampleScreenshotdSYMsGroup.png

おめでとうございます!プロジェクトの設定が完了しました。試しにCommand-RCommand-Uでアプリとユニットテストを実行し、問題なく設定ができているか確認してください。もし何かエラーが出たら、GitHub上にあるプロジェクトをダウンロードして、プロジェクトの設定を比較して原因を探してみましょう。

まとめ

Model、View、ViewModelをそれぞれフレームワークとして構成したアプリのアーキテクチャにより、依存性の方向が一貫してViewからViewModel、ViewModelらかModelとなるように保証できることがわかりました。実際にMVVMアーキテクチャのXcodeプロジェクトを作成し、外部フレームワークをCarthageでインストールしました。次回のブログ記事からは、MVVMアーキテクチャを活かし、ReactiveCocoaとSwinjectを用いて例題のアプリを開発していきます。

もし質問、提案、問題などがあれば気軽にコメントをどうぞ。


  1. 訳注: 英語版の著者本人による翻訳のため、翻訳に関わる著作権上の問題はありません。 

  2. 本記事の対象外のため、UI testsは除外しています。 

  3. 古いベータ版のXcodeでビルドされたフレームワーク (zipファイル) をダウンロードしないよう、--no-use-binariesオプションをcarthage updateコマンドに与えています。使用しているXcodeのバージョンがバイナリを作成したXcodeのバージョンに適合する場合、オプションなしでcarthage updateを実行するだけで済みます。 

  4. carthage update --no-use-binariesコマンドでエラーが発生したら--verboseオプションも付けて再度コマンドを実行し、問題を調査してみてください。 

  5. Result.frameworkはReactiveCocoaが使用します。 

38
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
38
36