Home > OS >  Difficulty calling an API for quote - unable to display results in list format (Beginner)
Difficulty calling an API for quote - unable to display results in list format (Beginner)

Time:11-10

My Code:

import SwiftUI

struct Response: Codable {
    var results: [Result]
}

struct Result: Codable {
    var author: String
    var text: String
}

struct ContentView: View {
    @State private var results = [Result]()
    
    var body: some View {
        List(results, id: \.author) { item in // <--- This is where the problem is
            VStack(alignment: .leading) {
                Text(item.text)
                Text(item.author)
            }
        }
        .task{
            await loadData()
                
        }
    }
    
    func loadData() async{
        guard let url = URL(string: "https://type.fit/api/quotes") else {
            print("Invalid URL")
            return
        }
        
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            
            if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
                results = decodedResponse.results
                print(results)
            }
        } catch {
            print("Invalid data")
            }
        }
    }


struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

Tutorial video: https://www.youtube.com/watch?v=MBCX1atOvdA&t=13s

The issue is that the tutorial I am following calls on a different api and uses information that my api does not provide as the id when displaying the results in a List. The tutorial uses an integer that is provided by the api as the index when placing the data into list format. I just am not sure what I should use for my id or if there is a better way I can get the information to display in a list

The api data that I am using is in the format & can be referenced at https://type.fit/api/quotes:

[
  {
    "text": "Genius is one percent inspiration and ninety-nine percent perspiration.",
    "author": "Thomas Edison"
  },
  {
    "text": "text",
    "author": "author"
  }, //and continues on for about 75,000 quotes
]

I have tried to set the id: .author and id: .text both of which throw no errors but I am getting nothing when I run the build

I would like to just end up with a single set of "text" and "author" information that I can display to a user

I have not tried much as I am relatively new to swiftui and xcode. I have a feeling this should be a simple fix and I may just be having a hard time wording my google searches to find a solution.

As previously mentioned, I would like to end up displaying a single pair of results containing 1 "text" and 1 "author"

CodePudding user response:

To get the data from the server you need to match your model structs to the json data. In your case the json data is an array of Quotes. So you need to decode this, given that the author and possibly the text, may be null.

Try this example code, works well for me:

// -- here
struct Quote: Identifiable, Codable {
    let id = UUID() // <-- ensures a unique id
    var author: String?
    var text: String?
    
    enum CodingKeys: String, CodingKey {
        case author, text  // <-- id is missing, it will not be decoded
    }
}

    
struct ContentView: View {
    @State var quotes = [Quote]()  // <-- here
    
    var body: some View {
        VStack {
            if quotes.count > 0 {
                Text(quotes[0].text ?? "no text")  // <-- here
                Text(quotes[0].author ?? "no author").foregroundColor(.blue) // <-- here
            }
        }
 //       List(quotes) { quote in
 //           VStack(alignment: .leading) {
//                Text(quote.text ?? "no text")  // <-- here
//                Text(quote.author ?? "no author").foregroundColor(.blue) // <-- here
//            }
//        }
        .task{
            await loadData()
        }
    }
    
    func loadData() async {
        guard let url = URL(string: "https://type.fit/api/quotes") else {
            print("Invalid URL")
            return
        }
        do {
            let (data, _) = try await URLSession.shared.data(from: url)
            quotes = try JSONDecoder().decode([Quote].self, from: data) // <-- here
        } catch {
            print(error)  // <-- important
        }
    }
}
  • Related