Alamofire sending a request to an API 'NSInvalidArgumentException' - swift

i am using this code to send an image and text to an api:
func uploadImageAndData(_ url:String,parameters1:Parameters,headers1:HTTPHeaders,images:[UIImage]){
Alamofire.upload(multipartFormData: { multipartFormData in
// import image to request
var i=0
for imageData in images {
// multipartFormData.append(self.resizeImage(image: uploadedProfileImage, targetSize: CGSize(width: 200, height: 200)
multipartFormData.append(imageData.pngData()!, withName: "profilePic", fileName: "profileimage"+".jpeg", mimeType: "image/jpeg")
i += 1
}
for (key, value) in parameters1 {
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
} ,to: url,method:.post,
headers:[:],
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (Progress) in
print("showuing upload progress")
print("Upload Progress: \(Progress.fractionCompleted)")
//SwiftSpinner.show(progress: Progress.fractionCompleted*100, title: "تحميل")
})
upload.responseJSON { response in
print(response.response?.statusCode as Any)
if let data = response.result.value, let utf8Text = String(data: data as! Data, encoding: .utf8) {
print("New Func Data: \(utf8Text)")
}
switch response.response?.statusCode {
case 201 :
print("Success")
break
case 413 :
print("Post too large")
break
case 200 :
print("200")
break
case 400 :
print("400")
break
case 401 :
print("401")
break
case 302 :
print("302")
break
case 500 :
print("500")
break
default: break
}
}
return
case .failure(let encodingError): break
}
})
}
and i got these datas :
let address: Parameters = [
"location" : addressField.text!,
"locationLat" :pickedLocationLat,
"locationLong" :pickedLocationLong
]
let body: Parameters = [
"name": "Sample Name",
"email": "test#test.com",
"password": "test123",
"address": address
]
and i am accessing the function using this code
var head = [String:Any]()
head["Accept"]="application/json"
uploadImageAndData(BASE_URL", parameters1: body, headers1: head as! HTTPHeaders, images: [uploadedProfileImage])
this code is throwing an error like
Terminating app due to uncaught exception 'NSInvalidArgumentException'
reason: '-[_TtGCs26_SwiftDeferredNSDictionarySSP__$ dataUsingEncoding:]: unrecognized selector sent to instance 0x6000015bed00'
terminating with uncaught exception of type NSException
i dont know what is causing this problem the api requires a json body with a content-type of application/json
i tired removing all the content of the body parameter except for the name to try and send only the name and when i do that the image uploads successfully but it still gives me this error,so please help me figure out this problem.

You need to make values a string not double
let address: Parameters = [
"location" : addressField.text!,
"locationLat" :"\(pickedLocationLat)",
"locationLong" :"\(pickedLocationLong)"
]

Related

how to use form data in Alamofire

In the postman in the body section in the form-data part when I pass a mobile number as a key and mobile number as Int value I get a response. But when I did it in the code I will not get the response as expected.
my code is
func fetchRegisterData(){
let url = registerApi
var mobileNumber = mobilenumberTextfield.text
let parameters = ["mobile" : mobileNumber] as [String : Any]
AF.request(url,method: .post, parameters: parameters, encoding:JSONEncoding.default).responseJSON
{ response in switch response.result {
case .success(let JSON):
print("response is :\(response)")
case .failure(_):
print("fail")
}
}
}
If you're trying to pass parameters as multipart/form-data you have to use the upload method in Alamofire:
func fetchRegisterData() {
let parameters = ["mobile": mobilenumberTextfield.text!]
AF.upload(multipartFormData: { (multiFormData) in
for (key, value) in parameters {
multiFormData.append(Data(value.utf8), withName: key)
}
}, to: registerApi).responseJSON { response in
switch response.result {
case .success(let JSON):
print("response is :\(response)")
case .failure(_):
print("fail")
}
}
}
For normal Alamofire request -
While passing values as a parameter in Alamofire Request. You need to know about the data type of the request type values which is valid for API.
At API end, these values are getting parsed based on some data types. There must be some validations on the data type at the API side.
for mobileNumber, it can be either Int or String
1 - let parameters = ["mobile" : mobileNumber] as [String : Int]
2 - let parameters = ["mobile" : mobileNumber] as [String : String]
For multipart form data request use have to use something like below. However, if you are not uploading anything you should not use it. Ask API team to make changes in the API and normal parameters request
Alamofire.upload(multipartFormData: { (multipartFormData) in
//Try this
multipartFormData.append(mobileNumber, withName: "mobile")
//or this
multipartFormData.append("\(String(describing: mobileNumber))".data(using: .utf8)!, withName: "mobile")
}, usingThreshold: 10 * 1024 * 1024, to: apiUrl, method: .post, headers: [:], encodingCompletion: { (encodingResult) in
switch encodingResult {
case .success(let upload, _, _):
case .failure( _):
}
})

Issue while hitting Post Method api in swift

I'm trying to call an API in which I'm trying to send some parameters and array of images, but when I hit the API I get an error, Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSCFNumber dataUsingEncoding:]: unrecognized selector sent to instance 0xfb47bab3e1e91166'. I have checked through breakpoint it crashes on parameters, I'm confused why it is giving this error, my code for the API is this,
func addAPI()
{
let headers: HTTPHeaders = [
/* "Authorization": "your_access_token", in case you need authorization header */
"Content-type": "multipart/form-data"
]
let parameters :[String: Any] = ["name":productNameTxt.text!,
"price":priceTxt.text!,
"size": sizeTxt.text!,
"weight":weightTxt.text!,
"quality":qualityTxt.text!,
"brand":brandTxt.text!,
"shippingCost":shippingCostTxt.text!,
"details":detailTxt.text!,
"material_id":materialId,
"material_type_id": subMaterialId,
"maxQuantity":4]
Alamofire.upload(multipartFormData: { multipartFormData in
for (key, value) in parameters {
if let data = ((value) as AnyObject).data(using: String.Encoding.utf8.rawValue) {
multipartFormData.append(data, withName: key)
}
}
for (index, image) in self.imageArray.enumerated() {
multipartFormData.append(image.pngData()!, withName: "file", fileName: "image\(index).png", mimeType: "image/png")
}
// for i in 0..
},
to: addProductUrl,method:HTTPMethod.post,
headers:headers, encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload
.validate()
.responseJSON { response in
switch response.result {
case .success(let value):
// compBlock(value as AnyObject,true)
print("responseObject: \(value)")
case .failure(let responseError):
print("responseError: \(responseError)")
}
}
case .failure(let encodingError):
print("encodingError: \(encodingError)")
let errorDesc = (encodingError as NSError).localizedDescription
//failure(errorDesc as NSString,false)
}
})
}
What is this error for and how I can remove it?
From here
if let data = ((value) as AnyObject).data(using: String.Encoding.utf8.rawValue) {
then any passed value have to be converted to Data and since you specify an integer ( NSNumber when wrapped inside json) hence the crash so change 4 to "4" and
"material_id":"\(materialId)",
It would be better if you follow like this:
//[String:String] instead of [String : Any]
let parameters :[String: String] = ["name":productNameTxt.text!,
"price":priceTxt.text!,
"size": sizeTxt.text!,
"weight":weightTxt.text!,
"quality":qualityTxt.text!,
"brand":brandTxt.text!,
"shippingCost":shippingCostTxt.text!,
"details":detailTxt.text!,
"material_id":"\(materialId)",
"material_type_id": subMaterialId,
"maxQuantity":4]
multipartFormData.append((value.data(using: .utf8))!, withName: key)

How to upload PDF file in swift using form-data or any other method

can we upload pdf/doc file using form-data in swift. Or any other way to upload. if possible please provide working example as i am new in this technology.
Its good to use excellent Alamofire library to upload any doc (such as pdf file).The below code will explain how to use alamofire to upload a file
Alamofire.upload(
multipartFormData: {
multipartFormData in
if let urlString = urlBase2 {
let pdfData = try! Data(contentsOf: urlString.asURL())
var data : Data = pdfData
multipartFormData.append(pdfData, withName: "pdfDocuments", fileName: namePDF, mimeType:"application/pdf")
for (key, value) in body {
multipartFormData.append(((value as? String)?.data(using: .utf8))!, withName: key)
}
print("Multi part Content -Type")
print(multipartFormData.contentType)
print("Multi part FIN ")
print("Multi part Content-Length")
print(multipartFormData.contentLength)
print("Multi part Content-Boundary")
print(multipartFormData.boundary)
}
},
to: url,
method: .post,
headers: header,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
print(" responses ")
print(response)
print("Responses ended")
onCompletion(true, "Something went wrong", 200)
}
case .failure(let encodingError):
print(encodingError)
onCompletion(false, "Something went wrong", 200)
}
})

