Upgrade to Pro — share decks privately, control downloads, hide ads and more …

巨大な機能を VIPER + MicroViewController でいい感じに実装した話

巨大な機能を VIPER + MicroViewController でいい感じに実装した話

こちらの勉強会の登壇資料です

iOS Clean Architecture勉強会 sponsored by Sansan
https://connpass.com/event/158269/

yuichiro_takahashi

January 21, 2020
Tweet

More Decks by yuichiro_takahashi

Other Decks in Technology

Transcript

  1. 巨大な機能を
    VIPER + MicroViewController
    でいい感じに実装した話
    Sansan株式会社
    髙橋 佑一朗

    View Slide

  2. - 髙橋佑一朗 (@ChaaaaaaaaaaanU)
    - Sansan 株式会社
    - iOS アプリエンジニア
    - Flutter やってます
    - ゲーム大好き
    - DbD, Bloodborne, Dark Souls, Pokemon, Smabro, etc...
    Who am I?

    View Slide

  3. - Why VIPER?
    - What’s happened?
    - What MicroViewController
    - How do we combine them?
    - Summary
    - Next Step
    Agenda

    View Slide

  4. Why VIPER?

    View Slide

  5. - アプリが複雑化し ViewController が FatViewController へ進化
    - 更に各ロジックがほとんど ViewController に記述されていて密接に絡み合って
    いる
    - テストも書けないし、コードの見通しも悪くなってしまった
    - どこかを直すとどこかが壊れるピ◯ゴラスイッチ状態
    Why VIPER? - 課題感

    View Slide

  6. - 昔の Sansan のアーキテクチャ
    Why VIPER? - 移行前

    View Slide

  7. - 単一責任の原則に則った、責務の分離が容易になった
    - チームの共通認識として実装時のコミュニケーションが円滑になった
    - 各コンポーネントは Interface に依存させているためテストが容易に
    - ViewController の責務が View の表示とユーザイベントの制御だけになり激痩

    Why VIPER? - 導入した結果

    View Slide

  8. What’s happened?

    View Slide

  9. - 名刺詳細の人物詳細へのリニューアル
    - 名刺に紐づく人物の情報を表示する画面
    - 最新の名刺情報と経歴、メモ、コンタクト、接点のある同僚
    など、表示する情報がとても多い
    - 更に自分が持っている名刺についての情報を表示するタブが
    もう一つある
    - 情報量的にとてもリッチになり、様々な情報が一つの画面で
    閲覧できるためユーザ的には価値が高い
    What’s happened? - 人物詳細

    View Slide

  10. どう実装する?

    View Slide

  11. 単一の VIPER Module として実装すると Fat になるのは
    火を見るより明らか

    View Slide

  12. - 機能が大きすぎて一つの VIPER Module として実装するのは無理がある
    - 各機能毎に独立した ViewController を用意して一つ一つの Module を小さく保
    ちたい
    - 機能毎に分割するとそこそこ ViewController の量が増えるので各
    ViewController の通信の Interface を統一したい
    - TableViewCell に ViewController を attach するのは意外と面倒そう
    What’s happened? - 課題感

    View Slide

  13. What MicroViewController?

    View Slide

  14. - Mercari 発のアーキテクチャで iOSDC 2018 にて発表
    - 画面はもちろん画面を構成するボタンやラベルまで全て ViewController で構成
    するアーキテクチャ
    - 画面を構成する全てのパーツが自身に紐づくロジックを持っており、画面遷移な
    ど複雑になりがちな処理をすっきりさせられる
    - 全てのUIパーツが独立した ViewController であるため並列での作業が行いや
    すい
    - MicroViewController の導入を支援する Mew というライブラリが公開されてい

    What MicroViewController?

    View Slide

  15. - // MicroViewController のイメージ
    What MicroViewController?

    View Slide

  16. How do we combine them?

    View Slide

  17. How do we combine them? - 人物詳細の構造

    View Slide

  18. How do we combine them? - 人物詳細の構造






    View Slide

  19. How do we combine them? - 人物詳細の構造

    View Slide

  20. How do we combine them? - VC間の通信






    View Slide

  21. How do we combine them? - VC間の通信
    public protocol Injectable {
    associatedtype Input
    func input(_ input: Input)
    }
    Mewより抜粋: https://github.com/mercari/Mew

    View Slide

  22. How do we combine them? - VC間の通信
    eventHandler(output)
    childVC.input(input)
    or
    dequeued()

    View Slide

  23. How do we combine them? - 第一層
    - 人物詳細の大元のモジュール
    - ここはまだ標準的な VIPER
    - この層の役割は以下の三つ
    - 人物詳細画面のコアとなる人物の基本データを取得する
    - 人物詳細画面全体にかかわるエラーやイベントの制御
    - 人物詳細全体の遷移処理を行う
    > 遷移に必要なデータと遷移のイベントは子や孫から渡ってくる

    View Slide

  24. How do we combine them? - 初期化と依存性の注入

    View Slide

  25. How do we combine them? - 遷移処理

    View Slide

  26. - PersonDetail モジュールの子供達
    - ここから Router がいなくなる
    - この三つのモジュールは PersonDetail の Router でインスタンス化される
    - この層の役割は主に以下の三つ
    - 子供達の管理
    - 子供達が自身を描画するのに必要データの取得
    - 子供から受け取った各種イベントを PersonDetail へ伝播させる
    How do we combine them? - 第二層

    View Slide

  27. How do we combine them? - モジュールの組み立て

    View Slide

  28. How do we combine them? - 子の生成

    View Slide

  29. - Summary, OwnBizCard モジュールの子供達
    - 大家族
    - ここの ViewController は 自身で依存性の解決を行う
    - この層の役割は主に以下の二つ
    - 親から受け取ったデータを表示する(正常系、エラー系共に)
    - 遷移などのイベントを必要なデータと共に親へ伝える
    How do we combine them? - 第三層

    View Slide

  30. How do we combine them? - 自身の組み立て

    View Slide

  31. - Instantiatable と Router の責務が被ってしまった (依存性の解決)
    - 更に遷移は大元の Router で行うようにしているため必要性がなくなってしまっ

    - 各 ViewController から遷移させるようにすれば Router の必要性は出てくるが
    どっちつかずになってしまいそう
    - 今は以下のルールで運用
    - 原則 Instantiatable を利用しない
    - Instantiatable を利用しなくてはならない場合は Router は作成しない
    - 遷移は Interactable 経由で大元の Router にイベントを伝えて遷移させる
    How do we combine them? - なぜ Router は消えたか?

    View Slide

  32. Summary

    View Slide

  33. - メリット
    - 当初抱えていた課題間は大方解消されていてかなりやりやすかった
    - Input, Output の Interface が統一されているのでデータの受け渡しについて混乱す
    ることはなかった
    - 面倒な ViewController の管理も Mew が引き受けてくれるのでとても楽
    Summary - この構成のメリット

    View Slide

  34. - デメリット
    - Instantiatable と Router の落とし所を見つけるのが少し大変だった
    - モジュールが多くなるのでファイル数とコード量が凄まじい
    - 二つのアーキテクチャを正しく理解しないといけない(自分もチームのメンバーも)
    - 基本的にデータの受け渡しが Input, Output 経由でのフロー同期なので距離が離れ
    た viewcontroller のデータの受け渡しはかなり冗長
    - Mewのメンテナンスがちょっと心配
    Summary - この構成のデメリット

    View Slide

  35. Next Step

    View Slide

  36. Next Step - 今後
    - Instantiatable を拡張して Router を渡せるようにしたい (もう少し VIPER に寄
    せたい)
    - ファイル数とボイラープレートの量が尋常じゃないのでもう少し減らしたい
    - Qiita にそんな記事があったような

    View Slide

  37. Thank you so much!

    View Slide