Home > Software engineering >  How to update view with API Call data
How to update view with API Call data

Time:10-02

I am trying to update my view using data received from a URLSession request.

I can get the data to print to the console with the onAppear:

struct HomeTab: View {

var body: some View {
    
    VStack(spacing: 1) {
        Text(//I want the data to show here)
        Spacer()
    }.padding()

   .onAppear {
    callDevices().getDevices { (response) in
        print(response.devices[0].parentRelations[0].displayName)
        }
    }
}

But I can't figure out how to actually show the data in my view? I want the data to be updated on every onAppear.

How can I achieve this?

Here is my model:

struct Welcome: Codable {
    let devices: [Device]
}

// MARK: - Device
struct Device: Codable {
    let name, type, assignee: String
    let parentRelations: [ParentRelation]
}

// MARK: - ParentRelation
struct ParentRelation: Codable {
    let parent, displayName: String
}

and my API Class:

class callDevices {
    private var project_id: String = "redacted"
    func getDevices(completion: @escaping (Welcome) -> ()) {
        guard let url = URL(string: "https://redacted") else {return}
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("redacted", forHTTPHeaderField: "Authorization")
        
        
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard error == nil else {print(error!.localizedDescription); return }
            guard let data = data else {print("empty data"); return }

            let theData = try! JSONDecoder().decode(Welcome.self, from: data)
            DispatchQueue.main.async {
                completion(theData)
            }
            
        }
        .resume()
    }
}

CodePudding user response:

Forget the completion handler and take advantage of the Combine framework and a @Published property.

In your class adopt ObservableObject and declare a @Published property devices. Assign the devices array to the property in the closure

import Combine

class CallDevices : ObservableObject {
    private var project_id: String = "redacted"
 
    @Published var devices = [Device]() 

    func getDevices() {
        guard let url = URL(string: "https://redacted") else {return}
        
        var request = URLRequest(url: url)
        request.httpMethod = "GET"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("redacted", forHTTPHeaderField: "Authorization")
        
        
        URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard error == nil else {print(error!.localizedDescription); return }
            // guard let data = data else {print("empty data"); return }

            let theData = try! JSONDecoder().decode(Welcome.self, from: data!)
            DispatchQueue.main.async {
                self.devices = theData.devices
            }
            
        }
        .resume()
    }
}

In the view declare a reference to the observable class, in onAppear call getDevices(). The view will be updated after devices has been modified

struct HomeTab: View {

    @StateObject var callDevices = CallDevices()
    
    var body: some View {
        
        VStack(spacing: 1) {
            Text(callDevices.devices.first?.parentRelations.first?.displayName ?? "")
            Spacer()
        }.padding()
    
       .onAppear {
           callDevices.getDevices()  
       }
    }
}
  • Related