Missing Data with URLSession and JSONDecode in Swi

2019-06-03 02:53发布

问题:

Using Swift4, iOS11.1, Xcode9.1,

The following parseData method almost works. Everything seems to work fine (and a correct JSON-data set is fetched by this URLSession and JSONDecode).

However, what is really surprising to me is the fact that a normal Browser shows different JSON-data (i.e. 20x as much is fetched than compared to this iOS-URLSession approach). Why is that ??

In the code below, you can find two print statements. The first showing the actual URLRequest-string (including all query- and type-parameters). The second prints the number of fetched JSON data-sets.

If you use a Browser and fetch the JSON with the exact same URLRequest-string, then the number of datasets is 20 sets. With the URLSession it is only 1 set

Why this difference in JSON data-length delivered ??????????

Here is PRINT_LOG 1 :

https://maps.googleapis.com/maps/api/place/textsearch/json?query=Cham,%20Langackerstrasse&type=bus_station&key=AIzaSyDYtkKiJRuJ9tjkeOtEAuEtTLp5a0XR1M0

(API key no longer valid - after having found the solution, I did change the API-key for security reasons. The Question and its Answer are still of value tough).

Here is PRINT_LOG 2 :

count = 1

Here is my Code:

func parseData(queryString: String) {

    // nested function
    func createURLWithComponents() -> URL? {
        let urlComponents = NSURLComponents()
        urlComponents.scheme = "https";
        urlComponents.host = "maps.googleapis.com";
        urlComponents.path = "/maps/api/place/textsearch/json";
        // add params
        urlComponents.queryItems = [
            URLQueryItem(name: "query", value: "Cham, Langackerstrasse"),
            URLQueryItem(name: "type", value: "bus_station"),
            URLQueryItem(name: "key", value: AppConstants.GOOGLE_MAPS_DIRECTIONS_API_KEY)
        ]

        return urlComponents.url
    }

    let myUrl = createURLWithComponents()
    var myRequest = URLRequest(url: myUrl!)
    myRequest.httpMethod = "GET"
    myRequest.setValue("application/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")

    let myConfiguration = URLSessionConfiguration.default
    let session = URLSession(configuration: myConfiguration, delegate: nil, delegateQueue: OperationQueue.main)

    // Until here everything all right !!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // i.e. the following print is correct !!!!!!!!!!!!!!!!!!!!!!!!!
    // (and when this print is copied to a Browser then the data fetched has 20 JSON-entries...)
    // PRINT_LOG 1
    print(myRequest.description)

    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!)

                // Here is the puzzling thing to happen !!!!!!!!!!!!!!!!!!!!!!
                // i.e. the following print reveals count = 1 !!!!!!!!!!!!!!!!!
                // ??? Why not 20 as with the Browser ????????????????????
                // PRINT_LOG 2
                print("count = " + "\(String(describing: stationData.results.count))")
            }
            catch let error {
                print(error)
            }
        }
    }
    myTask.resume()
}

For completeness, here is the matching Struct:

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

Thanks for any help on this.

回答1:

Found it !!!

It was the missing Info.plist localization ! (i.e. I ran the App form XCode having the Language set to German under Run-->Options-->Appliation Language). My Info.plist was only partly localized (i.e. My file InfoPlist.strings had only a few entries yet - but not a complete translation). And this missing translation lead to this problem !!!!

Running it in pure English works fine and I get 20 entries as expected.