Alamofire parameters from textfield - swift

#IBAction func login(sender: UIButton) {
let parameters = [
"username": usernameTextField?.text,
"password": passwordTextField?.text
]
Alamofire.request(.POST, "http://192.168.1.107:8080/api/v1/user/login", parameters: parameters, encoding: .JSON)
.validate()
.responseJSON { response in
switch response.result {
case .Success(let JSON):
print("Validation Successful")
if (JSON["status"] as! String != "Success") {
self.popup("Invalid Account", message: "Please check your username and password and try again")
}
case .Failure(let error):
print(error)
}
}
}
The parameters worked fine when I have the username and password hardcoded inside the dictionary. But when I changed it to a textfield, Im getting the error that says Cannot convert value of type [String: String?] to [String: Anyobject]?
How can I make the parameters anyobject so it would be accepted?

The error message is showing the there is optional type in params.
just unwrap the values before using it if you are creating textfield at run time.
if not creating at runtime simply use it like
let parameters = [
"username": usernameTextField.text,
"password": passwordTextField.text
]

Related

cast server response in AFError

What I'm trying to achieve is that I have a NetworkManager that handles the request's to the server, and handle the error through AFError.
However sometimes when the server response is 4xx, there is a custom message with that response which I want to show that to the user But don't know how to implement it.
This is my NetworkManager
static let shared:NetworkManager = {
return NetworkManager()
}()
typealias completionHandler = ((Result<Data, AFError>) ->Void)
func handleAFrequest(request: DataRequest,completion: #escaping completionHandler) {
request.validate(statusCode: 200..<300)
request.responseJSON { (response) in
switch response.result {
case .success(_):
if let data = response.data {
completion(.success(data))
}
case .failure(let error):
print(error.localizedDescription)
switch error {
case .invalidURL(let url):
print("Invalid URL: \(url) - \(error.localizedDescription)")
completion(.failure(.invalidURL(url: URL)))
case .responseValidationFailed(let reason):
print("Response validation failed: \(error.localizedDescription); Reason:\(reason)")
completion(.failure(.responseValidationFailed(reason: reason)))
I want to be able to cast server response in addition to the error, and show Message of the response to the user.
Server Response example when StatusCode is 4xx:
{
"data":
"code":401;
"Message":"Phone Invalid"
}
I have parsed api errors in many of my projects. I believe there is a better alternative to handle the showing or errors if any, to the user. Please see my code, in it, if there is a error I show it in a toast. Showing in a toast is the not focal point but you can see how I handle the error case in my code and it has never failed. Please change the params accordingly to your api call
func postMethod(mylist: [String:Any]) {
print(K.APIUrl)
print(K.port)
AF.request("\(K.urlFromUrlField!):\(K.configPort)/snhttp-01?", method: .put, parameters: mylist)
.authenticate(username: username, password: password)
.response { response in
switch response.result {
case .success:
print("\nValidation Successful from put method")
print(response.result)
print(response.value as Any)
//get xml code and error msg if any
if let response = response.data{
let xml = XML.parse(response)
print(xml)
print("\nThis is the data sent to the server: \(mylist["data"] ?? "No data in data key of the parameter")" )
let code = xml.Response.Code.text ?? "No code value in response"
let responseMessage = xml.Response.Message.text ?? "No message returned from server"
print("\nCode value from server: \(code)")
print("\nResponse message from server: \(responseMessage)")
}
else{
print("\nSuccess block: Request Successfully sent, BUT there was nothing from the server to unwrap! / nothing sent back from the server\nThis is the data sent to the server: \(mylist["data"] ?? "No data in data key of the parameter")")
}
case let .failure(error):
if let response = response.data {
let xml = XML.parse(response)
let code = xml.Response.Code.text ?? "\nNo code value in response"
let responseMessage = xml.Response.Message.text ?? "No message returned from server"
print("\nCode value from server: \(code)")
print("\nResponse message from server: \(responseMessage)")
print(error)
}
else {
print("\nFailure Block: A connection to the server could not be established")
}
}}
}
This code parses the xml from the api. However you can discard that and just focus on how I handle the response and consequently the error.
This is the solution that works for me.
All you need to do is create a custom error type:
struct APIError: Error, Decodable {
let status: Int?
let message: String?
let key: String?
}
Then call Alamofire, which will return an AFDataResponse which you can parse:
func performRequest<T: Decodable>(route: APIRouter, completion: #escaping (APIResponse<T>) -> Void) {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
AF.request(route)
.validate()
.responseDecodable(decoder: decoder, emptyResponseCodes: [200, 201]) { (response: AFDataResponse<T>) in
self.parseResponse(response: response, completion: completion)
}
}
Parsing is done like this:
private func parseResponse<T: Decodable>(response: AFDataResponse<T>, completion: #escaping (APIResponse<T>) -> Void) {
switch response.result {
case .success(let data):
completion(APIResponse<T>.success(data))
case .failure(let error):
if let data = response.data,
// THIS IS WHERE YOU CAST AFError TO YOUR CUSTOM APIError
let apiError = try? JSONDecoder().decode(APIError.self, from: data) {
completion(APIResponse.failure(apiError))
} else {
completion(APIResponse.failure(error))
}
}
}
Hope this helps!

Alamofire get method with parameters in the url with header swift

I have a get method with 3 parameters on the base url itself.I have tried the following code, but it is going to failure condition saying either not a valid url or not a valid JSON.
What is the correct way to approach this?
The code i have used is as below:
let header: HTTPHeaders = ["Content-Type":"application/json","x-token":self.token!]
let todosEndpoint: String = "https://reachwebdemo.com/2020/10/listcribdev/api/chatnotification?" + "channel_sid=\(self.channelsid!)&author=\(self.userid!)&message=\(inputMessage)"
if let encoded = todosEndpoint.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),let url = URL(string: encoded)
{
print("notify url is",url)
AF.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: header).responseString { response in
switch response.result {
case .success(let json):
print("Validation Successful for push notification",json)
case let .failure(error):
print("error for push notificaton",error.errorDescription)
}
}
}
You user parameters like url and it's wrong way to make request like this.
You need add parameters on request method like parameters. And yes, you can use parameters on 'GET' requests.
let header: HTTPHeaders = ["Content-Type":"application/json","x-token":self.token!]
let todosEndpoint: String = "https://reachwebdemo.com/2020/10/listcribdev/api/chatnotification"
let params:[String: Any] = ["channel_sid":self.channelsid!, "author":self.userid!, "message": inputMessage]
if let encoded = todosEndpoint.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
let url = URL(string: encoded) {
print("notify url is",url)
AF.request(url, method: .get, parameters: params, encoding: URLEncoding.default, headers: header).responseString { response in
switch response.result {
case .success(let json):
print("Validation Successful for push notification",json)
case let .failure(error):
print("error for push notificaton",error.errorDescription)
}
}
}

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)

Firebase update: Initializer for conditional binding must have Optional type, not 'String'

I recently updated my Firebase pods and now receive this error: "Initializer for conditional binding must have Optional type, not 'String' " for the second line of code.
storageRef.downloadURL { (url, error) in
guard let url = url, let profileImageUrl = url.absoluteString else { return }
let values = ["name": name, "email": email, "profileImageUrl": profileImageUrl]
self.registerUserIntoDatabaseWithUID(uid: uid, values: values as [String: AnyObject])
}
Please ⌥-click on the arguments in the optional binding expression, you will see that absoluteString is declared as non-optional. A non-optional cannot be conditional bound as the error message states.
Bind only url and add absoluteString in values:
storageRef.downloadURL { (url, error) in
guard let url = url else { return }
let values = ["name": name, "email": email, "profileImageUrl": url.absoluteString]
self.registerUserIntoDatabaseWithUID(uid: uid, values: values as [String: AnyObject])
}

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.