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()
}
}
}