[SwiftUI][CoreData] SwiftUI と MVVM で始める CoreData 入門 (その7:TODO アプリとして改良第1弾)

SwiftUI と CoreData を組み合わせたアプリの作り方を説明します。

環境&対象

以下の環境で動作確認を行なっています。

  • macOS Catalina 10.15.7
  • Xcode 12.2
  • iOS 14.2

前回までで、以下のような TODO アプリができました。

MyTODOアプリ
MyTODOアプリ

今回は、以下を実装していきます。

  • チェックされた要素は非表示にする
  • メインビューリストで、「未実行 TODOItem」「実行済み TODOItem」を選択表示できるようにする

リスト表示対象を isDone == false に限定する

まずは、テストから作成していきます。

テストの作成

3つの要素を作成し、1、2番目の要素にチェックを入れて、リストが適切に更新されるかをテストします。

test_filterTodoItemsOnlyUndone_addAndCheck_todoItemsShouldBeUpdated

コード解説
  1. 先に作成した PageObject を使うと、UI 経由の要素作成もこんなに簡単に書けます
  2. 要素をタップし、isDone フラグを 設定し、表示リストが更新されることをテストしてます

現在は、すべての TODOItem をリスト表示しているので、上記のテストは、failed になります。

Model の修正

現在の List に表示される要素は、TODOItemStore.items が返す要素です。

全ての要素を取得する方法は今後も必要ですので、指定されたフィルターを適用した要素を返すメソッドを作ることにします。

デフォルトは、フィルターなしです。

TODOItemStore

これまでは、TODOItemStore.items を使って、ViewModel から View に渡すデータを保存していましたが、切り替える必要があります。
具体的には、MyTODOViewModel 中で、要素を修正したときに、”todoItems = todoItemStore.items” としていたところを、” todoItems = todoItemStore.filteredItems(NSPredicate(format: “isDone == false”)) “とする必要があります。

これまでのテストを確認すると ほとんどパスするのですが、以下のテストで fail することに気づきます。

test_addOneElement_withSpecifiedData_allDataShouldBeDisplayedCorrectly(修正前)

TODOItem の各プロパティが正しく表示されているかを確認するテストに、TODOItem をタップすることで、チェックマークが入るか確認するテストも入れていたのですが、チェックされると非表示になるので、fail するようになったということです。

別のテストでチェックマークを確認することにして、このテストからは、削除します。

test_addOneElement_withSpecifiedData_allDataShouldBeDisplayedCorrectly(修正前)

# 削除部分をわかりやすくするためにコメントアウトしていますが、削除してしまって問題ありません。

これで、TODOItem はチェックされると非表示になり、テストも全てパスするようになりました。

ここまでで作ったアプリは、以下のような動作をします。

MyTODOAsOf20201215.m4v

リストに表示する TODOItem を選択可能に

次に、以下を実装します。

  • メインビューリストで、「未実行 TODOItem」「実行済み TODOItem」を選択表示できるようにする

現在の仕様では、TODOItem にチェックをいれてしまうと、それ以降 表示することができなくなってしまいます。

実施済み TODOItem と 未実施 TODOItem のどちらを表示するかを設定できるようにして、チェックを入れた後にも表示する方法を作ります。

実装の方針としては、ViewModel に、どちらを表示するか表すフラグを保持することにします。

UI 的には、NavigationBar に、ボタンを配置して、切り替えられるようにします。

テストの作成 (Model テスト)

Model, View のそれぞれでテストすることにします。

まずは、テストを書きます。

モデルに要素を作成し、チェックを操作して、返されるリストが正しいことをテストします。

test_checkFilteredList_createAndCheck_listShouldNotContainCheckedItem

モデルのテストは、これまでのコードでパスします。

次に、View のテストを作成します。

テスト作成 (View, ViewModel)

アプリ設計として、ViewModel が 何を表示するかのフラグを持ち、そのフラグに沿う NSPredicate を提供します。View からは、ViewModel のフラグを操作し、Model は ViewModel の提供する NSPredicate を使ってフィルターすることを想定します。

動作としては、View の切り替えボタンが押された時に、リストの表示が切り替わるということになります。

View のテストとしては、ボタン切り替えで、その時に表示されるべき要素が表示されるかをテストします。

test_filterTodoItems_toggleFilter_todoItemsShouldBeUpdated
コード解説
  1. UITest の動作が安定しなかったので、1秒待つようにしました
  2. テスト用の要素を作成しています
  3. 要素のチェックマークをつけています
  4. リストの行が 非表示になっていることをテスト
  5. リストの表示を切り替えると、1行表示されることをテスト
  6. 再度 表示を切り替えると、非表示になることをテスト

テストコードがコンパイルできないので、TODOListPageObject に toggle するボタンを追加します。

example

コード解説
  1. NavigationBar の一番右に追加されると仮定して、Toggle ボタンを取得できるようにしました
  2. ボタンを使って、filter を toggle する操作をメソッドとして作りました

テストコードのコンパイルはできるようになりました。

ViewModel の実装

先ほど想定した設計「ViewModel が 何を表示するかのフラグを持ち、そのフラグに沿う NSPredicate を提供する。View からは、ViewModel のフラグを操作し、Model は ViewModel の提供する NSPredicate を使ってフィルターする」を実装していきます。

MyTODOViewModel 修正後
コード解説
  1. undone な TODOItem を表示するかのフラグ
  2. フラグに応じて、NSPredicate を返します
  3. リストの表示を切り替えるメソッド。View から呼ばれる予定
  4. 表示に変更が発生する時はこのように、self.todoItems を更新します

View の実装

View のテストの fail の理由は、ボタンが未配置のためです。リストの表示を切り替えるボタンをつけます。

ContentView

コード解説
  1. 新規追加ボタンの右側に切り替えボタンを追加します
  2. どちらを表示しているモードかに応じて表示するアイコンを切り替えます

先ほどの UITest を実行するとパスすることが確認できます。

次回は、以下を予定してます。

  • リファクタリング
  • 既存の TODOItem を編集できるようにする

説明は以上です。
不明な点やおかしな点ありましたら、こちらまで。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です