skip to Main Content

I’m trying to retrieve an integer value from a JSON file in swift. I’m doing this as follows: self.trip.dist = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].dist) but I’m getting this error error.

Here is a link to the JSON file that I’m accessing. I’m trying to access the dist value.

These are my structures:

struct JSONStructure: Decodable {
    var Trip: [TripStructure]
}
 
struct TripStructure: Decodable {
    var LegList: LegListStructure
}
 
struct LegListStructure: Decodable {
    var Leg: [LegStructure]
}
 
struct LegStructure: Decodable {
    var Origin: StationStructure
    var Destination: StationStructure
    var Product: ProductStructure
    var name: String
    var type: String
    var dist: Int
}
 
struct StationStructure: Decodable {
    var time: String
    var name: String
    var date: String
}
 
struct ProductStructure: Decodable {
    var catIn: String
}
 
 
// Just to condense my varibales
struct LocationInfo {
    var iD = String()
    var input = String()
    var lat = String()
    var lon = String()
    var name = String()
    var time = String()
    var date = String()
    var vehicleType = String()
    var transportType = String()
    var dist = String()
    var legName = String()
}

Here is the function I’m using to call the function:

    @Published var trip: LocationInfo = LocationInfo()
    @Published var dest: LocationInfo = LocationInfo()
    @Published var origin: LocationInfo = LocationInfo()
    @Published var arrivalTime = String()
    @Published var travelDate = String()
    @Published var searchForArrival = String()
    @Published var tripIndex = Int()
    @Published var Trips: [Dictionary<String, String>] = []
 
public func FetchTrip() {
        Trips.removeAll()
        
        let tripKey = "40892db48b394d3a86b2439f9f3800fd"
        let tripUrl = URL(string: "http://api.sl.se/api2/TravelplannerV3_1/trip.json?key=(tripKey)&originExtId=(self.origin.iD)&destExtId=(self.dest.iD)&Date=(self.travelDate)&Time=(self.arrivalTime)&searchForArrival=(self.searchForArrival)")
        
        URLSession.shared.dataTask(with: tripUrl!) {data, response, error in
            if let data = data {
                do {
                    let decodedJson = try JSONDecoder().decode(JSONStructure.self, from: data)
                    self.tripIndex = decodedJson.Trip.count - 1
                    
                    for i in 0..<decodedJson.Trip[self.tripIndex].LegList.Leg.count {
                        self.trip.transportType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].type
 
                        if self.trip.transportType == "WALK" {
                            self.origin.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.name
                            self.origin.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.time.prefix(5))
                            self.origin.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.date
 
                            self.dest.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.name
                            self.dest.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.time.prefix(5))
                            self.dest.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.date
                            
                            self.trip.vehicleType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Product.catIn
                            self.trip.dist = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].dist) // This is where the problem lies
                            
                            self.Trips.append(["Origin": self.origin.name, "Destination": self.dest.name, "OriginTime": self.origin.time, "DestTime": self.dest.time, "OriginDate": self.origin.date, "DestDate": self.dest.date, "TransportType": self.trip.transportType, "VehicleType": self.trip.vehicleType, "Distance": self.trip.dist])
                        }
                        
                        else {
                            self.origin.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.name
                            self.origin.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.time.prefix(5))
                            self.origin.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.date
 
                            self.dest.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.name
                            self.dest.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.time.prefix(5))
                            self.dest.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.date
                            
                            self.trip.vehicleType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Product.catIn
                            self.trip.legName = decodedJson.Trip[self.tripIndex].LegList.Leg[i].name
                            
                            self.Trips.append(["Origin": self.origin.name, "Destination": self.dest.name, "OriginTime": self.origin.time, "DestTime": self.dest.time, "OriginDate": self.origin.date, "DestDate": self.dest.date, "TransportType": self.trip.transportType, "VehicleType": self.trip.vehicleType, "LegName": self.trip.legName])
                        }
                    }
                } catch {
                    print(error)
                }
            }
        }.resume()
    }

