Image to String using Base64 in swift 4 - swift

my php code creates an empty image on server
here is my code (swift4) :
var encoded_img = imageTobase64(image: image1.image!)
func convertImageToBase64(image: UIImage) -> String {
let imageData = UIImagePNGRepresentation(image)!
return imageData.base64EncodedString(options: Data.Base64EncodingOptions.lineLength64Characters)
}
php code :
$decodimg = base64_decode(_POST["encoded_img"]);
file_put_contents("images/".$imgname,$decodimg);
And the code to prepare the request:
#IBAction func BtnSend(_ sender: UIButton) {
var url = "http://xxxxxx/msg.php"
var encoded_img = imageTobase64(image: image1.image!)
let postData = NSMutableData(data: ("message=" + message).data(using: String.Encoding.utf8)!)
postData.append(("&encoded_img=" + encoded_img).data(using: String.Encoding.utf8)!)
request = NSMutableURLRequest(url: NSURL(string: url)! as URL,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 20.0)
request.httpMethod = "POST"
request.httpBody = postData as Data
let session = URLSession.shared
let dataTask = session.dataTask(with:
request as URLRequest, completionHandler:
{ (data, response, error)-> Void in
...
})
dataTask.resume()

The fundamental issue is that your x-www-form-urlencoded request is not well-formed. You have explicitly requested it to create base64 string with newline characters in it, but those are not allowed in x-www-form-urlencoded unless you percent encode them. Plus, we don't know what sort of characters are inside message.
I would suggest:
Not request newline characters to be added to the base64 string unless you really needed them; but
Percent escape the string values, anyway, as I don't know what sort of values you have for message.
Thus:
let parameters = [
"message": message,
"encoded_img": convertToBase64(image: image1.image!)
]
let session = URLSession.shared
let url = URL(string: "http://xxxxxx/msg.php")!
var request = URLRequest(url: url, timeoutInterval: 20.0)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") // not necessary, but best practice
request.setValue("application/json", forHTTPHeaderField: "Accept") // again, not necessary, but best practice; set this to whatever format you're expecting the response to be
request.httpBody = parameters.map { key, value in
let keyString = key.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
let valueString = value.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)!
return keyString + "=" + valueString
}.joined(separator: "&").data(using: .utf8)
let dataTask = session.dataTask(with:request) { data, response, error in
guard error == nil,
let data = data,
let httpResponse = response as? HTTPURLResponse,
(200 ..< 300) ~= httpResponse.statusCode else {
print(error ?? "Unknown error", response ?? "Unknown response")
return
}
// process `data` here
}
dataTask.resume()
where
func convertToBase64(image: UIImage) -> String {
return UIImagePNGRepresentation(image)!
.base64EncodedString()
}
and
extension CharacterSet {
/// Character set containing characters allowed in query value as outlined in RFC 3986.
///
/// RFC 3986 states that the following characters are "reserved" characters.
///
/// - General Delimiters: ":", "#", "[", "]", "#", "?", "/"
/// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
///
/// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
/// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
/// should be percent-escaped in the query string.
///
/// - parameter string: The string to be percent-escaped.
///
/// - returns: The percent-escaped string.
static var urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]#" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: generalDelimitersToEncode + subDelimitersToEncode)
return allowed
}()
}
Alternatively, you could consider using Alamofire which gets you out of the weeds of creating well-formed x-www-form-urlencoded requests.

Related

Passing headers to URL with Swift URLSession

I don't feel like sharing the actual link as it may have some private information so don't be surprised if it doesn't work.
I have a link that looks like this: www.somelink.com/stuff/searchmembers?member=John
And some headers that I need to pass, like Login: Admin, Password: Admin
When I use this site everything seems to be working just fine, I put the link, make it GET and put headers in key:value format and as a result I get the list of all members, but how can I do the same with URLSession? Here's what I currently have and I don't get anything at all. What am I doing wrong there?
func getAllMembers(urlString: String) {
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
request.httpBody = "member=John".data(using: .utf8)!
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(data)
}.resume()
}
Here is What You're Doing Wrong:
Your member=John is actually a URL query parameter. In general, URL requests have query parameters as a part of the URL string itself and not the httpbody.
Quick and Dirty Solution:
You should be good to go if you remove
request.httpBody = "member=John".data(using: .utf8)!
and instead pass the whole "www.somelink.com/stuff/searchmembers?member=John" into your getAllMembers(urlString:) function.
A Better Solution:
Let's say John's username is j o h n. Your function wouldn't make it past that first guard because spaces aren't valid URL string characters.
I like to use URLComponents because it saves me the trouble of having to handle spaces and such.
Here's how I'd write your function:
func getJohnMember(urlString: String) {
//URLComponents to the rescue!
var urlBuilder = URLComponents(string: urlString)
urlBuilder?.queryItems = [
URLQueryItem(name: "member", value: "j o h n")
]
guard let url = urlBuilder?.url else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(String(data: data, encoding: .utf8)) //Try this too!
}.resume()
}
Just to be clear, I would pass just "www.somelink.com/stuff/searchmembers" into the first parameter.
Now if I were to print(url) after the guard let, I'd get
www.somelink.com/stuff/searchmembers?member=j%20o%20h%20n
Much easier this way, no?
That member=John is a URL-query parameter, not part of the request body. So you need to add it to the URL itself.
func getAllMembers(urlString: String) {
guard let url = URL(string: "\(urlString)?member=John") else { return }
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Admin", forHTTPHeaderField: "Login")
request.setValue("Admin", forHTTPHeaderField: "Password")
URLSession.shared.dataTask(with: request) { (data, response, error) in
print(response)
print(data)
}.resume()
}

