한국어 | English | 日本語
Webアプリケーションエンジニア (経験8.8年)
技術・開発
engineering
ウェブフロントエンドと バックエンド開発を扱います

「開発日誌 - iOSアーキテクチャパターン MVC, MVVM, VIPER」

「iOS開発はWebと似ている部分も多いですが、ViewとControllerが強力に結合しているという独自の特性があります。プロジェクトの規模が拡大するにつれて肥大化するViewControllerを防止し、コードの再利用性と保守性を高めるために、アーキテクチャをどのように進化させるべきかを考察します。」
「初期のMVCパターンの限界を克服するために導入されたMVVMパターンの適用事例と、より明確な役割分担のために選択されたVIPERパターンの構造(Presenter, Interactor, Entity, Router)を分析します。各コンポーネントが持つ具体的な責任と、パターン変更に伴う開発効率性の違いを詳細に記述します。」

iOSは、ある意味でWebアプリケーション開発と非常に似ていると私は考えています。ただし、.NET WebFormのようにViewとControllerが密結合しており、React.jsのレンダリング(フロントエンド)ロジックとViewデータを渡すControllerを分けて考えることができません。初めてSwiftアプリを作成した際、アプリも結局ウェブページとほぼ同じモデルであるため、Webアプリケーション開発と同じ方法で開発しようとしました。MVCとMVVMに関する私の小さな経験をそのまま適用してみました。

MVC / MVVM

従来のWebアプリケーションを作成する際、サーバー側ではController, Application, Service, Repositoryの順に分類して作業していました。HTMLとJavaScriptはViewに、JPAのようなデータレベルはModelに、Modelを活用したすべてのビジネスロジックとViewとPOST/GET通信でイベントをやり取りする中間レイヤーはControllerに該当します。WebアプリケーションにおけるMVCのControllerは、実質的にはViewModelに相当します。Viewを直接描画するのではなく、Viewを描画できるViewModelを渡し、それを処理するのはクライアントエンジン上のSingle Pageアプリケーションだからです。名前を聞けば誰もが知っているようなフロントエンドフレームワークの.js系が、このようなパターンを使用しています。

iOSアプリ開発におけるMassive Controller問題

MVVMパターンをそのまま適用しようとしたところ、iOSでエラーが発生しました。**問題の核心は、View.storyboardViewController.swiftが実質的に一つのViewであるということです。**一般的に、フロントエンドとバックエンドのコードベースがJS、Javaなどに分かれるのとは異なり、iOS SwiftではViewに関する処理をすべて.swiftで行います。ViewController.swiftはControllerという名前を持っていますが、実質的にはViewに該当し、View.storyboardはCSS/HTMLおよびRouterが含まれた概念と見なすことができます。

Swiftは本質的にはMVCパターンです。ただし、言語の特性上、WebアプリケーションのMVCとは少し区別して考える必要があるように思われます。**SwiftではControllerが事実上Viewに該当するため、レンダーに関するロジックをControllerが持つことになります。**Service、Repositoryモジュール化を適切に行っても、ControllerにはViewのレンダーロジックだけでなく、Viewのレンダーに必要なデータ操作に関する「一部の」ビジネスロジックも含まれてしまいます。この問題をMassive Controllerと称します。

初期の開発で使用したMVCパターン

MVCをそのまま適用してみた私の最初のSwiftコードは以下のようでした。Barのような様々なAssetに描画するデータ(Model)を受け取り、統計データを作成し(ビジネスロジック)、それをViewに注入して描画していました(View)。もちろんご覧のように、簡単なUIViewであるにもかかわらず、Viewを描画するロジックだけでなく、ViewModelに関するロジックも見られます。

MVC Code

リファクタリングで使用したMVVMパターン

Controllerが大きくなると、無意識のうちに不安を感じました。コードを書いていると、これは本当に良くないという感覚を強く受けたので、リファクタリングを進めました。**これは、実質的にViewの意味を持つControllerの下に、真の意味での(…)ControllerであるViewModelを配置するということです。**形状、色、サイズに関するものはViewControllerに置き、これに必要なViewModelはViewModelControllerが提供する形です。以下の例を見ると、ViewControllerではViewModelであるmockBudgetsのみをうまく使用しています。View(Controller)とViewModel(Controller)のバインディングにはRxが使われると聞きますが、まだそこまでは適用できていません。