I am getting this error in the console:
keyNotFound(CodingKeys(stringValue: "dist", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "Trip", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "LegList", intValue: nil), CodingKeys(stringValue: "Leg", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: "dist", intValue: nil) ("dist").", underlyingError: nil))

2

Answers


  1. This site is great: https://app.quicktype.io You just paste in your json and it will write the Codable for you. QED.

    You need to use an Optional Int to extract the dist values because not all legs have one.

    Here’s the minimum necessary to access the dist values:

    func example() {
        let url = URL(string: "https://api.sl.se/api2/TravelplannerV3_1/trip.json?key=40892db48b394d3a86b2439f9f3800fd&originExtId=300101416&destExtId=300101426&Date=2021-04-15&Time=08:00&searchForArrival=1")!
        URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let data = data {
                let travelplanner = try? JSONDecoder().decode(Travelplanner.self, from: data)
                print(travelplanner as Any)
            }
        }
        .resume()
    }
    
    struct Travelplanner: Codable {
        let trip: [Trip]
    
        enum CodingKeys: String, CodingKey {
            case trip = "Trip"
        }
    }
    
    struct Trip: Codable {
        let legList: LegList
    
        enum CodingKeys: String, CodingKey {
            case legList = "LegList"
        }
    }
    
    struct LegList: Codable {
        let leg: [Leg]
    
        enum CodingKeys: String, CodingKey {
            case leg = "Leg"
        }
    }
    
    struct Leg: Codable {
        let origin: Station
        let destination: Station
        let product: Product?
        let name: String
        let type: String
        let dist: Int?
    
        enum CodingKeys: String, CodingKey {
            case origin = "Origin"
            case destination = "Destination"
            case product = "Product"
            case name
            case type
            case dist
        }
    }
    
    struct Station: Codable {
        let name: String
        let time: String
        let date: String
    }
    
    struct Product: Codable {
        let catIn: String
    }
    
    Login or Signup to reply.
  2. I’m glad you found a solution. There are other issues with your code that I feel the need to address.

    The following code has the same effect but is, IMO, much cleaner:

        func fetchTrip() {
            let url = URL(string: "https://api.sl.se/api2/TravelplannerV3_1/trip.json?key=40892db48b394d3a86b2439f9f3800fd&originExtId=300101416&destExtId=300101426&Date=2021-04-15&Time=08:00&searchForArrival=1")!
            URLSession.shared.dataTask(with: url) { (data, response, error) in
                if let data = data {
                    let decodedJson = try! JSONDecoder().decode(Travelplanner.self, from: data)
                    let lastLeg = decodedJson.trip.last?.legList.leg.last
                    self.tripIndex = decodedJson.trip.count - 1
                    self.trip.transportType = lastLeg?.type ?? ""
                    self.origin.name = lastLeg?.origin.name ?? ""
                    self.origin.time = lastLeg.map { String($0.origin.time.prefix(5)) } ?? ""
                    self.origin.date = lastLeg?.origin.date ?? ""
                    self.dest.name = lastLeg?.destination.name ?? ""
                    self.dest.time = lastLeg.map { String($0.destination.time.prefix(5)) } ?? ""
                    self.dest.date = lastLeg?.destination.date ?? ""
                    self.trip.vehicleType = lastLeg?.product?.catIn ?? ""
                    if let lastWalkLeg = decodedJson.trip.last?.legList.leg.last(where: { $0.type == "WALK" }) {
                        self.trip.dist = String(lastWalkLeg.dist ?? 0)
                    }
                    if let lastNonwalkLeg = decodedJson.trip.last?.legList.leg.last(where: { $0.type != "WALK" }) {
                        self.trip.legName = lastNonwalkLeg.name
                    }
    
                    self.trips = (decodedJson.trip.last?.legList.leg ?? [])
                        .map { $0.asDict }
                }
            }
            .resume()
        }
    }
    
    extension Leg {
        var asDict: [String: String] {
            [
                "Origin": origin.name,
                "Destination": destination.name,
                "OriginTime": String(origin.time.prefix(5)),
                "DestTime": String(destination.time.prefix(5)),
                "OriginDate": origin.date,
                "DestDate": destination.date,
                "TransportType": type,
                "VehicleType": product?.catIn ?? "",
            ].merging(type == "WALK" ? ["Distance": String(dist ?? 0)] : ["LegName": name], uniquingKeysWith: { $1 })
        }
    }
    

    For most of the variables, you are assigning to the same var each time through the loop. This is just wasteful. Assign the values from the last element of the loop and be done with it.

    Most of the work being done in the if...else... blocks are identical. Separate out the identical parts and only put the parts that care about the Leg.type in the if/else block. Do you actually use all the class properties in other methods of the class or were you just using them to make local variables in a weird way? (I’m assuming the former in the above code just in case.) If you don’t need all the other properties in other methods, and the only point is to populate the trips array, you could unload most of the code (except the self.trips =... line of course.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search