APIのデータを利用する
Published by @SoNiceInfo at 6/24/2020
概要
SwiftUIでWebサーバにあるAPIのデータを使ってアプリを実装する方法を紹介します。
ここでは「モックデータを利用する」でMVVMパターンに則ってモックデータを作っているので変更は最小限で済みます。
大まかな流れ
テレビ番組の一覧を表示するアプリを目指して作ります。 作成は以下の流れで作成します。
フォルダの構成は右のようになります。
- Modelを作成する(
ProgramModel.swift
) - APIのデータをロードするヘルパーを作る(
APIfetch.swift
) - ViewModelとViewを作成する(
ProgramView.swift
)
Modelを作成する(ProgramModel.swift
)
「モックデータを利用する」と同じです。 Modelではデータの形を定義します。
struct型は入れ子にすることができます。
TV番組はid, name, summary, image(mediumサイズ)を持ちます。
//
// ProgramModel.swift
//
import SwiftUI
struct Img: Hashable, Codable {
var large: String?
var medium: String?
var small: String?
}
struct Program: Hashable, Codable {
let id: Int
let name: String
let summary: String
let image: Img
}
APIのデータを作成する(programs.json
)
APIのデータはprograms.jsonで用意しているものとします。(このファイルは今回の検証に限り利用願います。)
[
{"id": 0, "name": "あるぱか散歩", "summary": "あるぱかさんといっしょに街歩きをしましょう", "image": { "medium": "animal_arupaka"}},
{"id": 1, "name": "ブタトーク", "summary": "MCぶったさんの独壇場", "image": { "medium": "animal_buta"}},
{"id": 2, "name": "とっとこはむはみー", "summary": "はむはみーが大冒険", "image": { "medium": "animal_hamster"}},
{"id": 3, "name": "ひよっと", "summary": "ひよっとちゃんがいろんなことに挑戦するよ", "image": { "medium": "animal_hiyoko"}},
{"id": 4, "name": "わんさんといっしょ", "summary": "わんさんといっしょに歌ったり踊ったりしよう", "image": { "medium": "animal_inu"}}
]
APIのデータをロードするヘルパーを作る(APIfetch.swift
)
APIのデータをモデルの形にロードするヘルパーを作ります。
今回使うProgram Model以外にも汎用的に利用できると思います。
WebデータはURLSession.shared.dataTask
で取得する事ができます。DispatchQueue.main.async
で非同期データを利用できるようにします。
取得したデータはapiFetch
を呼び出す際にcompletion
を渡すことで利用できます。(感想:JavaScriptのコールバックみたいだなと思いました)
//
// APIfetch.swift
//
import SwiftUI
func apiFetch<T: Decodable>(_ url: String, completion: @escaping ([T]) -> Void) {
guard let url = URL(string: url) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { return }
let decoder: JSONDecoder = JSONDecoder()
do {
let resData = try decoder.decode([T].self, from: data)
DispatchQueue.main.async {
completion(resData)
}
} catch {
fatalError("Couldn't load \(url) :\n\(error)")
}
}.resume()
}
ViewModelとViewを作成する(ProgramView.swift
)
ProgramViewModel
でヘルパーを使ってデータを呼び出します。ObservableObject
の使い方については変数を利用するでも紹介しています。
最後にContentView.Swift
でProgramView.swift
を呼び出します。
//
// ProgramView.swift
//
import SwiftUI
class ProgramViewModel: ObservableObject {
// ViewでScrollViewとForEachを組み合わせて使うと
// Updateされない現象が発生するので
// 回避策として初期値に何もないデータを入れています。
// 参考 -> https://stackoverflow.com/questions/59316078/swiftui-foreach-not-correctly-updating-in-scrollview
@Published var data: [Program] = [Program(id: 0, name: "", summary: "", image: Img(medium: ""))]
init() {
// 「モックデータを利用する」を見ていただいた方はコメントアウト
// self.data = dataLoad("programData.json")
// 今回のapiFetchを呼び出し
apiFetch("https://d1v1b.com/programs.json"){ resData in
self.data = resData
}
}
}
// 「モックデータを利用する」から変更はありません。
struct ProgramView: View {
@ObservedObject var programVM = ProgramViewModel()
var body: some View {
ScrollView(.vertical, showsIndicators: false) {
VStack(spacing: 10) {
// 順番にProgramを処理
ForEach(programVM.data, id: \.id) { (program) in
ZStack(alignment: .bottom) {
Image(program.image.medium)
.resizable()
.scaledToFill()
.frame(width: UIScreen.main.bounds.width, height: 300)
.background(LinearGradient(gradient: Gradient(colors: [.yellow, .red, .purple]), startPoint: .bottomLeading, endPoint: .topTrailing))
.clipShape(RoundedRectangle(cornerRadius: 20))
VStack {
Text(program.name)
.font(.largeTitle)
.foregroundColor(.white)
Text(program.summary)
.foregroundColor(.white)
}
}
}
}
}
}
}
struct ProgramView_Previews: PreviewProvider {
static var previews: some View {
ProgramView()
}
}
//
// ContentView.swift
//
// 「モックデータを利用する」から変更はありません。
import SwiftUI
struct ContentView: View {
var body: some View {
ProgramView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
参考
変数を利用するモックデータを利用する
ios - SwiftUI ForEach not correctly updating in scrollview - Stack Overflow