1 year ago

#328228

test-img

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

Accepted video resources