Reputation: 4411
I am learning Swift and I don't know how to send parameters to server using Swift.
In Objective-C we can do this by using "%@"
as the placeholder.
But what should be done in case of Swift, suppose I have a login webservice which requires email and password.
Now I want to know is that how will i send the logintextfield
and passwordtextfield
text to the server, such as,
var bodyData = "email=logintextfield.text&password=passwordtextfield.text"
Upvotes: 2
Views: 11295
Reputation: 650
Based on the above I ended up with this, to get a token within a Set-Cookie element.
Where the URLResponse was
<NSHTTPURLResponse: 0x17403ef20> { URL: http://bla.co.uk//auth/[email protected]&password=xcode } { status code: 200, headers {
"Cache-Control" = "private, must-revalidate";
Connection = "keep-alive";
"Content-Type" = "application/json";
Date = "Fri, 17 Feb 2017 10:51:41 GMT";
Expires = "-1";
Pragma = "no-cache";
Server = nginx;
"Set-Cookie" = "token=Cu4CmOaverylongstring0mCu4CmOpBGg; expires=Fri, 17-Feb-2017 20:51:41 GMT; Max-Age=36000; path=auth; httponly";
"Transfer-Encoding" = Identity;
"X-Powered-By" = "PHP/5.5.9-1ubuntu4.19, PleskLin";
} }
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
let httpResponse = response as! HTTPURLResponse
let statusCode = httpResponse.statusCode
if statusCode == 200 {
let keyValues = httpResponse.allHeaderFields.map { (String(describing: $0.key).lowercased(), String(describing: $0.value)) }
// Now filter the array, searching for your header-key, also lowercased
if let myHeaderValue = keyValues.filter({ $0.0 == "Set-Cookie".lowercased() }).first {
print(myHeaderValue.1)
let cookies = myHeaderValue.1
let cookieDict = cookies.components(separatedBy: ";")
print("\(cookieDict)")
let tokenEntryParameter = cookieDict.filter({$0 .contains("token")})
let tokenEntry = tokenEntryParameter.first
token = (tokenEntry?.components(separatedBy: "=").last)!
}
}
}
Upvotes: 0
Reputation: 437552
When creating a HTTP request that includes user input, one should generally percent escape it in case there are any reserved characters in the user's input, thus:
let login = logintextfield.text?.addingPercentEncodingForURLQueryValue() ?? ""
let password = passwordtextfield.text?.addingPercentEncodingForURLQueryValue() ?? ""
let bodyData = "email=\(login)&password=\(password)"
Note, you'd really want to check to see if login
and password
were nil
or not. Anyway, the percent-escaping is done as follows:
extension String {
/// Percent escapes values to be added to a URL query as specified in RFC 3986
///
/// This percent-escapes all characters besides the alphanumeric character set and "-", ".", "_", and "~".
///
/// http://www.ietf.org/rfc/rfc3986.txt
///
/// :returns: Returns percent-escaped string.
func addingPercentEncodingForURLQueryValue() -> String? {
let allowedCharacters = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~")
return self.addingPercentEncoding(withAllowedCharacters: allowedCharacters)
}
}
See this answer for another rendition of this extension.
If you wanted to see a demonstration of the use of the above, imagine the following request:
let keyData = "AIzaSyCRLa4LQZWNQBcjCYcIVYA45i9i8zfClqc"
let sensorInformation = false
let types = "building"
let radius = 1000000
let locationCoordinate = CLLocationCoordinate2D(latitude:40.748716, longitude: -73.985643)
let name = "Empire State Building, New York, NY"
let floors = 102
let now = Date()
let params:[String: Any] = [
"key" : keyData,
"sensor" : sensorInformation,
"typesData" : types,
"radius" : radius,
"location" : locationCoordinate,
"name" : name,
"floors" : floors,
"when" : now,
"pi" : M_PI]
let url = URL(string: "http://some.web.site.com/inquiry")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
request.httpBody = params.dataFromHttpParameters()
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard data != nil && error == nil else {
print("error submitting request: \(error)")
return
}
if let httpResponse = response as? HTTPURLResponse where httpResponse.statusCode != 200 {
print("response was not 200: \(response)")
return
}
// handle the data of the successful response here
}
task.resume()
I'm including lots of parameters that were not included in your example, but simply as a way of illustrating the routine's handling of a diverse array of parameter types.
Incidentally, the above uses my datafromHttpParameters
function:
extension Dictionary {
/// This creates a String representation of the supplied value.
///
/// This converts NSDate objects to a RFC3339 formatted string, booleans to "true" or "false",
/// and otherwise returns the default string representation.
///
/// - parameter value: The value to be converted to a string
///
/// - returns: String representation
private func httpStringRepresentation(_ value: Any) -> String {
switch value {
case let date as Date:
return date.rfc3339String()
case let coordinate as CLLocationCoordinate2D:
return "\(coordinate.latitude),\(coordinate.longitude)"
case let boolean as Bool:
return boolean ? "true" : "false"
default:
return "\(value)"
}
}
/// Build `Data` representation of HTTP parameter dictionary of keys and objects
///
/// This percent escapes in compliance with RFC 3986
///
/// http://www.ietf.org/rfc/rfc3986.txt
///
/// :returns: String representation in the form of key1=value1&key2=value2 where the keys and values are percent escaped
func dataFromHttpParameters() -> Data {
let parameterArray = self.map { (key, value) -> String in
let percentEscapedKey = (key as! String).addingPercentEncodingForURLQueryValue()!
let percentEscapedValue = httpStringRepresentation(value).addingPercentEncodingForURLQueryValue()!
return "\(percentEscapedKey)=\(percentEscapedValue)"
}
return parameterArray.joined(separator: "&").data(using: .utf8)!
}
}
Here, because I'm dealing with an array of parameter strings, I use the join
function to concatenate them separated by &
, but the idea the same.
Feel free to customize that function to handle whatever data types you may be passing into it (e.g. I don't generally have CLLocationCoordinate2D
in there, but your example included one, so I wanted to show what it might look like). But the key is that if you're supplying any fields that include user input, make sure to percent-escape it.
FYI, this is my rfc3339String
function which is used above. (Clearly, if you don't need to transmit dates, you don't need this, but I'm including it for the sake of completeness for a more generalized solution.)
extension Date {
/// Get RFC 3339/ISO 8601 string representation of the date.
///
/// For more information, see:
///
/// https://developer.apple.com/library/ios/qa/qa1480/_index.html
///
/// - returns: Return RFC 3339 representation of date string
func rfc3339String() -> String {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSX"
formatter.timeZone = TimeZone(secondsFromGMT: 0)
formatter.locale = Locale(identifier: "en_US_POSIX")
return formatter.string(from: self)
}
}
To see Swift 2 rendition, see previous rendition of this answer.
Upvotes: 10
Reputation: 4411
I am calling the json on login button click
@IBAction func loginClicked(sender : AnyObject){
var request = NSMutableURLRequest(URL: NSURL(string: kLoginURL)) // Here, kLogin contains the Login API.
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(self.criteriaDic(), options: nil, error: &err) // This Line fills the web service with required parameters.
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
// println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err1: NSError?
var json2 = NSJSONSerialization.JSONObjectWithData(strData.dataUsingEncoding(NSUTF8StringEncoding), options: .MutableLeaves, error:&err1 ) as NSDictionary
println("json2 :\(json2)")
if(err) {
println(err!.localizedDescription)
}
else {
var success = json2["success"] as? Int
println("Succes: \(success)")
}
})
task.resume()
}
Here, I have made a seperate dictionary for the parameters.
var params = ["format":"json", "MobileType":"IOS","MIN":"f8d16d98ad12acdbbe1de647414495ec","UserName":emailTxtField.text,"PWD":passwordTxtField.text,"SigninVia":"SH"]as NSDictionary
return params
}
Upvotes: 0
Reputation: 4411
It can be done by passing the required parameter in the service like this,
var urlPath = NSString(format: "https://maps.googleapis.com/maps/api/place/search/json?key=AIzaSyCRLa4LQZWNQBcjCYcIVYA45i9i8zfClqc&sensor=false&types=restaurant&radius=100000&location=\(locationCoord)")
Here urlPath
is the url containing web service and locationCoord
(as last parameter) is the run time value for the location parameter for the web service. The parameter key, sensor, radius and types are fixed.
Upvotes: 0