Thread 1 : signal SIGABRT alamofire

I'm very new to Swift 3, and i have to do a GET request on my API. I'm using Alamofire, which uses Asynchronous functions.
I do exactly the same on my Android App, and the GET returns JSON data
This is my code in swift :
func getValueJSON() -> JSON {
var res = JSON({})
let myGroup = DispatchGroup()
myGroup.enter()
Alamofire.request(url_).responseJSON { response in
res = response.result.value as! JSON
print("first result", res)
myGroup.leave()
}
myGroup.notify(queue: .main) {
print("Finished all requests.", res)
}
print("second result", res)
return res
}
But i have a problem with the line "res = response.result.value" wich gives me the error : Thread 1 : signal SIGABRT
I really don't understand where the problem comes from, it was pretty hard to do a "synchronous" function, maybe i'm doing it wrong.
My objective is to store the result of the request in a variable that i return. Anyone can help ?
I'd recommend you to use Alamofire together with SwiftyJSON because that way you'll be able to parse JSON easier a lot.
Here's a classical example:
Alamofire.request("http://example.net", method: .get).responseJSON { response in
switch response.result {
case .success(let value):
let json = JSON(value)
print("JSON: \(json)")
case .failure(let error):
print(error)
}
}
If you need to pass parameters, or headers, just add it in the request method.
let headers: HTTPHeaders = [
"Content-Type:": "application/json"
]
let parameters: [String: Any] = [
"key": "value"
]
So your request will be something like this (this is POST request):
Alamofire.request("http://example.net", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
switch response.result {
case .success(let value):
print(value)
case .failure(let error):
print(error)
}
}
I haven't tested it, but it should work. Also, you need to set allow arbitary load to yes (App Transport Security Settings in info.plist) if you want to allow requests over HTTP protocol.
This is NOT recommended, but it's fine for development.

