今回はjsonデータを使って画面表示を行ってみたいと思います
APIを使って戻り値のjsonデータをパースして値を取得するというのはモバイル開発では必ずと言っていいほどやる処理だと思うので、学んでいきます!!
jsonデータの準備
今回は実際にAPIを使ってではなく、jsonのデータを用意して、そのデータを取得するようにします
今回は人間の情報をjsonで定義します
Resourceフォルダを新しく作成して、HumanData.json
を作成しました
[ { "name": "Tom", "age": "28", "city": "AAAAA", "sex": "M", "id": 1001, "extra": { "blood": "O", "hand": "left" }, }, { "name": "Nick", "age": "30", "city": "BBBBB", "sex": "F", "id": 1002, "extra": { "blood": "A", "hand": "left" }, } ]
このjsonデータをモデルにしたものを新しく作成します
Modelフォルダを新しく作成してHuman.swift
を作りました
import SwiftUI struct Human: Hashable, Codable { var id: Int var name: String var age: String var city: String var sex: String var extra: Extra } struct Extra: Hashable, Codable { var blood: String var hand: String }
また、定義したjsonデータをモデルに変換する処理をModelフォルダ配下にData.swiftとして作成します
import UIKit import SwiftUI import CoreLocation let humanData: [Human] = load("HumanData.json") func load<T: Decodable>(_ filename: String) -> T { let data: Data guard let file = Bundle.main.url(forResource: filename, withExtension: nil) else { fatalError("Couldn't find \(filename) in main bundle.") } do { data = try Data(contentsOf: file) } catch { fatalError("Couldn't load \(filename) from main bundle:\n\(error)") } do { let decoder = JSONDecoder() return try decoder.decode(T.self, from: data) } catch { fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)") } }
上記はどちらかというとSwiftの解説になるので、別の機会にできたら行いたいですが、勉強になるのでみてみるといいかもしれません
チュートリアルからダウンロードできるものを少し改良しました
ここまででデータの準備はできたので、あとは表示させるクラスを準備します
HumanRow.swift
を作成します
import SwiftUI struct HumanRow: View { var human: Human var body: some View { Text(human.age) } } struct HumanRow_Previews: PreviewProvider { static var previews: some View { HumanRow(human: humanData[0]) } }
表示するデータであるhuman
をプレビューに渡すことで表示できるようになります
また、下記のようにGroup
で囲むことで複数表示できます
import SwiftUI struct HumanRow: View { var human: Human var body: some View { Text(human.age) } } struct HumanRow_Previews: PreviewProvider { static var previews: some View { Group { HumanRow(human: humanData[0]) HumanRow(human: humanData[1]) } } }
これだと複数のプレビューができてしまいます
一般的なリストは1つのプレビューに複数のリストを表示させると思うので、これだと想像していたのとは違いました
では、リスト表示はどうすればいいのでしょうか?
これはちょっとした発想の転換ですが、Listのクラスを列を表示させれば実現できます
import SwiftUI struct HumanList: View { var body: some View { List { HumanRow(human: humanData[0]) HumanRow(human: humanData[1]) } } } struct HumanList_Previews: PreviewProvider { static var previews: some View { HumanList() } }
ただ、これだと100個データがあったら100行書かないといけなくなり大変です
for文で回すような処理が欲しいところですね
そこで下記のようにするとクロージャーから列を取得でき、それを表示できます
import SwiftUI struct HumanList: View { var body: some View { List(humanData, id: \.name) {human in HumanRow(human:human) } } } struct HumanList_Previews: PreviewProvider { static var previews: some View { HumanList() } }
上記だとユニークなキーを元にして値を取得していますが、モデルでIdentifiableプロトコルを適用させればキーを使わずに表示させることが可能です
import SwiftUI struct Human: Hashable, Codable, Identifiable { var id: Int var name: String var age: String var city: String var sex: String var extra: Extra } struct Extra: Hashable, Codable { var blood: String var hand: String }
import SwiftUI struct HumanList: View { var body: some View { List(humanData) {human in HumanRow(human:human) } } } struct HumanList_Previews: PreviewProvider { static var previews: some View { HumanList() } }
ユニークなデータであることが前提ではありますが、これだと少し簡略にできますね!
最後にリストにNavigation Linkを付けたいと思います
import SwiftUI struct HumanList: View { var body: some View { NavigationView { List(humanData) {human in NavigationLink(destination: HumanDetail()) { HumanRow(human:human) } } .navigationBarTitle(Text("Human")) } } } struct HumanList_Previews: PreviewProvider { static var previews: some View { HumanList() } }
HumanDetailというクラスを空で作った上で上記のようにすれば、Navigation Linkが追加されます
また、navigationBarTitle
で上に表示するタイトルを決めることができます
これはスコープを意識しないとたまに間違えてしまうんですが、NavigationView
内でListの次に書かないとうまく表示されないです
ついでに血液型も追加して表示してみたらこんな感じでした
これでjsonの扱いはある程度できることがわかりました!!
ただ、細かいサイズの指定や場所の指定などできるのかが今の自分の学びの中では未知数なので、調べていきたいです