Swift GET request with parameters

get, ios, nsurlrequest, swift

Solution

When building a `GET` request, there is no body to the request, but rather everything goes on the URL. To build a URL (and properly percent escaping it), you can also use `URLComponents`.

var components = URLComponents(string: "https://www.google.com/search/")!

components.queryItems = [
    URLQueryItem(name: "q", value: "War & Peace")
]

guard let url = components.url else {
    throw URLError(.badURL)
}

The only trick is that most web services need `+` character percent escaped (because they'll interpret that as a space character as dictated by the `application/x-www-form-urlencoded` specification). But `URLComponents` will not percent escape it. Apple contends that `+` is a valid character in a query and therefore shouldn't be escaped. Technically, they are correct, that it is allowed in a query of a URI, but it has a special meaning in `application/x-www-form-urlencoded` requests and really should not be passed unescaped.

When I presented this issue to Apple support, they advised to manually percent escaping the `+` characters:

components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")

This is an inelegant work-around, but it works, and is what Apple advises if your queries may include a `+` character and you have a server that interprets them as spaces.

Anyway, combining that with your `sendRequest` routine, you might end up with something like:

enum WebServiceError: Error {
    case invalidResponse(Data, URLResponse)
    case statusCode(Int, Data)
}

func object<T: Decodable>(from baseUrl: URL, parameters: [String: String]? = nil) async throws -> T {
    guard var components = URLComponents(url: baseUrl, resolvingAgainstBaseURL: false) else {
        throw URLError(.badURL)
    }

    components.queryItems = parameters?.map { (key, value) in
        URLQueryItem(name: key, value: value)
    }
    components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")

    guard let url = components.url else {
        throw URLError(.badURL)
    }

    let (data, response) = try await URLSession.shared.data(from: url)

    guard let statusCode = (response as? HTTPURLResponse)?.statusCode else {  // is there HTTP response
        throw WebServiceError.invalidResponse(data, response)
    }

    guard 200 ..< 300 ~= statusCode else {                                    // is statusCode 2XX
        throw WebServiceError.statusCode(statusCode, data)
    }

    return try JSONDecoder().decode(T.self, from: data)
}

And you'd call it like:

do {
    let foo: Foo = try await object(from: baseUrl)
    // do something with `foo` here
} catch WebServiceError.statusCode(404, _) {        // if you want, you can catch individual status codes here
    // handle not found error here
} catch {
    // handle other errors here
}

Clearly, there are lots of permutations on the idea, but hopefully this illustrates the basic idea of how to percent encode the parameters into the URL of a GET request.

See previous revisions of this answer for Swift 2, manual percent escaping renditions, and non-Swift concurrency renditions of the above.

Problem

I'm very new to swift, so I will probably have a lot of faults in my code but what I'm trying to achieve is send a `GET` request to a localhost server with paramters. More so I'm trying to achieve it given my function take two parameters `baseURL:string,params:NSDictionary`. I am not sure how to combine those two into the actual URLRequest ? Here is what I have tried so far ``` func sendRequest(url:String,params:NSDictionary){ let urls: NSURL! = NSURL(string:url) var request = NSMutableURLRequest(URL:urls) request.HTTPMethod = "GET" var data:NSData! = NSKeyedArchiver.archivedDataWithRootObject(params) request.HTTPBody = data println(request) var session = NSURLSession.sharedSession() var task = session.dataTaskWithRequest(request, completionHandler:loadedData) task.resume() } } func loadedData(data:NSData!,response:NSURLResponse!,err:NSError!){ if(err != nil){ println(err?.description) }else{ var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: nil) as NSDictionary println(jsonResult) } } ```

Original source

Related problems