Home > OS >  JSON file is not shown in HTTP request while same URL outputs a JSON file in Postman
JSON file is not shown in HTTP request while same URL outputs a JSON file in Postman

Time:07-09

I am not able to make a valid HTTP request using just guard let url = URL(string: "URL") and let (data, _) = try await URLSession.shared.data(from: url)

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

struct Result: Identifiable, Codable {
    let id = UUID()
    var BusStopCode: String!
    var RoadName: String!
}
struct ContentView: View {
    @State private var value = [Result]()
    //var locations = [Locations]()
    var body: some View {
        List(value, id: \.id) { item in
            VStack(alignment: .leading) {
                Text(item.BusStopCode)
                    .font(.headline)
                Text(item.RoadName)
            }
        }
        .task {
            await loadData()
        }
        
    }
    
    func loadData() async {
        guard let url = URL(string: "URL")
        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) {
                value = decodedResponse.value
            }
        } catch {
            print("Invalid data")
        }
        
    }
}

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

I used Postman to see the output and it's like this:

{
    "odata.metadata": "",
    "value": [
        {
            "BusStopCode": "01012",
            "RoadName": "Victoria St",
            "Description": "Hotel Grand Pacific",
            "Latitude": 1.29684825487647,
            "Longitude": 103.85253591654006
        },
        
        ...

However when I use another data set (from iTunes) with the same code, and just changing the variable names in the struct and View, it yields a list when the code is run.

(EDIT: I didn't know strings could be used as IDs (like BusStopCode in my case can be used as long as it's unique) so I changed my question as the real problem was that I wasn't able to make a valid HTTP request to a server which required an account key with just a URL)

CodePudding user response:

your struct for Result is correct with the let id = UUID(), it decodes the json data you provided without errors. I suspect the "response" from the server is not what you think (for example some error message). Try using

 struct Result: Identifiable, Codable {
     let id = UUID()
     var BusStopCode: String?  // <-- here
     var RoadName: String?     // <-- here
 }

You can also use the CodingKeys as mentioned, if you get really scared about the Xcode message.

Could you add print(String(data: data, encoding: .utf8)) just after let (data, _) = try await URLSession.shared.data(from: url), and show us exactly what it prints.

This is the full code I used to test my answer and show that it works:

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

struct Result: Identifiable, Codable {
    let id = UUID()
    var BusStopCode: String?
    var RoadName: String?
}

struct ContentView: View {
    @State private var value = [Result]()
    //var locations = [Locations]()
    var body: some View {
        List(value, id: \.id) { item in
            VStack(alignment: .leading) {
                Text(item.BusStopCode ?? "no BusStopCode")
                    .font(.headline)
                Text(item.RoadName ?? "no RoadName")
            }
        }
        .task {
            await loadData()
        }
        
    }
    
    func loadData() async {
        let json = """
{
    "odata.metadata": "",
    "value": [
        {
            "BusStopCode": "01012",
            "RoadName": "Victoria St",
            "Description": "Hotel Grand Pacific",
            "Latitude": 1.29684825487647,
            "Longitude": 103.85253591654006
        }
]
}
"""
        guard let url = URL(string: "google.com") //link replaced due to privacy reasons
        else { print("Invalid URL")
            return
        }
        do {
           // let (data, _) = try await URLSession.shared.data(from: url)
            // simulated server response, since the url is not provided
            let data = json.data(using: .utf8)!
            if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
                value = decodedResponse.value
            }
        } catch {
            print("Invalid data: \(error)")
        }
    }
}

PS: your url should be https: as per Apple requirements, not http: unless you have set the appropriate NSAppTransportSecurity in your Info.plist

EDIT-1: try this approach, that includes the required AccountKey that you need to use to make a successful request, as per the documentations:

func loadData() async {
    guard let url = URL(string: "http://datamall2.mytransport.sg/ltaodataservice/BusStops")
    else { print("Invalid URL")
        return
    }
    
    let accountKey = "xxxx"  // <-- here your AccountKey
    
    var request = URLRequest(url: url)
    request.httpMethod = "GET"
    request.setValue("Bearer \(accountKey)", forHTTPHeaderField: "Authorization")
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    request.setValue("application/json", forHTTPHeaderField: "Accept")
    
    do {
        let (data, _) = try await URLSession.shared.data(for: request) // <-- here
        print(String(data: data, encoding: .utf8)) // <-- here
        let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
        value = decodedResponse.value
    } catch {
        print("Invalid data: \(error)")
    }
}
  • Related