iOS
MVVM
Swift
iOSDay 18

MVVMっぽい構成のデモアプリを公開してみる

More than 3 years have passed since last update.

こんなエセMVVMになるつもりじゃなかったんです・・


作ったデモアプリと概要

SwiftQiitaClient

Simulator Screen Shot 2015.12.17 22.10.42.png

簡単なQiitaクライアントです。

APIから最新の投稿100件を取得しリストに表示して、投稿をタップすることで詳細が見れてストックの付与・削除ができる簡単なアプリをMVVM構成で書きたかったのですが何だか微妙な感じになってしまいました。


動かし方

git clone https://github.com/hachinobu/SwiftQiitaClient.git

cloneしてきたSwiftQiitaClientに移動して

pod install

また、Qiita APIを使用しているのでアクセストークンが必要です。

ここ から個人用アクセストークンを取得してください。

取得したらSwiftQiitaClientフォルダ配下にSecrets.plistという名前のplistファイルを作成して

Dictionary TypeでAccessToken: 'アクセストークン' となるようにしてください。※下記参照

screen.png


使ったライブラリ

使い慣れているものや使ってみたかったものを選定して下記にしました

* Alamofire ネットワーク通信

* ObjectMapper JSONとオブジェクトのマッピング

* Kingfisher 画像のダウンロードとキャッシュ

* SwiftTask Promise拡張

* Bond ViewとViewModelのBinding

* Timepiece NSDate拡張

* R Storyboardなどのリソースをインスタンス化する際のTypo対策


作った背景や作りながらの私の心情

iOS開発を始めてからこれまで肥大になりがちなViewControllerを如何に軽量化するか、どうやってレイヤーごとに責務を分けるかといった問題を解決するために試行錯誤の連続でした。

先日、 @susieyyさん が投稿されていた MVVMをベースに複雑な振る舞いをしっかり把握できるアプリ開発 の記事を見てMVVMに興味を持ち、今回、簡単なデモアプリを作ってみようと思いました。

なんとなく、こんな感じかなと書き始めたもののレイヤーごとに責務をしっかり分けたいのだが、明確な境界線をどこに引けば良いのか。

ViewModelが色々やりすぎだろうか?とか様々な疑問が生まれ戸惑いました。

そして、最終的には、

あれ?私のアプリ、MVVM構成って言えるレベルじゃなくなってる!?という感じにまでなりました


本アプリのエセMVVM構成の流れ


  1. ViewControllerが画面全体を構成すべくデータをAPI通信クラスに要求する

  2. APIのレスポンスJSONをModelにマッピングしてModelオブジェクトが生成される

  3. Modelオブジェクトの値を基にViewBinding用の値やViewの状態を持つViewModelを生成する

  4. ViewControllerは生成されたViewModelとViewのBindingをする

  5. ViewはViewModelの値の更新に応じて動的に更新される


このアプリのレイヤーごとの責務

Modelレイヤー


  • API通信

  • APIからのレスポンスデータを持つ

Viewレイヤー(View/ViewController)


  • ViewControllerは画面を構成すべく必要なデータをAPI通信クラスを通して要求する

  • ViewControllerはViewModelとViewのBindingをする架け橋となる

  • ViewはViewModelで生成した値をレンダリングする

ViewModelレイヤー


  • Modelオブジェクトのデータを基にViewに食わせる値を生成するロジックやViewの状態を持つ

  • 単体のViewで必要となる画像やタップイベントの処理として通信要求やデータの更新を行う


やってみての感想や疑問

ViewModelが通信クラスを介してデータを受け取ってViewとBindingしている自身の値を更新することでViewが動的に変わるようになっているけど、これだとModelとの差異が生まれてしまう

Viewの状態以外でModelに存在しないような変数をViewModelに生やしてしまっていいのだろうか

このデモアプリの場合、Modelオブジェクトの変更を基にViewModelのデータも変化してViewが更新されるというModelオブジェクトが基点となる流れが正解なのだろうか

今回、画像のダウンロードをKingfisherでやっているけれど、元来KingfisherはImageViewを拡張しているので、ImageViewから画像をダウンロードして更新。とViewだけで処理が完結できるのだけれど、わざわざその処理までViewModelでやっているので、これは無駄な気がした

いっそViewModelなしでModelオブジェクトだけで良いのでは?とも思ったが、1つの画面を構成するModelオブジェクトがあるとして、画面のViewごとに、そのModelを引数にしたViewModelを生成することでViewに食わせるデータのロジックなどを各ViewModelに分散できるのは利点だと思った

そして何よりBondが便利すぎる!!