user2145124 user2145124 - 1 month ago 10
Swift Question

Data in HTTPBody with a PUT method fails, while it works with a POST?

first of all i would like to say i got the exact same problem as the following question: How to add data to HTTPBody with PUT method in NSURLSession?. But it wasn't answered so i made my own question.

We have written a node API for a school assignment. We've tested the whole API. (The chances of being something wrong there are slim.)

After that i went working on a iOS client to CRUD users.
Making a user is going perfectly, but whenever i try to edit a user something strange happens. The data on the server arrives as undefined.

I use the following code to save a user:

func saveUser(user: User, completionHandler: (String?, User?) -> Void) {
let url = NSURL(string: "https://pokeapi9001.herokuapp.com/api/users/")
let request = NSMutableURLRequest(URL:url!)
request.HTTPMethod = "POST"

let postString = "email=\(user.email)&password=\(user.password!)&role=\(user.role)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)

request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData


let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error: \(error)")
}

do {
guard let data = data else {
throw JSONError.NoData
}
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
throw JSONError.ConversionFailed
}

//do specific things

} catch let error as JSONError {
completionHandler(error.rawValue, nil)
} catch let error as NSError {
completionHandler(error.debugDescription, nil)
}

}
task.resume()
}


keep in mind, this is working perfectly (don't know if it is intended to be used like this)

To edit a user i use the following code:

func editUser(user: User, completionHandler: (String?, User?) -> Void) {
let url = NSURL(string: "https://pokeapi9001.herokuapp.com/api/users/\(user.id!)")
let request = NSMutableURLRequest(URL:url!)
request.HTTPMethod = "PUT"

let postString = "email=\(user.email)&password=\(user.password!)&role=\(user.role)"
request.HTTPBody = postString.dataUsingEncoding(NSUTF8StringEncoding)

request.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringCacheData

let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
data, response, error in
if error != nil {
print("error: \(error)")
}

do {
guard let data = data else {
throw JSONError.NoData
}
guard let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary else {
throw JSONError.ConversionFailed
}

//do specific things

} catch let error as JSONError {
completionHandler(error.rawValue, nil)
} catch let error as NSError {
completionHandler(error.debugDescription, nil)
}
}
task.resume()
}


(The original code is a bit longer but i removed parts that had nothing to do with the actual posting of the data)

I have really no idea what i'm doing wrong, could be something small and stupid. Please help.

edit after input from @fiks

To be clear, the problem i am having is that I fill the "postString" the same way in the editUser method as I do in the saveUser method.(At least I think I do)
However in the saveUser method the postString seems to be correctly passed through to the API (it creates a new user with the given values).
The editUser method does not pass the values through.

If I put a console log on the server it shows all values are "undefined".
To test if the postString was correct on the iOS part I printed both strings out. Both of them outputted email=user@test.com&password=test&role=admin

Answer

From what I see in the postman request, you are sending a x-www-form-urlencoded request.

You have to specify it in the code. See example: POST request using application/x-www-form-urlencoded

Regarding Charles: since you are using https, you have to enable proxy for the host. More info here: https://www.charlesproxy.com/documentation/proxying/ssl-proxying/