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