Alamofire multiple parameters (Query and Form) Swift 4

I'm having a problem sending a POST request with Alamofire.
I need to send the usser and password fields as application/x-www-form-urlencode and also some query data in the url.
I am creating a URLRequest to handle the process, but I'm getting always a 400 response from the server, so I guess the problem must be in the way I create the request.
This is the example in Postman:
I need to send a param in the url and two more in as application/x-www-form-urlencode
Postman 1 - Parameters
Postman 2 - ContentType
I need to do this (that i have in Android)
#FormUrlEncoded
#POST(Constants.AUTH_LDAP)
Call<ResponseBody> authLdap(
#Query(value = Constants.PARAM_REQ, encoded = true) String req,
#Field(Constants.PARAM_LOGIN) String login,
#Field(Constants.PARAM_PASSWORD) String password
);
And this is what I have in swift
let queryParamters = [Constants.Params.PARAM_REQ:req]
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
let fieldParameters = [
Constants.Params.PARAM_LOGIN : user,
Constants.Params.PARAM_PASSWORD : pass]
let url = URL(string: Constants.EndPoints.AUTH_LDAP)
let request = URLRequest(url: url!)
let encoding = try URLEncoding.default.encode(request, with: queryParamters as Parameters)
let encodingpa = try URLEncoding.httpBody.encode(request, with: fieldParameters as Parameters)
var urlRequest = encodingpa
urlRequest.url = encoding.url
urlRequest.allHTTPHeaderFields = headers
Alamofire.request(urlRequest).responseData(completionHandler: { response in
switch response.result {
case .success:
print("sucess")
print(response.response)
case .failure(let error):
print(error)
}
})
Thanks for your help.
Try to create url from queryParameters using URLComponents like
var urlComponents = URLComponents(string: Constants.EndPoints.AUTH_LDAP)!
urlComponents.queryItems = [
URLQueryItem(name: Constants.Params.PARAM_REQ, value: req)
]
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: fieldParameters)
request.allHTTPHeaderFields = headers
Alamofire.request(request).responseJSON { response in
}

Swift unwanted URL percent encoding

I'm trying to format a URL for use in accessing an API. The documentation for the api specifies a URL that contains the "[" and "]" characters in it. When I create a URL from my string in Swift, it is adding percent encoding and changing these characters to "%5B" and "%5D".
How do I tell swift to not percent encode these characters? Thanks!
func attemptPubgIDLookup (playerName: String, completion: #escaping (String) -> Void) {
let region = "pc-na"
let urlString = "https://api.playbattlegrounds.com/shards/" + region + "/players?filter[playerNames]=" + playerName
let finalUrl = URL(string:urlString)
if let finalUrl = finalUrl {
var urlRequest = URLRequest(url: finalUrl)
urlRequest.httpMethod = HTTPMethod.get.rawValue
urlRequest.addValue(pubgApiKey, forHTTPHeaderField: "Authorization: Bearer")
urlRequest.addValue("application/vnd.api+json", forHTTPHeaderField: "Accept")
Alamofire.request(urlRequest)
.responseJSON(completionHandler: { (response) in
debugPrint(response)
})
}
}
That's what you want. When the server receives your request it will decode the URL...
%5B is '[' and %5D is ']'
This is called percent encoding and is used in encoding special characters in the url parameter values.

Swift how to send symbols like "&(*(" with HTTP post

I have got this Swift code
var request = URLRequest(url: URL(string: "http://www.centill.com/ajax/logreg.php")!)
request.httpMethod = "POST"
let postString = "app_log_pass=\(pass_text_field.text!)"
request.httpBody = postString.data(using: .utf8)
And when my text is
(1&*^&&2
It only prints (1.How can i send string that contains various symbols safely with Swift?
As Sulthan suggested, no predefined CharacterSet can be used for actual servers when you want to include some symbol characters in your POST data.
An example CharacterSet which I often use:
extension CharacterSet {
static let rfc3986Unreserved = CharacterSet(charactersIn:
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~")
}
let postValue = "(1&*^&&2"
let postString = "app_log_pass=\(postValue.addingPercentEncoding(withAllowedCharacters: .rfc3986Unreserved)!)"
print(postString) //->app_log_pass=%281%26%2A%5E%26%262
Another CharacterSet which would work for usual PHP servers:
extension CharacterSet {
static let queryValueAllowed = CharacterSet.urlQueryAllowed.subtracting(CharacterSet(charactersIn: "&+="))
}
let postValue = "(1&*^&&2"
let postString = "app_log_pass=\(postValue.addingPercentEncoding(withAllowedCharacters: .queryValueAllowed)!)"
print(postString) //->app_log_pass=(1%26*%5E%26%262
Anyway, you need to escape the key and the value separately, to prevent escaping the separator =.
ADDITION
An example of escaping multiple key-value pairs:
extension String {
var queryValueEscaped: String {
return self.addingPercentEncoding(withAllowedCharacters: .queryValueAllowed)!
}
}
let params: [String: String] = [
"app_log_usn": "OOPer",//usn_text_field.text!,
"app_log_pass": "(1&*^&&2"//pass_text‌​_field.text!
]
let postString = params.map {"\($0.key)=\($0.value.queryValueEscaped)"}.joined(separator: "&")
print(postString) //->app_log_usn=OOPer&app_log_pass=(1%26*%5E%26%262
Assuming each key is made of safe characters and no need to escape.
Use the addingPercentEncoding method with the urlHostsAllowed parameter:
string.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)

Spaces between words are getting lost after I send post method [duplicate]

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"
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.
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.
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
}
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/authenticate?email=bob#isp.eu&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)!
}
}
}