Wednesday, October 17, 2018

ios - Swift 4 send POST request as x-www-form-urlencoded



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 {
case success(T)
case failure(Error)
}

func urlSession(method: String? = nil, file: String, data: Data? = nil, completionHandler: @escaping (Result) -> 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.stride)

}

static func unarchive(d:Data) -> User {
guard d.count == MemoryLayout.stride else {
fatalError("BOOM!")
}

var w:User?
d.withUnsafeBytes({(bytes: UnsafePointer)->Void in
w = UnsafePointer(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) 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 get Undefined index:




Full code of the app can be seen here https://gitlab.com/rexhin/ios-kida.git


Answer



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) 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.


No comments:

Post a Comment

plot explanation - Why did Peaches' mom hang on the tree? - Movies & TV

In the middle of the movie Ice Age: Continental Drift Peaches' mom asked Peaches to go to sleep. Then, she hung on the tree. This parti...