Swift Alamofire: How to get the HTTP response status code

I would like to retrieve the HTTP response status code (e.g. 400, 401, 403, 503, etc) for request failures (and ideally for successes too). In this code, I am performing user authentication with HTTP Basic and want to be able to message the user that authentication failed when the user mistypes their password.
Alamofire.request(.GET, "https://host.com/a/path").authenticate(user: "user", password: "typo")
.responseString { (req, res, data, error) in
if error != nil {
println("STRING Error:: error:\(error)")
println(" req:\(req)")
println(" res:\(res)")
println(" data:\(data)")
return
}
println("SUCCESS for String")
}
.responseJSON { (req, res, data, error) in
if error != nil {
println("JSON Error:: error:\(error)")
println(" req:\(req)")
println(" res:\(res)")
println(" data:\(data)")
return
}
println("SUCCESS for JSON")
}
Unfortunately, the error produced does not seem to indicate that an HTTP status code 409 was actually received:
STRING Error:: error:Optional(Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo=0x7f9beb8efce0 {NSErrorFailingURLKey=https://host.com/a/path, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://host.com/a/path})
req:<NSMutableURLRequest: 0x7f9beb89d5e0> { URL: https://host.com/a/path }
res:nil
data:Optional("")
JSON Error:: error:Optional(Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo=0x7f9beb8efce0 {NSErrorFailingURLKey=https://host.com/a/path, NSLocalizedDescription=cancelled, NSErrorFailingURLStringKey=https://host.com/a/path})
req:<NSMutableURLRequest: 0x7f9beb89d5e0> { URL: https://host.com/a/path }
res:nil
data:nil
Additionally, it would be nice to retrieve the HTTP body when an error occurs because my server-side will put a textual description of the error there.
Questions
Is it possible to retrieve the status code upon a non-2xx response?
Is it possible to retrieve the specific status code upon a 2xx response?
Is it possible to retrieve the HTTP body upon a non-2xx response?
Thanks!
For Swift 3.x / Swift 4.0 / Swift 5.0 users with Alamofire >= 4.0 / Alamofire >= 5.0
response.response?.statusCode
More verbose example:
Alamofire.request(urlString)
.responseString { response in
print("Success: \(response.result.isSuccess)")
print("Response String: \(response.result.value)")
var statusCode = response.response?.statusCode
if let error = response.result.error as? AFError {
statusCode = error._code // statusCode private
switch error {
case .invalidURL(let url):
print("Invalid URL: \(url) - \(error.localizedDescription)")
case .parameterEncodingFailed(let reason):
print("Parameter encoding failed: \(error.localizedDescription)")
print("Failure Reason: \(reason)")
case .multipartEncodingFailed(let reason):
print("Multipart encoding failed: \(error.localizedDescription)")
print("Failure Reason: \(reason)")
case .responseValidationFailed(let reason):
print("Response validation failed: \(error.localizedDescription)")
print("Failure Reason: \(reason)")
switch reason {
case .dataFileNil, .dataFileReadFailed:
print("Downloaded file could not be read")
case .missingContentType(let acceptableContentTypes):
print("Content Type Missing: \(acceptableContentTypes)")
case .unacceptableContentType(let acceptableContentTypes, let responseContentType):
print("Response content type: \(responseContentType) was unacceptable: \(acceptableContentTypes)")
case .unacceptableStatusCode(let code):
print("Response status code was unacceptable: \(code)")
statusCode = code
}
case .responseSerializationFailed(let reason):
print("Response serialization failed: \(error.localizedDescription)")
print("Failure Reason: \(reason)")
// statusCode = 3840 ???? maybe..
default:break
}
print("Underlying error: \(error.underlyingError)")
} else if let error = response.result.error as? URLError {
print("URLError occurred: \(error)")
} else {
print("Unknown error: \(response.result.error)")
}
print(statusCode) // the status code
}
(Alamofire 4 contains a completely new error system, look here for details)
For Swift 2.x users with Alamofire >= 3.0
Alamofire.request(.GET, urlString)
.responseString { response in
print("Success: \(response.result.isSuccess)")
print("Response String: \(response.result.value)")
if let alamoError = response.result.error {
let alamoCode = alamoError.code
let statusCode = (response.response?.statusCode)!
} else { //no errors
let statusCode = (response.response?.statusCode)! //example : 200
}
}
In the completion handler with argument response below I find the http status code is in response.response.statusCode:
Alamofire.request(.POST, urlString, parameters: parameters)
.responseJSON(completionHandler: {response in
switch(response.result) {
case .Success(let JSON):
// Yeah! Hand response
case .Failure(let error):
let message : String
if let httpStatusCode = response.response?.statusCode {
switch(httpStatusCode) {
case 400:
message = "Username or password not provided."
case 401:
message = "Incorrect password for user '\(name)'."
...
}
} else {
message = error.localizedDescription
}
// display alert with error message
}
Alamofire
.request(.GET, "REQUEST_URL", parameters: parms, headers: headers)
.validate(statusCode: 200..<300)
.responseJSON{ response in
switch response.result{
case .Success:
if let JSON = response.result.value
{
}
case .Failure(let error):
}
Best way to get the status code using alamofire.
Alamofire.request(URL).responseJSON {
response in
let status = response.response?.statusCode
print("STATUS \(status)")
}
Or use pattern matching
if let error = response.result.error as? AFError {
if case .responseValidationFailed(.unacceptableStatusCode(let code)) = error {
print(code)
}
}
you may check the following code for status code handler by alamofire
let request = URLRequest(url: URL(string:"url string")!)
Alamofire.request(request).validate(statusCode: 200..<300).responseJSON { (response) in
switch response.result {
case .success(let data as [String:Any]):
completion(true,data)
case .failure(let err):
print(err.localizedDescription)
completion(false,err)
default:
completion(false,nil)
}
}
if status code is not validate it will be enter the failure in switch case
In your responseJSON completion, you can get the status code from the response object, which has a type of NSHTTPURLResponse?:
if let response = res {
var statusCode = response.statusCode
}
This will work regardless of whether the status code is in the error range. For more information, take a look at the NSHTTPURLResponse documentation.
For your other question, you can use the responseString function to get the raw response body. You can add this in addition to responseJSON and both will be called.
.responseJson { (req, res, json, error) in
// existing code
}
.responseString { (_, _, body, _) in
// body is a String? containing the response body
}
Your error indicates that the operation is being cancelled for some reason. I'd need more details to understand why. But I think the bigger issue may be that since your endpoint https://host.com/a/path is bogus, there is no real server response to report, and hence you're seeing nil.
If you hit up a valid endpoint that serves up a proper response, you should see a non-nil value for res (using the techniques Sam mentions) in the form of a NSURLHTTPResponse object with properties like statusCode, etc.
Also, just to be clear, error is of type NSError. It tells you why the network request failed. The status code of the failure on the server side is actually a part of the response.
Hope that helps answer your main question.
I needed to know how to get the actual error code number.
I inherited a project from someone else and I had to get the error codes from a .catch clause that they had previously setup for Alamofire:
} .catch { (error) in
guard let error = error as? AFError else { return }
guard let statusCode = error.responseCode else { return }
print("Alamofire statusCode num is: ", statusCode)
}
Or if you need to get it from the response value follow #mbryzinski's answer
Alamofire ... { (response) in
guard let error = response.result.error as? AFError else { return }
guard let statusCode = error.responseCode else { return }
print("Alamofire statusCode num is: ", statusCode)
})
For Swift 2.0 users with Alamofire > 2.0
Alamofire.request(.GET, url)
.responseString { _, response, result in
if response?.statusCode == 200{
//Do something with result
}
}
For Swift 3.x / Swift 4.0 / Swift 5.0 users with Alamofire >= 5.0
Used request modifier to increase and decrease the timeout interval.
Alamofire's request creation methods offer the most common parameters for customization but sometimes those just aren't enough. The URLRequests created from the passed values can be modified by using a RequestModifier closure when creating requests. For example, to set the URLRequest's timeoutInterval to 120 seconds, modify the request in the closure.
var manager = Session.default
manager.request(urlString, method: method, parameters: dict, headers: headers, requestModifier: { $0.timeoutInterval = 120 }).validate().responseJSON { response in
OR
RequestModifiers also work with trailing closure syntax.
var manager = Session.default
manager.request("https://httpbin.org/get") { urlRequest in
urlRequest.timeoutInterval = 60
urlRequest.allowsConstrainedNetworkAccess = false
}
.response(...)
AF.request(url, method: .get).responseDecodable(of: Weather.self) { response in
switch response.result {
case .success(let data):
print(data)
var statusCode = response.response?.statusCode
if statusCode == 200 {
print(response)
}
case .failure(let error):
print(error)
}
}