2

Using Swift4, iOS11.1, Xcode9.1,

Trying to match the JSON-file in a decodable struct for Swift4, I end up with the following error message:

"Expected to decode Array<Any> but found a dictionary instead." --> After finding two typo's - there is still something wrong!

The error-message now says: The Error-Messages now says: intValue: Optional(5))], debugDescription: "No value associated with key photos (\"photos\").", underlyingError: nil))

The Coordinates-Initializer inside the Coordinates-Struct (see code below), I got form the following post. Everything else I tried to match exactly with the JSON-File (see at very bottom)...

For now, I don't see any typos - could there be something else I am missing ?

The JSON-File is fetched as follows. Is there maybe anything wrong here ?

    let myTask = session.dataTask(with: myRequest) { (data, response, error) in

        if (error != nil) {
            print("Error1 fetching JSON data")
        }
        else {
            do {
                //Decode retrived data with JSONDecoder and assing type of Station object
                let stationData = try JSONDecoder().decode(Station.self, from: data!)

                //Get back to the main queue
                DispatchQueue.main.async {
                    print(stationData)
                }
            }
            catch let error {
                print(error)
            }
        }
    }
    myTask.resume()

The struct looks as follows (see JSON below):

struct Station: Codable {

    let htmlAttributions: [String]
    let nextPageToken: String
    let results: [Result]
    let status: String

    struct Result: Codable {
        let formattedAddress: String
        let geometry: Geometry
        let icon: String
        let id: String
        let name: String
        let photos: [Photo]
        let placeID: String
        let rating: Double
        let reference: String
        let types: [String]

        struct Geometry: Codable {
            let location: Coordinates
            let viewport: Viewport

            struct Coordinates: Codable {
                let lat: Double
                let lng: Double
                init(from decoder: Decoder) throws {
                    let values = try decoder.container(keyedBy: CodingKeys.self)
                    lat = try values.decode(Double.self, forKey: .lat)
                    lng = try values.decode(Double.self, forKey: .lng)
                }
                enum CodingKeys : String, CodingKey {
                    case lat
                    case lng
                }
            }

            struct Viewport: Codable {
                let northeast: Coordinates
                let southwest: Coordinates

                enum CodingKeys : String, CodingKey {
                    case northeast
                    case southwest
                }
            }

            enum CodingKeys : String, CodingKey {
                case location
                case viewport
            }
        }
        // !!!!!!!!!!!! something wrong here ???? !!!!!!!!!!!
        struct Photo: Codable {

            let height: Int
            let htmlAttributions: [String]
            let photoReference: String
            let width: Int
            enum CodingKeys : String, CodingKey {
                case height
                case htmlAttributions = "html_attributions"
                case photoReference = "photo_reference"
                case width
            }
        }

        enum CodingKeys : String, CodingKey {
            case formattedAddress = "formatted_address"
            case geometry
            case icon
            case id
            case name
            case photos
            case placeID = "place_id"
            case rating
            case reference
            case types
        }
    }

    enum CodingKeys : String, CodingKey {
        case htmlAttributions = "html_attributions"
        case nextPageToken = "next_page_token"
        case results
        case status
    }
}

Here is the JSON File:

