[SwiftUI][CoreData] SwiftUI と MVVM で始める CoreData 入門 (その6:View の改良)

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

環境&対象

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

  • macOS Catalina 10.15.7
  • Xcode 12.2
  • iOS 14.2

以下の点を改良していきます。

  • TODOItem 追加時に、タイトル等を指定できるようにする
  • TODOItem に、完了フラグを追加する
  • TODOItem 表示の改良(タイトル、詳細を表示)

リファクタリング

UITest では、要素を取得してから操作やテストを行う必要があるため、コードが煩雑になりがちです。

解決策の1つとして こちらで紹介されている Page Object というパターンがあります。

UITest でも、XML の XPath 等と同様に、要素取得が煩雑になりがちなので、同じパターンが使えます。

この Page Object を使って、UITest を読みやすくなるように リファクタリングしていきます。

TODOList のビューを Page Object 化

まずは、PageObject の Protocol を定義しました。

TestPage protocol

コード解説
  1. UI 要素は、XCUIApplication 経由で取得するので、各ページで 保持します

PageObject に準拠する形で、ビューごとに PageObject を作っていきます。考慮すべき点は以下です。

  • UI操作を public なメソッドとして提供する
  • ビュー内部の情報を不必要に外部に提供しない
  • Page Object 内で、assertion (テスト) しない

現在のページ向けの PageObject (TODOListPageObject) を作りました。

TODOListPageObject

コード解説
  1. UI 操作に必要なコンポーネントを、計算プロパティで定義しています
  2. 追加操作をメソッドにして提供しています
  3. Edit ボタン押下経由での要素削除メソッドです
  4. 左スワイプによる要素削除メソッドです

UITest のリファクタリング

この PageObject を使って、テストコードを追加します。

test_addAndRemoveItem_afterLaunch_todoItemsShouldBeUpdated

コード解説
  1. Add ボタンを押すことで、行が1づつ増えていくことをテスト
  2. Edit ボタン経由、行を左スワイプ それぞれで要素を削除し、行が減ることをテスト

随分テストが読みやすくなったと思います。

新規 TODOItem 作成時に、Title や Detail を指定可能にする

これまでは、固定文字列で TODOItem を作成していましたが、UI で指定できるようにします。

新しいビューを作り、TODOItem 作成時に、指定できるようにします。

Title/Detail 指定時のUITest

先ほどの UITest を変更していきます。

アイテム追加時に、Title と Detail を TextField 経由で入力できるようにします。

テストコードは以下のようになる予定です。

test_addAndRemoveItem_afterLaunch_todoItemsShouldBeUpdated(updated)

コード解説
  1. Add ボタンをタップすると、新規 TODOItem 作成ビューに遷移しますので、そのビューの PageObject が返されます
  2. Title や Detail を入力し、OK ボタンをおすテスト

新規 TODOItem 作成ビュー向け PageObject

上記のテスト向けに、新規 TODOItem 作成ビュー向けの PageObject を作ります。

TODOListNewItemPageObject

コード解説
  1. ビューの要素をテスト用に取得します
  2. Title, Detail を TextField に入力するためのメソッドです
  3. OKボタン、CANCELボタンの入力メソッドです

また作成していないビューの PageObject を作成するのは不思議な感じですが、こうして コードにしてみると、新しいビューのキーとなる要素を明確に理解することができます。

新規 TODOItem 作成ビューの作成

View のテストをアップデートし、新しい View の PageObject も作成しましたので、View の実装を進めていきます。

新規 TODOItem 作成ビュー は、ContentView から開かれて、新規 TODOItem を作る or キャンセル で ContentView に戻ります。

NewTODOItemView

コード解説
  1. 新しい TODOItem のタイトル入力の TextField です
  2. 新しい TODOItem の詳細入力の TextField です
  3. OK ボタンと、Cancel ボタンです。OK 時には、新しい TODOItem を作成し、Cancel 時には、何もせずに、戻ります

続いて、ContentView を先ほど作成した NewTODOItemView を必要に応じて表示するように修正します。

ContentView

上記の実装で、先ほど作成したテストがパスするようになります。

ここまでで、以下ができました。

  • テストコードのリファクタリング
  • TODOItem 追加時に、タイトル等を指定できるようにする

終了フラグの追加

TODOItem なのに、終了しているかどうかのフラグを設定し忘れてました。

モデルテストのアップデート

終了しているかのフラグを isDone という名前のプロパティにするので、テストを更新します。

Initializer で値を指定することもできますが、デフォルトは false ということにします。

以前作成した Model に Item を作成するテストを修正します。

test_createItem_newItem_withCorrectValues

モデルをアップデート

テストができたので、モデルのコードもアップデートし、テストがパスするか確認します。

example

コード解説
  1. 新しいプロパティ isDone を追加します
  2. initializer の引数にも、isDone を追加します
  3. initailizer で、isDone プロパティを初期化します

上記にプラスして、CoreData のモデルもアップデートする必要があります。

以下のように修正しました。

CoreDataモデル修正後
CoreDataモデル修正後

アプリは、直接 TODOItem を作成せずに、TODOItemStore 経由で作成します。ですので、TODOItemStore.createTODOItem メソッドのアップデートも必要となります。

TODOItemStore.createTODOItem

コード解説
  1. 引数で、isDone を設定できるようにしました。
  2. TODOItem と同じ情報を CoreData の Entity にも設定します

CoreData の Entity から TODOItem を作る initializer のアップデートも忘れずに行います。

init from CoreData entity

上記の変更を入れることで、Model_Tests がパスすることが確認できます。

アプリは、ViewModel 経由で操作することになります。
ですが、どのような API が必要になるかまだ分からないので、必要になった時に作ることにします。

  • TODOItem 追加時に、タイトル等を指定できるようにする
  • TODOItem に、完了フラグを追加する
  • TODOItem 表示の改良(タイトル、詳細を表示)

上記の3つのうち、最初の2つができたので、残りの1つに着手します。

現在の表示は、あくまで 確認用なので、きちんと 作っていきます。

TODOItem の表示改善

TODOItem 用 PageObject の作成

テストから着手していきたいのですが、TODOItem の表示は何度か修正が入る気がするので、TODOItem を表す Page Object を作っておきます。

TODOItemPageObject

TODOItem は、List 内の行として複数表示されるので、List の行を表す cell から表示要素を取得するようにします。

この TODOItemPageObject を使って、メインビューでの表示の正しさを確認するテストを作成します。

TODOListView テストの修正

入力として指定した String が表示されていることをテストします。isDone フラグは、作成時には false であるため、そのためのイメージ名が 指定通りかをテストします。

イメージをタップすると、isDone フラグを toggle できるとして、その動作により、イメージ名が変更されるかをテストします。

example

View の実装

テストができましたので、View を実装していきます。

TODOItem 用に View を作成して表示するようにします。

TODOItemView

こんな感じになりました。

TODOItemView view
TODOItemView

先に作ったテストを実行すると、パスすることも確認できました。

なんとなく TODO アプリっぽくなってきました。

次回以降で、以下を実装していきます。

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

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

コメントを残す

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