iOS/Androidアプリのアーキテクチャ

[モバイルアプリの開発依頼は、フィールドデザインまでお気軽にお問い合わせください。]

ここでは、iOSとAndroidアプリのアーキテクチャについて説明します。ソフトウェアのアーキテクチャには、MVC, MVVMなどさまざまな解釈がありますので、ここでは一例として捉えてください。一般的ではないかもしれないため、ご了承ください。
なお、FlutterなどiOS/Android共通でコードを書けるものもありますが、細かい制御ができないことや、デバッグがしづらい、安定してないことなどから、弊社で開発するようなアプリには適してないため、利用しておりません。そのため、iOSとAndroidのそれぞれに対して説明をしています。


さまざまなモバイルアプリのアーキテクチャ

モバイルアプリの主要なアーキテクチャには、下記のようなものがあります。

  • MVC(Model-View-Controller)
  • MVVM(Model-View-ViewModel)
  • MVP(Model-View-Presenter)

MVCとMVVMの違いは、MVVMがViewとViewModelの間をデータを自動的に同期するかどうかだと思います(諸説あるため断定ができません)。
いろいろなアーキテクチャがありますが、PresentationであるViewの部分と、Business LogicであるModelの部分が分離していることが、重要です。


弊社で採用しているモバイルアプリのアーキテクチャ

弊社では、モバイルアプリの開発には、MVVMのアーキテクチャを採用しております。

WEBアプリケーションでフロントエンドのVue.jsのアーキテクチャでもMVVMを取っていますので、直感的に開発しやすいのは、MVVMなのだろうと思われます。

どのアーキテクチャを使っても良いですが、重要なことは、下記の点です。

  • アプリが落ちないこと、メモリリークしないこと
    当然ですが、アプリが落ちないことがアプリ開発で最重要です。
    モバイルアプリでは、Viewは頻繁に生成されたり破棄されたりします。そのようなケースで、Modelと強結合(参照)していると、Viewが消えた時に、簡単にnull pointerアクセスでアプリが落ちてしまいます。アーキテクチャを考える上で、各モジュールをどのように結合するかが重要になります。また、メモリリークも溜まると、アプリが落ちてしまうため、絶対にしてはなりません。
  • コードが読みやすいこと
    人間の記憶力はそれほど良くないため、コードが膨大になり煩雑になると、内容を把握できなくなり、バグを生みやすくなります。そのため、理解しやすいような単位にコードをわけられるアーキテクチャであることが必要です。
  • 修正・拡張がしやすいこと
    コードは、複数の人が修正・拡張したり、同じ人でも時間が経った後で修正・拡張したりすることがあります。
    例えば、画面デザインを担当する人は画面の配置や形状などのコーディングのみを行います。その時、Viewの部分だけ修正ができれば、他のコードに影響を与えることなく、分業しやすくなります。また、開発後半で、画面デザインを修正することは頻繁にありますので、ロジック部分に影響を与えないアーキテクチャであることは重要です。
  • 再利用がしやすいこと
    機能が綺麗に分かれているアーキテクチャだと、再利用がしやすくなります。

iOSアプリのアーキテクチャ

弊社のiOSの場合、下記のようなMVVMアーキテクチャにしております。

Viewは、SwiftUIでのstruct :Viewに相当します。アプリは複数画面で構成されるため、その画面やダイアログなどの部品の分だけViewが存在します。

ViewModelは、Viewごとに対応し(一部例外あり)、@StateObjectで宣言したclassであり、Viewの生成時にインスタンス化したものです。Modelとは、Notification Centerにて通信します。Viewとは、@Bindingなどで、Data Bindingをして、通信しています。

Modelは、DBやBluetoothモジュール、Webアクセスモジュールなどが相当します。ViewModelからは、メソッドの呼び出しで直接呼ばれます。基本的に、Modelはアプリが動いている時は消えることがないように作っているため、強結合していても問題ありません。ViewModelには、Notification Centerのpostでイベントを通知します。

このように、iOSの場合はNotification Centerで通信し、ModelからViewModelへの結合が全くない仕組みを利用しています。

世の中には、View/ViewModelとModel間をdelegateを用いてやりとりするケースもありますが、その場合、weak宣言にて弱参照することで、アプリが落ちることに対応しています。ただし、delegateが通常複数に対応できない点から(無理に対応しても、構成が複雑になる)、弊社では採用しませんでした。

また、ごくたまにView/ViewModelとModelとのやりとりをData Bindingで行う例もありますが、Bindingするパラメータが膨大になると、動作が複雑化し、把握しきれなくなるということから、弊社では採用しませんでした。そもそも、ModelにViewであるSwiftUIのプロパティーラッパーを使うこと自体が問題なので、使うことはありません。

なお、SwiftUIの場合、ViewModelに書くべきコードもViewに書けてしまうので、そうならないように意識する必要があります。View内にViewModelの内容が入り込むと、規模が大きくなった場合に、コードが見づらくなります。


Androidアプリのアーキテクチャ

弊社のAndroidの場合、下記のようなアーキテクチャにしております。

Viewは、res/layoutにあるactivity_main.htmlなどのXMLファイルが相当します。画面の分だけ、XMLファイルが存在します。

ViewModelは、ActivityやFragmentなどが相当します。通常、画面遷移などのIntentで生成される。Viewに対しては、findViewById()にてXMLファイルのidに関連付け、操作を行います。ViewModel内でデータが更新された際は、コード上で前記idを使い設定をすると、Viewに反映されます。Modelからの通信は、BroadcastReceiverにて、Intentを受信することで行います。

Modelは、DBやBluetoothモジュール、Webアクセスモジュールなどが相当します。ViewModelからは、メソッドの呼び出しで直接呼ばれます。基本的に、Modelはアプリが動いている時は消えることがないように作っているため、強結合していても問題ありません。ViewModelには、Intentでイベントを通知します。

このように、Androidの場合はIntentで通信し、ModelからViewModelへの結合が全くない仕組みを利用しています。

なお、Androidの場合、SwiftUIと同様のView BindingというData更新でViewを自動更新する仕組みがありますが、2024年現在、動作が安定しないため、弊社では使っておりません。便利なので、いずれ、使いたいと思います。