0
votes

I am fetching json from a remote server.

When I build the url, the querystring is a ?, however, when it gets sent via a datatask, the ? gets encoded to %3F, and my call fails.

How can I prevent urlComponents from encoding the query string? I have switched out the actual values.

//Note: the host and path are coming from info.plist, I just added them in here.
 string host = servicehost.com 
 string path = "/service/getData?cc=al&lastDate="

 let urlComponents = NSURLComponents.init()
 urlComponents.scheme = "https"
 urlComponents.host = host
 urlComponents.path = path

When I print out the value:, I get

print(": urlComponents.url! \(urlComponents.url!)")
https://servicehost.com/service/getData%3Fcc=al&lastDate=

inside of dataTask, I get the following error, when inspected:

Printing description of error:
▿ DecodingError
  ▿ typeMismatch : 2 elements
    - .0 : Swift.String
    ▿ .1 : Context
      ▿ codingPath : 3 elements
        - 0 : CodingKeys(stringValue: "data", intValue: nil)
        - 1 : CodingKeys(stringValue: "date-published", intValue: nil)
        - 2 : CodingKeys(stringValue: "date", intValue: nil)
      - debugDescription : "Expected to decode String but found a number instead."
      - underlyingError : nil
Printing description of data:
▿ 0 bytes
  - count : 0
  ▿ pointer : 0x00007f90d2fdd000
    - pointerValue : 140259991867392
  - bytes : 0 elements

The size of the data is: jsonDecodableTask: 68 bytes

The failure message is:

Failure: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))

If I goto a browser and enter the url with the encoded query string, I get the following returned: <html><head><title>Error</title></head><body>Not Found</body></html>

If I go to a browser and change out the %3F to a ?, the request is successful.

1
The tricky part here is that different parts of a URL need different escaping rules. The path of your URL should not contain "?" characters. The "?" indicates the beginning of the query parameters section. (The whole ?cc=al&lastDate= part of your URL should not be in the path.) See Leo's answer for how to build your URL correctly. - Duncan C

1 Answers

3
votes

The issue there is that "?" is not part of the path. It should be automatically added when you add the query items components of your URL:

let host = "www.google.com"
let path = "/service/getData"
let queryItems: [URLQueryItem] = [.init(name: "cc", value: "al"),
                                  .init(name: "lastDate", value: "")]
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems =  queryItems
urlComponents.url?.absoluteString  // "https://www.google.com/service/getData?cc=al&lastDate="