potatotips #48 (iOS/Android開発Tips共有会) にて発表したスライドです
iOSなアーキテクチャVIPERのススメ@hirothings
View Slide
5分で話すこと1.なぜプロダクト開発にVIPERを採用したか2. VIPERとは3.どうチームに共有したか4.テストについて
自己紹介@hirothings新卒でラーメン屋勤務半年で麺場を任される1年でラーメン屋店長色々あって現在iOSエンジニアMoney Forward, Inc.所属
MVC、MVVMアーキテクチャでアプリを作って感じた課題工夫しないとルーティングがないデータの取得・加工だけをする層がない結果、Viewのロジックとデータに関するロジックが混在する依存性をひとまとめに解決する層がないよって実装が属人化する
新規でプロジェクトを始めるにあたって先の課題は仕組みで解決したい初期リリースに間に合わせるにはそこまで時間がない前提:データバインディングを多用するほどのインタラクティブなアプリではない(プロパティ監視などの実装で担保できると判断)
もう少しかっちりしたアーキテクチャが欲しいスピードも担保したい
VIPER出典: https://cheesecakelabs.com/blog/ios‑project‑architecture‑using‑viper/
VIPERとは単一責任の原則をもとに作られたアーキテクチャ各レイヤーはInterface(Protocol)にのみ依存しているiOSの現場で生まれたアーキテクチャ"VIPER is an application of Clean Architecture to iOS apps."出典: https://www.objc.io/issues/13‑architecture/viper/
VIPERのレイヤーViewInteractorPresenterEntityRouter各レイヤーの頭文字の組み合わせで VIPER
各レイヤーの説明Entityバリューオブジェクト
各レイヤーの説明InteractorPresenterのイベントに応じてユースケースごとにデータの取得、加工をする層バリューオブジェクト(Entity)を操作するビジネスロジックが含まれるViewからは完全に分離している
各レイヤーの説明PresenterViewに対するビジネスロジックを持つ層ViewとRouter, Interactorの橋渡し的存在Viewから受け取ったイベントをもとにInteractorにデータを要求するViewから受け取ったイベントをもとにRouterに画面遷移を依頼するInteractorから受け取ったデータをViewに渡す
各レイヤーの説明ViewPresenterの言われたとおりに画面にデータを表示するPresenterにユーザーのイベントを通知する
各レイヤーの説明Router画面遷移を管理する各レイヤーのインスタンスを生成し、画面を描画する(依存性をひとまとめに解決する層はここ)
※各レイヤーはInterfaceで繋がっていて、別のレイヤーの具体的な実装は知らない
今までの設計に抱いた課題 VIPERを採用するとルーティングがない Routerが担保してくれるViewのロジックとデータに関するロジックが混在 Presenter, Interactorで明確に分離されている依存性をひとまとめに解決する層がない Routerがインスタンスを生成し依存性をまとめる役割を担っている属人化する レイヤーが明確なため属人化しづらい
求めていたものが揃っている感☺iOSの現場で生まれたアーキテクチャだからかあ、なんかしっくりくると思った
チームに共有したい..
サンプルアプリを作ったよさそう →自信を持って勧められる状態にするためシンプルなアプリで実装の流れをチームに知ってもらう
TLが見られる簡単なTwitterクライアント
実装内容ログイン状態に応じたルーティング追加ローディングエラー処理単体テストコードジェネレータ結果:自信を持って勧めたいと思った。チームでも好評だった
各レイヤーはInterfaceで繋がっていて、実態が何をやっているかは知らない=テストが簡単
テストStubとSpyを使ったユニットテストを採用入力 (データ取得)Protocolを使ってスタブに差し替える出力 (View)Protocolを使ってスパイに差し替えるメリットテストしたいこと以外のレイヤーをダミーにすることでテストしたいレイヤー以外の実装に振り回されずに済む参考:Speaker Deck:単体テストのハジメ @yokoyas000
例)タイムライン表示のPresenterのテスト
手順1.モックを作るテストケースごとのダミーデータを用意する
手順2.モックを入力するスタブを作るInteractorの代わりに、指定されたモックを返すだけのスタブを作成
手順3.結果を出力するスパイを作るViewの代わりに、Presenterの結果を出力するだけのスパイを作成
手順4. Presenterのテストを書く
VIPER x UnitTest各レイヤーがInterfaceだけで繋がっているため簡単にダミーに差し替えられたPresenter以外のレイヤーの実装に悩まされず済んだ(View層のUIKitのライフサイクルなど)単一責任なためレイヤーごとのテストもしやすい
レイヤーが多い=ボイラープレートが多い問題Kuriなどのコードジェネレータを使って解決https://github.com/bannzai/Kuriテンプレを用意したら画面ごとに必要なファイルを吐き出してくれる
VIPERで実装してみた感想どのレイヤーが何をすべきか理解しやすい欲しいレイヤーが揃っていてiOS開発してて、しっくりくるInterfaceで各レイヤーが依存しているため簡単にモックに差し替えられるUIロジックとビジネスロジックが分離しているのでテストがしやすい
まとめ前提:現場によって必要なアーキテクチャは違うただiOSやってる人には誰にもわかりやすく取り入れやすいアーキテクチャだと思った
サンプルアプリを公開しましたhttps://github.com/hirothings/VIPERTwitterClient
参考記事iOS Project Architecture: Using VIPERArchitecting iOS Apps with VIPER
ご静聴ありがとうございました