MVVM Code

最近の開発で使用中のVIPERパターン

個人プロジェクトであるため、時間があるときに作業を進めていると、数日、数週間が経つと自分が書いたコードがまったく新しいもののように感じられることがありました。開発を進めるたび、進捗があるたびに、コードを読み直して理解するのに時間がかかるようになり、これはコードがそれぞれ具体的で明確な役割を持たないということに気づきました。もちろん、Service、Repositoryレベルのコードはうまく整理されていたので問題ありませんでしたが、Viewはいくら慣れようとしても難しいものでした。ましてや、私はSwiftを初めて学びながら最初のアプリケーションを作成しているのですから。

VIPERパターン

VIPERは、実のところViewModelの二重化と見なすことができます。既存のビジネスロジックを、Viewに関連するビジネスロジックと、Modelデータレベルに近いビジネスロジック、およびロギング、ネットワークインスタンス管理という二つに細分化したものと理解すると簡単です。前者をPresenter、後者をInteractorと呼びます。これにより、3つだったコンポーネントが4つになりました。さらに、ViewController間の画面遷移のようなsegue処理を担当するRouterが追加され、合計5つのコンポーネントとなります。

私の既存のコードでは、Modelはすでにうまく整理されていたため、この部分はEntityとInteractorにすでに分離されていました。ViewModelにも可能な限りModelに関するロジックは含めないようにしていました。**従来のViewControllerに集中していたViewに関するビジネスロジックをPresenterに移管してみると、既存のViewにはViewModelロジックが多すぎたのだと気づきました。**また、画面遷移(segue)処理も従来のViewControllerが担当していましたが、これはメタ的に考えるとViewController間の移動を調整するものであるため、上位レベルのコンポーネントが管理するのが適切だと考えられました。segue移動に関して、各ViewControllerで重複して持つボイラープレートコードをどのように中央処理するかと考えていたところ、VIPERのRouterを使用すれば良いことが分かりました。

このように適用してみましたが、コンポーネントが5つもあるため、かなりの量のボイラープレートコードが必要になります。面倒でしたが、今後の生産性向上のために適用してみました。その効果はまだ分かりません。ReSwift(Redux on Swift)の概念もあるようですが、React.jsを短期間使ってみた経験では、コンポーネントはせいぜい1、2レイヤーだったので、あえてReduxを適用する必要性を感じず、学ぶ機会がありませんでした。これは後で適用することにするべきでしょう。新しいアーキテクチャパターンや、最近話題のものを適用してみるのも良いかもしれませんが、たとえ個人開発であっても、迅速にデプロイすることがより重要になるでしょう。

「開発日誌 - iOSアーキテクチャパターン MVC, MVVM, VIPER」
Author
Aaron
Posted on
Licensed Under
CC BY-NC-SA 4.0
CC BY-NC-SA 4.0
同じカテゴリーの関連記事
最新記事
LLMフィルターが奪う会話の筋肉とコミュニケーション様式
会話における無礼さを濾過し、洗練された回答を生成するLLMツールが日常化した現代において、私たちは本当に思慮深い会話をしているのだろうか?リアルタイムのコミュニケーションにおける数多くの失敗を通じて磨かれるべき会話能力が、外部ツールに依存することで退化している現象と、それがもたらす社会的な不安や世代間の行動様式の変化について考察する。
シニア採用における年俸交渉の最適なタイミングと戦略
年俸交渉は単なる数字の交換ではなく、心理的な駆け引きとタイミングが重要です。本稿では、企業側にとって、候補者が計算的な態度を取りがちな最終合格後よりも、採用プロセスの初期段階から段階的に交渉を進めることが、なぜより効率的であり、率直な情報の共有に繋がるのかを考察します。
法治主義の限界と人間の多様性
全ての人間の行為を単一の法体系で規制できるという信念は、傲慢であるかもしれない。この記事は、中世の階層的な統制から脱却し、現代の無限の自由を手に入れた人類が直面する法治主義の逆説と、多様性という名のもとに深化する社会的強制力と他者への悪魔化現象を鋭く分析する。
토스트 예시 메세지