100DaysOfSwiftUI Day60 Challengeをやってみる (Step2 : メイン画面(Userをリスト表示)実装)

データモデルを作ったので、メインのUIを作っていきます。

まずは、@ObservedObject

まずは、ビューが参照するモデルを記述します。

対象モデルをそのままクラスにしましたので、すごくシンプルです。

このプロパティを追加すると、ContentView_Previewsでエラーが発生しますので、そのエラーにも対応しましょう。
新しいプロパティを追加したのに、Previewでは、その情報が渡されないことがエラーの原因です。なので、追加すればOKです。

メインUIの主役は、List

UITableViewが非常によく使われたように、SwiftUIでは、Listが非常によく使われると思います。

UserListに保持されているUserを行として、表示するリストにします。行には、ユーザーのIDと名前とisActiveを表示することにしましょう。

将来的に階層的な表示にしていくので、NavigationViewにすることも忘れてはいけません。

一発では動きませんでした。Listで扱うためには、IdentifiableにConformしていなければいけないというエラーが発生しました。

Listは、このIdentifiableを拠り所にして、リストの管理をしてくれていますので、無視できません。例えば、行が削除されたときに、そのようなアニメーションになるのは、Listがどの行が削除されたか判断できるからです。

UserをIdentifiableにConformさせます。実は、組み込みタイプで構成されるクラスは、Identifiableになれます。(Codableと似た感じと言えばよいでしょうか)

これで、動くかと思いましたが、今度は、SceneDelegate.swiftでエラーが発生します。

このクラスが実際に動く時に実行されるクラスなのですが、その中でContentViewを作る際に、引数が足りなくてエラーです。
さきほどのPreviewのエラーと同じエラーですね。

UserListを渡すように変更します。

ContentViewの引数が追加された部分です。

シミュレータで実行すると以下のようになります。

Day60EmptyUI

NavigationViewで情報表示

すこしさみしいので、タイトルを表示するようにしました。

“.navigationBarTitle”modifierを使って、”FriendDB”と表示するようにしました。

Previewの改良

もちろん、このままJSONファイル読み込みに進んでも良いのですが、HStackやVStackを使ったデータ表示がどうなるかが、よく見えません。
サンプルデータを作って、Previewで表示するようにしてみましょう。

サンプルを追加する関数を作ります。追加しようとすると、以下のことが必要になります。

サンプル関数の追加

ContentView_Previewコードの修正

上記のコードだけでは、Userのinitializerがないので、うまく動きません。

Userのinitializer

すべての値を渡すInitializerを作るのが理想的かもしれませんが、プロパティが多いので、練習ということも考慮して、id, isActive, name以外のプロパティには、値を設定してしまい、id,isActive,nameを受け取るinitializerを作りましょう。

サンプル追加コードの改良

UIの改良

サンプルを作ってみて良かったことがわかります。レイアウトがすこし変です。Spacer()とかを追加しつつ調整します。

調整後のコード

Day60UpdatedUI

# 普段は、シミュレータでスナップショットを撮るのですが、今回はPreviewにサンプルを与えているので、XCodeの画面をキャプチャしてます。

JSONを読もう!

ここまで作って、JSONファイルを読み込む準備が完了です。

ContentViewに読み込むための関数を追加しましたが、なんと、mutatingをつけた関数の中からでも、その中のClosureからは、selfが変更できないという・・・
まぁ、当たり前と言えば当たり前のことに、XCodeのエラーメッセージから気づきました。

当初は、onAppearで呼び出すことを考えていましたが、SceneDelegate中のContentViewが作成される前段階で読み込むことにしました。

さっそく、
なので、UserListにinitを追加して、URLを使っての初期化を試してみましたが、動かない・・・どうやら、decodeできていないみたい。

初心に返って、UserListの簡単なinitを作るところから。
以下のようなInitializerを作って、ローカルファイルからきちんと読めるか確認しました。

原因は、JSONファイル中では、UserListに対して名称は付いていないからでした。なので、UserListのinit(from decoder)に手を入れないといけません。

注意
この調査中に気づきましたが、Previewのコードをデバッグすることはできません。コードをデバッグしたければ、シミュレータで行うしかないようです。

Root要素が無名配列への対応

今回対象としているJSONファイルは、User相当の要素を配列なのですが、その配列には、名称がついていません。ですが、コードの方は、Decodeしようとする時には、UserListという名称が付いていることを期待しています。

このことが、読み込めない理由でした。

Userの配列を、アプリケーションのデータモデルにしても良いのですが、やはり少し、扱いにくい気がしますので、配列を明示的にクラスにしたUserListという単位で扱いたいです。

JSON Encoder/Decoderで無名配列に対応できないとすると、現在のデータ構造から見直さないといけなくなってしまいます・・・・
それなりに、ありそうな要望な気がしたので、調べてみたところ、ありました!
このような無名の配列は、JSONDecoder/Encoderでは、unkeyedContainerで扱うようです。

UserListのdecode側は以下のようなコードで、無名配列に格納されたUserデータを読み込むことができました。

今回のアプリでは使いませんが、Encode側も対応しました。

WebからJSONを取得してDecodeするコード

うまくEncode/Decodeでいるようになったので、Webから取得してDecodeするコードをUserListのInitializerとして、追加しました。

このInitializerを使って、読み込んだ後に、ContentViewを構築するようにSceneDelegate.swiftを変更しました。

データを読み込むところまでいけました。

Day60DataLoaded

あまりにも、ガタガタなので、すこしレイアウトと表示する要素を変更しました。この辺りは、SwiftUIだから、簡単に大きな変更を行えますね。

Day60DataLoadedImproved

ここまでのまとめ

  • jsonファイルの構造に100%合うようなデータ構造にするのが、すすめやすいが、要検討
  • CodableにConformするために実装な必要なコードを調整することで柔軟にJSONに対応できる

コメントを残す

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