Swift GET request with parameters
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 url = URLComponents(string: "https://www.google.com/search/")!
url.queryItems = [
URLQueryItem(name: "q", value: "War & Peace")
]
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.
Apple acknowledges that we have to percent escaping the +
characters, but advises that we do it manually:
var url = URLComponents(string: "https://www.wolframalpha.com/input/")!
url.queryItems = [
URLQueryItem(name: "i", value: "1+2")
]
url.percentEncodedQuery = url.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.
So, combining that with your sendRequest
routine, you end up with something like:
func sendRequest(_ url: String, parameters: [String: String], completion: @escaping ([String: Any]?, Error?) -> Void) {
var components = URLComponents(string: url)!
components.queryItems = parameters.map { (key, value) in
URLQueryItem(name: key, value: value)
}
components.percentEncodedQuery = components.percentEncodedQuery?.replacingOccurrences(of: "+", with: "%2B")
let request = URLRequest(url: components.url!)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard
let data = data, // is there data
let response = response as? HTTPURLResponse, // is there HTTP response
200 ..< 300 ~= response.statusCode, // is statusCode 2XX
error == nil // was there no error
else {
completion(nil, error)
return
}
let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
completion(responseObject, nil)
}
task.resume()
}
And you'd call it like:
sendRequest("someurl", parameters: ["foo": "bar"]) { responseObject, error in
guard let responseObject = responseObject, error == nil else {
print(error ?? "Unknown error")
return
}
// use `responseObject` here
}
Personally, I'd use JSONDecoder
nowadays and return a custom struct
rather than a dictionary, but that's not really relevant here. Hopefully this illustrates the basic idea of how to percent encode the parameters into the URL of a GET request.
See previous revision of this answer for Swift 2 and manual percent escaping renditions.
Swift GET request with url parameters
First off, you're doing a lot more work than is probably necessary. You're encoding your query string parameters into URLComponents
which is correct. Then, in your send you are decomposing your URL and parsing out the components then re-encoding them. You're also doing a lot of force-unwrapping, which is fragile and hides problems.
Here's your code simplified in a playground that works for me:
import UIKit
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
let sampleURL = "https://someserver.com/somepath"
func sendRequest(_ url: URL, completion: @escaping ([String: Any]?, Error?) -> Void) {
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, // is there data
let response = response as? HTTPURLResponse, // is there HTTP response
(200 ..< 300) ~= response.statusCode, // is statusCode 2XX
error == nil else { // was there no error, otherwise ...
completion(nil, error)
return
}
let responseObject = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any]
completion(responseObject, nil)
}
task.resume()
}
var urlComponents: URLComponents? {
let resultID = "resultID123"
let resultResponseID = "responseID456"
let questionIndex = "questionNumbers1"
var urlComponents = URLComponents(string: sampleURL)
urlComponents?.queryItems = [
URLQueryItem(name: "surveyResultsId", value: "\(String(describing: resultID))"),
URLQueryItem(name: "surveyResultsResponseId", value: "\(String(describing: resultResponseID))"),
URLQueryItem(name: "questions", value: "\(questionIndex)"),
URLQueryItem(name: "selectedAnswer", value: "\("storedAnswer1")")
]
return urlComponents
}
if let urlComponents = urlComponents, let url = urlComponents.url?.absoluteURL {
sendRequest(url) { (result, error) in
print("Got an answer: \(String(describing: result))")
}
}
When I run this against a server URL that returns valid JSON, I get:
Got an answer: Optional(["image": {
href = "https://example.com";
}, "object_types": {
card = {
fields = {
};
pollable = 1;
};
}])
GET request with parameters
Example how to use URLQueryItem
for the requests.
func getRequest(params: [String:String]) {
let urlComp = NSURLComponents(string: "https://my-side.com/data")!
var items = [URLQueryItem]()
for (key,value) in params {
items.append(URLQueryItem(name: key, value: value))
}
items = items.filter{!$0.name.isEmpty}
if !items.isEmpty {
urlComp.queryItems = items
}
var urlRequest = URLRequest(url: urlComp.url!)
urlRequest.httpMethod = "GET"
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in
})
task.resume()
}
getRequest(params: ["token": "AS7F87SAD84889AD"])
HttpRequest with multiple parameters swift
If you have the option use Alamofire. It is very good :)
But if you want to use the dictionary. It seems you have to convert it to a string. Did you try something like
let parameters = ["auth":"asdf", "width":"123"]
let parametersString = (parameters.compactMap({ (key, value) -> String in
return "\(key)=\(value)"
}) as Array).joined(separator: "&")
And use the parametersString as the parameter
How can I make a post request in Swift that reads more than 1 parameter?
I don't think the JSON data you were providing in parameters was valid (I check using jsonlint.com) Try this:
func postRequest(classroomID: String, email: String, vote: String){
//declare parameter as a dictionary which contains string as key and value combination.
let parameters: [String:Any] = [
"classroomID": classroomID,
"LastUpdated": "2020-01-01",
"TheVoteData":[
"Email": email,
"TheVote": vote
]
]
//create the url with NSURL
let url = URL(string: "https://www.api-gateway/dynamoDB/resource")!
//now create the Request object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
do {
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted) // pass dictionary to data object and set it as request body
} catch let error {
print(error.localizedDescription)
}
//HTTP Headers
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let jsonData = try! JSONSerialization.data(withJSONObject: parameters, options: [])
//create dataTask using the session object to send data to the server
let task = URLSession.shared.uploadTask(with: request, from: jsonData) { data, response, error in
if let data = data, let dataString = String(data: data, encoding: .utf8) {
print(dataString)
}
//Returns HHTP response if
if let httpResponse = response as? HTTPURLResponse {
print(httpResponse.statusCode)
}
}
task.resume()
}
Swift: HTTP request with parameters in header
Swift 5+, 4.1+ and Swift 3
var request = URLRequest(url: url)
request.setValue("secret-keyValue", forHTTPHeaderField: "secret-key")
URLSession.shared.dataTask(with: request) { data, response, error in }
Swift 2.2
Wrap your NSURL
into NSMutableRequest
like this:
let request = NSMutableURLRequest(URL: url)
And then use this method to add header to your request
object:
request.setValue("secret-keyValue", forHTTPHeaderField: "secret-key")
And instead of using dataTaskWithURL:
use dataTaskWithRequest:
method.
session.dataTaskWithRequest(request){
(data: NSData?,response: NSURLResponse?, error: NSError?) -> Void in }
Related Topics
Take Screenshot of Host App Using iOS Share/Action Extensions
What's the Rationale of Swift's Size Methods Taking 'Int'S
How to Add Openssl to a Swift Project
Rotate a Sprite to Sprite Position Not Exact in Spritekit with Swift
How to Combine Two Nsdictionary in Swift
Hittest Prints Ar Entity Name Even When I am Not Tapping on It
How to Handle Parameter Validation Swift
Fetch Coredata with One to Many Relationship in Swift
Response Struct Does Not Like Codingkeys
Create Endless Cgpath Without Framedrops
Convert String to Nsdate in Swift
Ios-Charts Set Maximum Visible X Axis Values
Swift. Declaring Private Functions in Internal Protocol
Operation Not Permitted When Executing 'Killall' with Swift
Count the Number of Lines in a Swift String
How to Compare Range<String.Index> and Defaultbidirectionalindices<String.Characterview>