1 year ago
#328228
kmcclenn
Swift URLSession running asynchronously so it doesn't register in time for onAppear to add data to View
I am trying to call an API and then display that data in my view in Swift. I can successfully make the API call and get the data back, but I am having trouble displaying this data. As seen in the below code, my load function moves on before it is done decoding the data and saving it in a self variable.
Here is the load function:
func loadRestaurant(completed:@escaping () -> ()) {
print("loaded started")
//print(self.restaurants)
guard let url = URL(string: "http://127.0.0.1:8000/api/restaurant/") else {
print("api is down")
return
}
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Basic a21jY2xlbm46ZGV4SVNkZXgzMTQ=", forHTTPHeaderField: "Authorization")
print("request created")
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
//
if let response = try? JSONDecoder().decode([Restaurant].self, from: data) {
print("decoder run")
DispatchQueue.main.async {
self.restaurants = response
//print(self.restaurants)
queue.async {
Thread.sleep(forTimeInterval: Double.random(in: 0...2))
completed()
}
}
return
} else {
print("response decoding failed")
}
}
}.resume()
}
Here is how I am trying to display in in the view:
@State var restaurants = [Restaurant]()
let queue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
var body: some View {
ForEach(restaurants, id: \.id) {item in
HStack {
Text(item.name)
Spacer()
}
}.onAppear(perform: {
print("before running function")
loadRestaurant(completed: {
print("restaurants: \(restaurants)")
})
print("after running function")
})
}
When I run the program, the order of the print statements goes like this:
before running function
loaded started
request created
after running function
decoder run
restaurants: RESTAURANTS DATA
As you can see, the decoder and the final display of the data (where it is saved to the self.restaurants variable) runs after the .onAppear finishes. So, there is no data to be loaded into the display. Does anyone have any ideas on how to fix this? I have tried to add a closure (see the completed() function), but it seems like there is no way to re-draw the view even after the closure function is run. Thank you for your help.
swift
xcode
asynchronous
swiftui
urlsession
0 Answers
Your Answer