Using API Data
Published by @SoNiceInfo at 6/24/2020
Overview
I'll show you how to use data from an API on a web server.
You can implement this with minimum changes because this page is based on Using Mock Data which conform to MVVM pattern.
Guide
We're going to make an app that displays a list of TV programs.
The creation process is as follows
The structure of the folder looks like the one on the right.
- Creating a Model (
ProgramModel.swift
) - Creating a Helper to Load Data from API (
APIfetch.swift
) - Creating a ViewModel and View (
ProgramView.swift
)
Creating a Model (ProgramModel.swift
)
This section is the same as Using Mock Data. Model defines the struct of the data.
structs can be nested.
A TV program has id, name, summary and image (medium size).
//
// 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
}
Creating API Data (programs.json
)
API Data can be fetched as programs.json. (Please use this file for this verification only.)
[
{"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"}}
]
Creating a Helper to Load Data from API (APIfetch.swift
)
Create a helper to load the API data into the struct of a model.
I think it can be used for other purposes than this program.
Use URLSession.shared.dataTask
to fetch Web data.
Use DispatchQueue.main.async
to do async proccess.
You can use this data by passing completion
when calling apiFetch
.
//
// 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()
}
Creating a ViewModel and View (ProgramView.swift
)
Use the helper in ProgramViewModel
to fetch data.
Understanding Property Wrappers also show you how to use ObservableObject
. Finally, call ProgramView.swift
from ContentView.Swift
.
//
// ProgramView.swift
//
import SwiftUI
class ProgramViewModel: ObservableObject {
// If you use a combination of ScrollView and ForEach in View,
// The problem is that it is not updated.
// As a workaround, I've included a blank data in the initial value.
// Reference -> 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() {
// Call apiFetch this time
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) {
// Process the Program in sequence
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()
}
}
References
Using Mock Dataios - SwiftUI ForEach not correctly updating in scrollview - Stack Overflow