I'm trying to create a list of data collected from an API and I'm stuck as to how to append the API results to the list. I know it's a bit of a dumb one but I was hoping somebody could eye over the code and help me work out how to implement it
The function which has dummy data inside:
private func getLocations() -> [Crime] {
let locationArray : [Crime] = [
Crime(latitude: 51.4586, longitude: -2.5936),
Crime(latitude: 51.4599, longitude: -2.5939)
]
return locationArray
}
My API call function:
struct Crime: Codable, Identifiable{
var id = UUID()
var latitude: Double
var longitude: Double
}
func getAPI(completion: u/escaping ([Crime])->()){
guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=2022-02") else {return}
URLSession.shared.dataTask(with: crimeURL) { (data, _, _) in
let crime = try! JSONDecoder().decode([Crime].self, from: data!)
DispatchQueue.main.async{
completion(crime)
}
}
.resume()
}
To summarise, I'm trying to extract the longitude and latitude from each entry in the JSON file and append it to locationArray in this format Crime(latitude: 51.4586, longitude: -2.5936)
I'm relatively new to swift so any advice as to how to tackle this would be welcome!
EDIT:
As a basis, this was my attempt at the implementation of it, however it did not function :(
func getLocations() -> [Crime] {
var locationArray : [Crime] = []
getCrimes{ (crimes) in
self.crimes = crimes
}
ForEach(crimes, id: \.self) crime in{
locationArray.append(Crime(latitude:crimes.latitude, longitude: crimes.longitude))
}
return locationArray
}
EDIT 2:
From playing around a bit, I seem to have stumbled upon a "half-solution" below:
func getLocations() -> [Crime] {
var locationArray : [Crime] = []
getCrimes{ (crimes) in
self.crimes = crimes
}
for crime in crimes{
locationArray.append(Crime(latitude:crime.latitude, longitude: crime.longitude))
}
return locationArray
}
But I do receive this error when trying to run the simulator now: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.String, Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "id", intValue: nil)], debugDescription: "Expected to decode String but found a number instead.", underlyingError: nil))
So if anyone knows how to fix this please let me know!
CodePudding user response:
You should add a do-catch block wrapping the try!
func getAPI(completion: u/escaping ([Crime])->()){
guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=2022-02") else {return}
URLSession.shared.dataTask(with: crimeURL) { (data, _, _) in
do {
let crime = try! JSONDecoder().decode([Crime].self, from: data!)
DispatchQueue.main.async{
completion(crime)
}
} catch {
print(error.localizedDescription)
}
}
.resume()
}
The problem probably is because the Crime model doesn't match with the data, try changing the type of your id to Int:
struct Crime: Codable, Identifiable{
var id: Int
var latitude: Double
var longitude: Double
}
CodePudding user response:
The issue is that you didn't provide the correct format for your Crime struct.
If you take a look at your JSON, it will look like this:
{
"category": "drugs",
"location_type": "Force",
"location": {
"latitude": "51.461094",
"street": {
"id": 543495,
"name": "On or near King Square Avenue"
},
"longitude": "-2.591658"
},
"context": "",
"outcome_status": {
"category": "Further action is not in the public interest",
"date": "2022-04"
},
"persistent_id": "7fb542fb5265c59ff0b613c473e69af6cf2b9421313c7cef28d626b1360523c5",
"id": 99489374,
"location_subtype": "",
"month": "2022-02"}
The latitude and longitude values are under the location node. Just adjust the code a little, and you are good to go:
struct Location: Codable {
let latitude: String
let longitude: String
}
struct Crime: Codable {
let location: Location
let id: Int
enum CodingKeys: String, CodingKey {
case location
case id
}
}
func getAPI(completion: @escaping ([Crime])->()){
guard let crimeURL = URL(string: "https://data.police.uk/api/crimes-street/drugs?poly=51.5623,-2.5709:51.4952,-2.7292:51.4008,-2.6235:51.4028,-2.4875:51.4569,-2.4274&date=2022-02") else {return}
URLSession.shared.dataTask(with: crimeURL) { (data, _, _) in
do {
let crime = try JSONDecoder().decode([Crime].self, from: data!)
completion(crime)
} catch {
print(error)
}
}
.resume()
}
//Call your method and you are good to go.
getAPI { crimes in
print(crimes)
}