{
   "html_attributions" : [],
   "next_page_token" : "F3ddaOzOcyo94AA2skDm",
   "results" : [
      {
         "formatted_address" : "Strasse 1, 6003 Luzern, Switzerland",
         "geometry" : {
            "location" : {
               "lat" : 47.04951260000001,
               "lng" : 8.310404999999999
            },
            "viewport" : {
               "northeast" : {
                  "lat" : 47.0508615802915,
                  "lng" : 8.311753980291503
               },
               "southwest" : {
                  "lat" : 47.0481636197085,
                  "lng" : 8.309056019708498
               }
            }
         },
         "icon" : "https://maps.gstatic.com/mapfiles/place_api/icons/generic_business-71.png",
         "id" : "a3d600a6e78105b6ce2b5f5a3fac98ca1910a09b",
         "name" : "Luzern",
         "photos" : [
            {
               "height" : 4000,
               "html_attributions" : [
                  "\u003ca href=\"https://maps.google.com/maps/contrib/113951418385089253589/photos\"\u003eAlex Marcu\u003c/a\u003e"
               ],
               "photo_reference" : "CmRaAAAAYHK1VHDFlkbzXuMnF2MLEdew-36lgHC2lS1Cxg_DELgP-ckZH7G6aa-81LGDpR5rPZY1XMw64mytsjXIrdB5n3QQmXjGgelwZEbHaetT2jpy9SeaHDH3qUGGAUW-7BtZEhCxXy2dxGSv6A_g7fipsCr5GhRZlPuliykokXIkqfqIN_vMWzmYyA",
               "width" : 3000
            }
         ],
         "place_id" : "ChIJqQIbhpj7j0cRjUguIM__gZw",
         "rating" : 4.4,
         "reference" : "CmRSAAAAzBZCshpypXcbMhrBQIdK2zISd3Q40QRSFO0KKhIrTejnGiZIoASuVqCVtmNBnFsodLWrYtOP-RmwCqDBDVbMheeCbFk7f0L8gwixLx_SGhYTDqPd6B2IwPWWXH5Pb6lxEhBoQtWj-kB-g1ZiOZ74hswNGhSd9Kf9Qj1P2_fdQCTO_VCoTU09JA",
         "types" : [
            "transit_station",
            "bus_station",
            "train_station",
            "point_of_interest",
            "establishment"
         ]
      },
      { ...more results... },
      { ...more results... }
   ],
   "status" : "OK"
}
6
  • Could you please post the full error message? It should also say at which point the decoding failed. Commented Nov 28, 2017 at 16:21
  • I did find two typo's and updated the post accordingly - but still, there is something wrong with the Photos-Struct. The Error-Messages now says: intValue: Optional(5))], debugDescription: "No value associated with key photos (\"photos\").", underlyingError: nil)) Commented Nov 28, 2017 at 16:44
  • Thanks, but you're still cutting off the first half of the error message – what does that say? Commented Nov 28, 2017 at 17:00
  • Here is the full error-message from the log: keyNotFound(MyApp.Station.Result.CodingKeys.photos, Swift.DecodingError.Context(codingPath: [MyApp.Station.CodingKeys.results, Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 5", intValue: Optional(5))], debugDescription: "No value associated with key photos (\"photos\").", underlyingError: nil)) Commented Nov 28, 2017 at 17:02
  • Thank you, that helps already (I am learning). But isn't the 6th element the "photos" element and its key is as well "photos" ?? Or am I missing something. What is to do in this case ? Commented Nov 28, 2017 at 17:11

2 Answers 2

2

The error message says that the 6th element (index 5) of the results array does not have a key photos.

Easy solution is to declare the array as optional

let photos: [Photo]?

Extensive solution is to add an initializer to be able to assign a default non-optional value.

Sign up to request clarification or add additional context in comments.

7 Comments

Thank you for your post! Do I have to set every element as Optional ? Since after putting [Photo]? as you suggested, the error message went to brag about index6 (i.e element-7). But there placing a ? does not help... Can you please explain a bit more when to place a ? and when not ? And what could be wrong now with index6 ???
Please read the error message carefully intValue: 5 ... No value associated with key foo means that the 6th dictionary (indices are zero-based) has no key foo and you have to make the corresponding property in the struct optional to avoid that error.
Thanks a lot, vadian, for your help on this. It was again a learning to read carefully the error message ;). Can you maybe answer whether it is good practice to set all the Struct-entries to be an optional ? (since it is not known whether the JSON really contains the value. Or what does the ? in this context really mean ?
The question mark means that the property is an optional (can be nil). Personally I'm not a supporter of declaring all properties thoughtlessly as optional. Optionals are not an alibi for don't-care.
Thanks - therefore one must know which JSON-entries might be missing and which not. Strange enough: With all Struct-entries set to optional, I receive only one element (instead of 20). Don't know why yet... Thanks for your splendid help !!
|
1

With the help of vadian, I finally have found my matching Struct. Thanks a lot vadian!

struct Station: Codable {

    let htmlAttributions: [String]
    let nextPageToken: String
    let results: [Result]
    let status: String

    struct Result: Codable {
        let formattedAddress: String
        let geometry: Geometry
        let icon: String
        let id: String
        let name: String
        let photos: [Photo]?
        let placeID: String
        let rating: Double?
        let reference: String
        let types: [String]

        struct Geometry: Codable {
            let location: Coordinates
            let viewport: Viewport

            struct Coordinates: Codable {
                let lat: Double
                let lng: Double
                init(from decoder: Decoder) throws {
                    let values = try decoder.container(keyedBy: CodingKeys.self)
                    lat = try values.decode(Double.self, forKey: .lat)
                    lng = try values.decode(Double.self, forKey: .lng)
                }
                enum CodingKeys : String, CodingKey {
                    case lat
                    case lng
                }
            }

            struct Viewport: Codable {
                let northeast: Coordinates
                let southwest: Coordinates

                enum CodingKeys : String, CodingKey {
                    case northeast
                    case southwest
                }
            }

            enum CodingKeys : String, CodingKey {
                case location
                case viewport
            }
        }

        struct Photo: Codable {

            let height: Int
            let htmlAttributions: [String]
            let photoReference: String?
            let width: Int
            enum CodingKeys : String, CodingKey {
                case height
                case htmlAttributions = "html_attributions"
                case photoReference = "photo_reference"
                case width
            }
        }

        enum CodingKeys : String, CodingKey {
            case formattedAddress = "formatted_address"
            case geometry
            case icon
            case id
            case name
            case photos
            case placeID = "place_id"
            case rating
            case reference
            case types
        }
    }

    enum CodingKeys : String, CodingKey {
        case htmlAttributions = "html_attributions"
        case nextPageToken = "next_page_token"
        case results
        case status
    }
}

2 Comments

Why do you need this line ? init(from decoder: Decoder) throws { let values = try decoder.container(keyedBy: CodingKeys.self) lat = try values.decode(Double.self, forKey: .lat) lng = try values.decode(Double.self, forKey: .lng) } Your code should works without this.
In fact, this init(from decoder: ....) is no longer needed !! It was needed as for earlier Swift-versions. But since Swift4 you can leave it away ! Also, I normally use the service json4swift (which is great and delivers the struct back from your json entry) - however they somehow did not update fully to Swift4, I think, at least their structs keep the init(from decoder: ....) for some reason. And that's how it sneaked into my code ;) Again, just leave it aside and it will work !

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.