Swift 4 send POST request as x-www-form-urlencoded
Solution 1
You are passing the result of User.archive(w: thing)
as the data
embedded in the request body, which may never work. Generally, your archive(w:)
and unarchive(d:)
would never generate any useful results and you should better remove them immediately.
If you want to pass parameters where x-www-form-urlencoded
is needed, you need to create a URL-query-like string.
Try something like this:
func login(username: String, password: String, completionHandler: @escaping (Login) -> Void) {
let dataThing = "username=\(username)&password=\(password)".data(using: .utf8)
xhr.urlSession(method: "POST", file: "https://kida.al/login_register/", data: dataThing) { (result: XHR.Result<User>) in
//...
}
}
The example above is a little bit too simplified, that you may need to escape username
and/or password
before embedding it in a string, when they can contain some special characters. You can find many articles on the web about it.
Solution 2
I used below code in swift 4
guard let url = URL(string: "http://192.168.88.129:81/authenticate") else {
return
}
let user1 = username.text!
let pass = passwordfield.text!
print(user1)
print(pass)
let data : Data = "username=\(user1)&password=\(pass)&grant_type=password".data(using: .utf8)!
var request : URLRequest = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField:"Content-Type");
request.setValue(NSLocalizedString("lang", comment: ""), forHTTPHeaderField:"Accept-Language");
request.httpBody = data
print("one called")
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
// vs let session = URLSession.shared
// make the request
let task = session.dataTask(with: request, completionHandler: {
(data, response, error) in
if let error = error
{
print(error)
}
else if let response = response {
print("her in resposne")
}else if let data = data
{
print("here in data")
print(data)
}
DispatchQueue.main.async { // Correct
guard let responseData = data else {
print("Error: did not receive data")
return
}
let decoder = JSONDecoder()
print(String(data: responseData, encoding: .utf8))
do {
// let todo = try decoder.decode(T.self, from: responseData)
// NSAssertionHandler(.success(todo))
} catch {
print("error trying to convert data to JSON")
//print(error)
// NSAssertionHandler(.failure(error))
}
}
})
task.resume()
}
Solution 3
Quoting from this post
In PHP, a variable or array element which has never been set is different from one whose value is null; attempting to access such an unset value is a runtime error.
The Undefined index error occurs when you try to access an unset variable or an array element. You should use function isset inorder to safely access the username param from the POST body. Try the below code in your PHP file.
if (isset($_POST["username"]))
{
$user= $_POST["username"];
echo 'Your Username is ' . $user;
}
else
{
$user = null;
echo "No user name found";
}
Solution 4
Another way of doing this is as follows:
-
Add the URLEncodedFormEncoder.swift into your project. This is a custom URLEncodedFormEncoder from Alamofire / Vapor.
-
Conform your model to native Swift Encodable protocol, just as you do with JSON coding.
-
Encode the model just as you do during json encoding
// example
let requstModel = OpenIDCTokenRequest(
clientId: clientId,
clientSecret: clientSecret,
username: username,
password: password
)
guard let requestData: Data = try? URLEncodedFormEncoder().encode(requstModel) else {
return // handle encoding error
}
M1X
Experienced Software Engineer with a demonstrated history of working in the web development industry. Strong engineering professional skilles in Angular, TypeScript, ECMAScript 6 and Node.Js.
Updated on June 15, 2022Comments
-
M1X almost 2 years
I want to send a POST request to my php 7 server which accepts data as
application/x-www-form-urlencoded
. The data I have is inside a Struct and I want to get every property of this struct as a parameter when I submit it.This is the struct which handles my urlSession requests both GET and POST
XHR.swift
struct XHR { enum Result<T> { case success(T) case failure(Error) } func urlSession<T>(method: String? = nil, file: String, data: Data? = nil, completionHandler: @escaping (Result<T>) -> Void) where T: Codable { let file = file.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)! // Set up the URL request guard let url = URL.init(string: file) else { print("Error: cannot create URL") return } var urlRequest = URLRequest(url: url) if method == "POST" { urlRequest.httpMethod = "POST"; urlRequest.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") urlRequest.httpBody = data print(urlRequest.httpBody) } // set up the session let config = URLSessionConfiguration.default let session = URLSession(configuration: config) // vs let session = URLSession.shared // make the request let task = session.dataTask(with: urlRequest, completionHandler: { (data, response, error) in DispatchQueue.main.async { // Correct guard let responseData = data else { print("Error: did not receive data") return } let decoder = JSONDecoder() print(String(data: responseData, encoding: .utf8)) do { let todo = try decoder.decode(T.self, from: responseData) completionHandler(.success(todo)) } catch { print("error trying to convert data to JSON") //print(error) completionHandler(.failure(error)) } } }) task.resume() } }
This is the functions which sends a POST request to the server:
VideoViewModel.swift
struct User: Codable { let username: String let password: String static func archive(w:User) -> Data { var fw = w return Data(bytes: &fw, count: MemoryLayout<User>.stride) } static func unarchive(d:Data) -> User { guard d.count == MemoryLayout<User>.stride else { fatalError("BOOM!") } var w:User? d.withUnsafeBytes({(bytes: UnsafePointer<User>)->Void in w = UnsafePointer<User>(bytes).pointee }) return w! } } enum Login { case success(User) case failure(Error) } func login(username: String, password: String, completionHandler: @escaping (Login) -> Void) { let thing = User(username: username, password: password) let dataThing = User.archive(w: thing) xhr.urlSession(method: "POST", file: "https://kida.al/login_register/", data: dataThing) { (result: XHR.Result<User>) in switch result { case .failure(let error): completionHandler(.failure(error)) case .success(let user): //let convertedThing = User.unarchive(d: user) completionHandler(.success(user)) } } }
And I call it like this:
videoViewModel.login(username: "rexhin", password: "bonbon") { (result: VideoViewModel.Login) in switch result { case .failure(let error): print("error") case .success(let user): print(user) } }
From PHP I can see that a POST request is submitted successfully but when I try to get the
username
field by doing$_POST["username"]
I getUndefined index:
Full code of the app can be seen here https://gitlab.com/rexhin/ios-kida.git
-
M1X over 6 yearsI know that but the variable is not SET
-
Rizwan Ahmed over 6 yearsAre you passing your data properly from your app?
-
M1X over 6 yearsThat's the problem, I think no
-
Rizwan Ahmed over 6 yearsAre you encoding your string data before sending it to the server? Example: [yourString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
-
M1X over 6 yearsYes but I need to do this manually every time I send a POST request. Is it a way to convert a struct to
"username=\(username)&password=\(password)"
? -
OOPer over 6 years@RexhinHoxha, yes, do it manually. If you want to ask something about any structs, that's another question. In this question, you have written this struct.
-
Krika over 5 yearsThis might not work if the values of username or password need to be URL-encoded.
-
OOPer over 5 years@JeremyWhite, thanks for clarifying. The same thing is noted in the part The example above is ..., but your comment would be better.
-
Dilip Saket almost 4 yearsI was having issue with form data since last 1 year and I was using alamofire temporary . But, finally found this code useful which pass parameters using pure coding. Many Thanks!