こんなエセMVVMになるつもりじゃなかったんです・・
作ったデモアプリと概要
簡単な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: 'アクセストークン' となるようにしてください。※下記参照
使ったライブラリ
使い慣れているものや使ってみたかったものを選定して下記にしました
- Alamofire ネットワーク通信
- ObjectMapper JSONとオブジェクトのマッピング
- Kingfisher 画像のダウンロードとキャッシュ
- SwiftTask Promise拡張
- Bond ViewとViewModelのBinding
- Timepiece NSDate拡張
- R Storyboardなどのリソースをインスタンス化する際のTypo対策
作った背景や作りながらの私の心情
iOS開発を始めてからこれまで肥大になりがちなViewControllerを如何に軽量化するか、どうやってレイヤーごとに責務を分けるかといった問題を解決するために試行錯誤の連続でした。
先日、 @susieyyさん が投稿されていた MVVMをベースに複雑な振る舞いをしっかり把握できるアプリ開発 の記事を見てMVVMに興味を持ち、今回、簡単なデモアプリを作ってみようと思いました。
なんとなく、こんな感じかなと書き始めたもののレイヤーごとに責務をしっかり分けたいのだが、明確な境界線をどこに引けば良いのか。
ViewModelが色々やりすぎだろうか?とか様々な疑問が生まれ戸惑いました。
そして、最終的には、
あれ?私のアプリ、MVVM構成って言えるレベルじゃなくなってる!?という感じにまでなりました
本アプリのエセMVVM構成の流れ
- ViewControllerが画面全体を構成すべくデータをAPI通信クラスに要求する
- APIのレスポンスJSONをModelにマッピングしてModelオブジェクトが生成される
- Modelオブジェクトの値を基にViewBinding用の値やViewの状態を持つViewModelを生成する
- ViewControllerは生成されたViewModelとViewのBindingをする
- 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が便利すぎる!!