Hello guys. I've been stuck two days with this issue, hope you can understand my question below because I'm still new with Swift.
I want to create a generic function that request some JSON data from an API, decode the response using the Codable protocol and save it in a @State variable to update the View
Here is what I've done so far.
Here is the function
The thing is, so far I'm only able to print the response, but I can't do something like this:
Because it shows me this error Value of type 'T' has no member ' results'
Here is the View
Somebody could give a hint? I really appreciate it
Thank you.
Dennis
I want to create a generic function that request some JSON data from an API, decode the response using the Codable protocol and save it in a @State variable to update the View
Here is what I've done so far.
Code Block swift struct Response: Codable { var results: [Result] } struct Result: Codable { var trackId: Int var trackName: String var collectionName: String }
Here is the function
Code Block swift func loadData<T: Decodable>(model: T.Type) { guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song") else { print("Invalid URL") return } let request = URLRequest(url: url) URLSession.shared.dataTask(with: request) { data, response, error in if let data = data { if let decodedResponse = try? JSONDecoder().decode(model, from: data) { DispatchQueue.main.async { print(decodedResponse) } return } } print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")") } .resume() }
The thing is, so far I'm only able to print the response, but I can't do something like this:
Code Block swift DispatchQueue.main.async { self.results = decodedResponse.results }
Because it shows me this error Value of type 'T' has no member ' results'
Here is the View
Code Block swift struct TestView: View { @State private var results = [Result]() var body: some View { List(results, id: \.trackId) { item in VStack(alignment: .leading) { Text(item.trackName) .font(.headline) Text(item.collectionName) } } .onAppear { loadData(model: Response.self) } } // Here is the function defined }
Somebody could give a hint? I really appreciate it
Thank you.
Dennis
If you want to make your loadData generic and access the property results of decodedResponse,
You need to tell that type T has a property named results.
For example:
As you see, the protocol HoldingResults is used to tell Swift that the type conforming to it has a property named results.
You can define your loadData using the protocol:
And use it as:
(I replaced if-let-try? to do-try-catch, that's my preference and not required.)
You need to tell that type T has a property named results.
For example:
Code Block struct Response: Codable { var results: [Result] } struct Result: Codable { var trackId: Int var trackName: String var collectionName: String } protocol HoldingResults { associatedtype R var results: [R] {get} } extension Response: HoldingResults {}
As you see, the protocol HoldingResults is used to tell Swift that the type conforming to it has a property named results.
You can define your loadData using the protocol:
Code Block func loadData<T: Decodable>(model: T.Type, completion: @escaping ([T.R])->Void) where T: HoldingResults { guard let url = URL(string: "https://itunes.apple.com/search?term=taylor+swift&entity=song") else { print("Invalid URL") return } let request = URLRequest(url: url) URLSession.shared.dataTask(with: request) { data, response, error in if let data = data { do { let decodedResponse = try JSONDecoder().decode(model, from: data) DispatchQueue.main.async { print(decodedResponse) completion(decodedResponse.results) } return } catch { print(error) } } print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")") } .resume() }
And use it as:
Code Block loadData(model: Response.self) { self.results = $0 }
(I replaced if-let-try? to do-try-catch, that's my preference and not required.)