Swift SQL POST method - Why does it always create a response with <null> as the string? - sql

I'm trying to set up POST requests to connect my Swift Project to my SQL server. I found a lot of good resources on how to create the POST requests, and my code seems to create the request fine. However, it always returns a null string instead of the desired output string. How can I get the request to submit with the correct string?
My POST function:
func addString() {
// declare the parameter as a dictionary that contains string as key and value combination. considering inputs are valid
let parameters: [String: Any] = ["textstring":"Testing..."]
// create the url with URL
let url = URL(string: "http://localhost:3000/strings")! // change server url accordingly
// create the session object
let session = URLSession.shared
// now create the URLRequest object using the url object
var request = URLRequest(url: url)
request.httpMethod = "POST" //set http method as POST
// add headers for the request
request.addValue("application/json", forHTTPHeaderField: "Content-Type") // change as per server requirements
do {
// convert parameters to Data and assign dictionary to httpBody of request
request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
} catch let error {
print(error.localizedDescription)
return
}
// create dataTask using the session object to send data to the server
let task = session.dataTask(with: request) { data, response, error in
if let error = error {
print("Post Request Error: \(error.localizedDescription)")
return
}
// ensure there is valid response code returned from this HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode)
else {
print("Invalid Response received from the server")
return
}
// ensure there is data returned
guard let responseData = data else {
print("nil Data received from the server")
return
}
do {
// create json object from data or use JSONDecoder to convert to Model struct
if let jsonResponse = try JSONSerialization.jsonObject(with: responseData, options: .mutableContainers) as? [String: Any] {
print(jsonResponse)
// handle json response
} else {
print("data maybe corrupted or in wrong format")
throw URLError(.badServerResponse)
}
} catch let error {
print(error.localizedDescription)
}
}
// perform the task
task.resume()
}
My Object TextString that emulates the SQL table (Table "strings" has two columns: "id" (SERIAL PRIMARY KEY) and "textstring" (VARCHAR(255))
struct TextString: Codable, Hashable, Identifiable {
var id: Int
var textString: String
enum CodingKeys: String, CodingKey {
case id = "string_id"
case textString = "textstring"
}}
My response output:
["string_id": 28, "textstring": <null>]
I'm thinking that the issue is with the serialization but the code is taken almost directly from a youtube video where the test cases are shown and they work. Does anyone know why the output returns "textstring" as null and not "Testing..."?
Thanks in advance!

Related

Getting error while uploading image using multipart request in Alamofire

xcode 10.3
swift 4.2
iPhone app
I'm hitting a post type API to upload image along with some string data from a form. I'm getting response result as success but there is an error parameters which indicates: CredStore - performQuery - Error copying matching creds. Error=-25300
I have tried doing some research and found that I've problem with URLCredentialStorage but I don't have any Credentials in API.
I looked at CredStore Perform Query error, I'm having very similar issue but can't find my solution there.
{
let url = route.asURL(constants: constants)
let parameters = route.asParameters(constants: constants)
let headers: HTTPHeaders = [
"authorization-name": APISession.token,
"Content-Type": "multipart/form-data"
]
Log(request: URLRequest(url: url))
Alamofire.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
if let strValue = value as? String, let valueData = strValue.data(using: .utf8) {
multipartFormData.append(valueData, withName: key)
}
if let imgValue = value as? UIImage, let imgData = imgValue.jpegData(compressionQuality: 0.5) {
multipartFormData.append(imgData, withName: key, mimeType: "image/jpg")
}
}
}, usingThreshold: UInt64.init(), to: url, method: .post, headers: headers) { (result) in
print(result)
switch result{
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
print("Preogress is: ", progress.fractionCompleted)
})
upload.responseJSON { response in
print("Succesfully uploaded")
switch response.result {
case .success(let value):
print(value)
case .failure(let error):
print(error)
}
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
}
}
I'm getting response like this,
success(request: 2019-08-12 13:07:04.224913-0400 project name[65508:460860] CredStore - performQuery - Error copying matching creds. Error=-25300, query={
class = inet;
"m_Limit" = "m_LimitAll";
ptcl = htps;
"r_Attributes" = 1;
sdmn = "url";
srvr = "url";
sync = syna;
}
so I'm confused why I'm getting success response with error parameters in that.
Not sure but I think authorization-name key present in headers is incorrect. It should be Authorization.

Alamofire.download by post with parameters not working

Hello I am doing download file by post with parameters. But server can't receive post parameters.
But if i do same thing with get with url parameters. Everything works fine.
Almofire.request also works fine by post with parameters. But only Almofire.download by post with parameter does not work.
Why Alamofire.download does not send paramters by post method ??
var sourceStringURL : String = "\(tmp_url)download"
let destination: DownloadRequest.DownloadFileDestination =
{
_, _ in
let fileURL = URL(fileURLWithPath: destPath)
return (fileURL, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(sourceStringURL, method: .post, parameters: ["id": idStr, "var": varStr], encoding: JSONEncoding.default, headers: nil, to: destination)
.downloadProgress
{
progress in
var tmpPercent : Int = Int(progress.fractionCompleted*100 / 1.0)
}
.response
{
response in
if let error = response.error
{
print(error)
}
else
{
//success
}
}
Server receives post request correctly with Retrofit library in Android.
I just found that if i change JSONEncoding.default to URLEncoding.default.
It works fine.

Formating json object into Array of dictionary in swift

let todoEndpoint: String ="http://localhost:16789/api/product"
guard let url = NSURL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = NSURLRequest(url: url as URL)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: urlRequest as URLRequest) {
(data, response, error) in
guard let responseData = data else {
print("Error: did not receive data")
return
}
guard error == nil else {
print("error calling GET")
print(error)
return
}
do{
self.users = try JSONSerialization.jsonObject(with: responseData, options: []) as? Array}
catch{
print("Error parsing")
}
}
task.resume()
this is my Output my API
[{"ProductId":2,"Category":"Laptop","Brand":"Apple","ModelName":"MacBook Pro","SerialNumber":"Test1","DatePurchased":"2016-08-12T00:00:00","EmployeeName":"Vismita Shetty","DateAssigned":"2017-01-17T00:00:00"},{"ProductId":4,"Category":"Keyboard","Brand":"Logitech","ModelName":"K200","SerialNumber":"TestKeyboard1","DatePurchased":"2017-01-23T03:02:13.247","EmployeeName":"Vismita Shetty","DateAssigned":"2017-01-17T00:00:00"},{"ProductId":5,"Category":"Keyboard","Brand":"Logitech","ModelName":"K200","SerialNumber":"testkeyboard2","DatePurchased":"2017-01-23T03:03:26.07","EmployeeName":"Suraj Pangam","DateAssigned":"2017-01-17T00:00:00"}]
What I currently trying to do convert user into different variable which will be of type Array of dictionaries in swift. So that Each object can be accessed by key. I want to make it generic so that even some properties got changed it won't matter as it won't be accessed by providing property name. Like (users[indexPath.row] as? [String: AnyObject])?["EmployeeName"] as! String!.
Instead if i make it to dictionary i will be easily do it by running for (key,value) loop for array of dictionary.

Get server response message from error

My server (CakePHP) is responding like so:
$this->response->statusCode('400');
$this->response->type('json');
$this->response->body(json_encode(array('message' => 'Bookmark already exists')));
The Postman output looks like what you would expect:
{"message":"Bookmark already exists"}
The problem is that I cannot find a way to access this message from the failure handler (Alamofire 3.1.3 + SwiftyJSON 2.3.2)
Alamofire.request(.POST...
.validate()
.responseJSON { response in
switch response.result {
case .Success(_):
// All good
case .Failure(let error):
// Status code 400
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result)
I cannot find a way to cast response.data to JSON as a I simply get nil and the result returns just FAILURE.
Is there a way to access this server message from the failure handler ?
The data is not parsed in the .Failure case per the Alamofire 3.0 migration guide. However, server data is still available in response.data and can be parsed.
Below should work to parse this manually:
Alamofire.request(.POST, "https://example.com/create", parameters: ["foo": "bar"])
.validate()
.responseJSON { response in
switch response.result {
case .Success:
print("Validation Successful")
case .Failure(_):
var errorMessage = "General error message"
if let data = response.data {
let responseJSON = JSON(data: data)
if let message: String = responseJSON["message"].stringValue {
if !message.isEmpty {
errorMessage = message
}
}
}
print(errorMessage) //Contains General error message or specific.
}
}
}
This uses SwiftyJSON which provides the JSON struct to convert NSData. Parsing NSData to JSON can done without SwiftyJSON, answered here.
Another cleaner option might be to write a Custom Response Serializer.
A method with the router and no SwiftyJSON:
Alamofire.request(APIRouter.Register(params: params)).validate().responseJSON { response in
switch response.result {
case .Success(let json):
let message = json["clientMessage"] as? String
completion(.Success(message ?? "Success"))
case .Failure(let error):
var errorString: String?
if let data = response.data {
if let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: String] {
errorString = json["error"]
}
}
completion(.Error(errorString ?? error.localizedDescription))
}
}
I have used the following lines to read the response body from a Alamofire request.
Alamofire.request(.POST, serveraddress, headers: headers, encoding: .JSON)
.response{ request, response, data, error in
let responseData = String(data: data!, encoding: NSUTF8StringEncoding)
print(responseData)
}
With this body I can get my custom server response errormessage.
best regards
For Alamofire 4.0 and above :
Try this
response.response?.statusCode
url : "YOUR-URL"
parameters: "YOUR PARAMETER DICTIONARY"
headers: "YOUR HEADER DICTIONARY"
I am using SwiftyJSON for JSON Parsing
A sample request is here :
func createPostRequestWith(path: String?,
parameters: [String : Any]? = nil,
success : #escaping (Any?) -> (),
failure : #escaping (NSError) -> ()) {
if !(Alamofire.NetworkReachabilityManager()?.isReachable)! {
let error = NSError(domain: "", code: -1003, userInfo: nil)
failure(error)
} else {
guard let url = path else { return }
Alamofire.request(url, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: createCurrentHeader()).validate(statusCode: 200..<300).responseJSON {
response in
switch response.result {
//Remove loader here either after parsing or on error
case .success(let data):
success(data)
case .failure(let error):
print(error)
if let responseData = response.data {
var parsedResponseData = JSON.init(data: responseData)
let customError = NSError(domain: parsedResponseData["message"].stringValue, code: response.response?.statusCode ?? 555, userInfo: nil)
failure(customError as NSError)
} else {
failure(error as NSError)
}
}
}
}
}

Missing argument for parameter #2 in call to Alamofire.request(URLRequestConvertible)

Trying to pass an Authentication header in, using the recommended approach of a custom URLRequestConvertible.
So here is my URLRequestConvertible object that conforms to the protocol:
class SecureJSONRouter : URLRequestConvertible {
var type: String
var token: String
var parameters: [String: AnyObject]
init(typevar: String, tokenvar: String, parametersvar: [String: AnyObject]) {
type = typevar
token = tokenvar
parameters = parametersvar
}
var URLRequest: NSURLRequest {
let URL = NSURL(string: da_url)!
let URLRequesting = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(type))
let encoding = Alamofire.ParameterEncoding.JSON
URLRequesting.setValue(token, forHTTPHeaderField: "Authorization")
return encoding.encode(URLRequesting, parameters: parameters).0
}
}
It's basically a place to store a few things, like a bit of URL, and a token, and a way to create a NSURLRequest with a full URL, the JSON parameters, and the header field for authorization.
When I try to call it like this:
let myUrlRequest: URLRequestConvertible = SecureJSONRouter(typevar: "locations", tokenvar: token!, parametersvar: parameters)
Alamofire.request(myUrlRequest).response{ (req, resp, data, error) in
if (error != nil) {
println(req)
println(resp)
} else {
println("saved \(data)")
}
}
it won't compile, gives me a "Missing argument for parameter #2 in call" error, at the request line.
Any ideas?
PS: I did have my SecureJSONRouter thingy as the recommended Enum instead of a Class, but I got the same error. I was looking at the protocol definition, and figured there's no reason it can't be a simpler (class) in my case, so I changed it. Still the same error.
I think there's a few possible culprits here. First off, are you sure you're calling Alamofire and not AlamoFire? I've seen people make that mistake before.
As for the sample code you posted, it didn't compile for a few different reasons. I couldn't reproduce exactly the same compiler errors you were seeing, but here's a slightly modified version of you original post that does compile.
class SecureJSONRouter : URLRequestConvertible {
var type: String
var token: String
var parameters: [String: AnyObject]
init(typevar: String, tokenvar: String, parametersvar: [String: AnyObject]) {
type = typevar
token = tokenvar
parameters = parametersvar
}
var URLRequest: NSURLRequest {
let URL = NSURL(string: "http://httpbin.org")!
let URLRequesting = NSMutableURLRequest(URL: URL.URLByAppendingPathComponent(type))
let encoding = Alamofire.ParameterEncoding.JSON
URLRequesting.setValue(token, forHTTPHeaderField: "Authorization")
return encoding.encode(URLRequesting, parameters: parameters).0
}
}
Then here's an example of calling your SecureJSONRouter.
let token: String? = "my_fancy_token"
let parameters: [String: AnyObject] = ["sample_parameter": "sample_parameter_value"]
let myUrlRequest: URLRequestConvertible = SecureJSONRouter(typevar: "locations", tokenvar: token!, parametersvar: parameters)
Alamofire.request(myUrlRequest).response{ (req, resp, data, error) in
if (error != nil) {
println(req)
println(resp)
} else {
println("saved \(data)")
}
}
That is compiling with Xcode 6.1.1 against the Alamofire 1.1.3 release. Hope that helps!