I am trying to fetch data from an api by clicking on a 'Fetch' button and show them via an ForEach loop in a ScrollView.
I am using MVVM Model. The fetch itself happens in an ObservableObject class.
Unfortunately the ScrollView does not show the content. If I am using a List instead of a ScrollView it works fine.
Do you have any idea what I am missing here?
Thank you a lot for your help!
import SwiftUI
struct Photo: Identifiable, Decodable {
let id = UUID()
let title: String
}
class ContentViewModel: ObservableObject {
let api = "https://jsonplaceholder.typicode.com/photos"
@Published var photos: [Photo] = []
func fetchData() {
print("Fetching started")
guard let url = URL(string: api) else { return }
URLSession.shared.dataTask(with: url) { data, _, _ in
DispatchQueue.main.async {
self.photos = try! JSONDecoder().decode([Photo].self, from: data!)
print("Fetching successfull. Fetched \(self.photos.count) photos.")
}
}.resume()
}
}
struct ContentView: View {
@ObservedObject var contentVM = ContentViewModel()
var body: some View {
NavigationView {
ScrollView {
ForEach(self.contentVM.photos) { photo in
Text(photo.title)
}
}
.navigationBarTitle("Home")
.navigationBarItems(trailing: Button(action: {
self.contentVM.fetchData()
}, label: {
Text("Fetch")
}))
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
You didn't miss anything, the problem is with renders content, I think. even in your example data displays on real device (iPhone 7 iOS 13.1.1), but with long delay. Try with less content or help ScrollView
with alignment. I tried this - on device works faster, but only on device:
class ContentViewModel: ObservableObject {
let api = "https://jsonplaceholder.typicode.com/photos"
@Published var photos: [Photo] = []
@Published var first100Photos: [Photo] = []
func fetchData() {
print("Fetching started")
guard let url = URL(string: api) else { return }
URLSession.shared.dataTask(with: url) { data, _, _ in
DispatchQueue.main.async {
self.photos = try! JSONDecoder().decode([Photo].self, from: data!)
for index in 0...100 {
self.first100Photos.append(self.photos[index])
}
print("Fetching successfull. Fetched \(self.photos.count) photos.")
}
}.resume()
}
}
struct ContentView: View {
@ObservedObject var contentVM = ContentViewModel()
var body: some View {
NavigationView {
ScrollView(.vertical) {
VStack {
ForEach(self.contentVM.first100Photos) { photo in
Text(photo.title)
}
}
}
.navigationBarTitle("Home")
.navigationBarItems(trailing: Button(action: {
self.contentVM.fetchData()
}, label: {
Text("Fetch")
}))
}
}
}