I'm trying to compare the answer that Alamofire returns with a series of values, But I do not understand how I should compare the response with a String. This is my code
Alamofire.request(urlRegister, method: .post, parameters: parameters).responseString { response in
let myResponse: String = response.result.value!
if let data = response.data, let myResponse2 = String(data: data, encoding: .utf8) {
if myResponse2 == "Insert" {
print("Insert")
} else if myResponse2 == "exists" {
print("exists")
} else {
print("Conetion Error")
}
}
}
I have also tried to store the response in a String variable, but it also does not allow me to make the comparison.I thank for any help, thanks in advance.
Your code seems fine if the response has a valid data. But in case of error, you will not be able find a reason for unwanted result. Better to use the response result enum to get a clear idea of what is being happening as below,
Alamofire.request(urlRegister, method: .post, parameters: parameters).responseString { response in
switch response.result {
case .success(let value):
print(value)
if value == "Insert" {
print("This is insert!")
} else if value == "exists" {
print("Value exists!")
} else {
print("Some unwanted value!")
}
case .failure(let error):
print(error)
}
}
}
Related
I am using a post API method like this
AF.request("http://40.86.255.119/api/Authentication", method:.post, parameters: parameters,encoding: JSONEncoding.default) .responseJSON { (response) in
print(response)
}
It's showing me a response
success({
data = {
security = (
);
token = "eyJhbGc";
};
key = "<null>";
message = "Login Successfully";
succeed = 1;
})
I need to print just a token of data and need to know how can I do this?
Response screenshot by swagger
I try to like this but the issue is how ill show token that is inside the data array. It's showing a message which is outside an array but the token is inside an array.
AF.request("http://40.86.255.119/api/Authentication", method: .post, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
switch response.result {
case .success(let value):
if let json = value as? [String: Any] {
print(json)
print(json["message"]) //this work
print(json["data"]["token"]) //this didnt work
}
case .failure(let error):
print(error)
}
}
I highly recommend to take advantage of the JSON decoding capabilities of AF.
Create two structs
struct Response : Decodable { let data : TokenData }
struct TokenData : Decodable { let token : String }
And replace .responseJSON with .responseDecodable and an appropriate type annotation of response.
In any case you should handle the potential error
AF.request("http://40.86.255.119/api/Authentication", method:.post, parameters: parameters) .responseDecodable { (response : DataResponse<Response,AFError>) in
switch response.result {
case .success(let result): print(result.data.token)
case .failure(let error): print(error)
}
}
I have an app where I used RxSwift for my networking by extending ObservableType this works well but the issue I am having now is when I make an API request and there is an error, I am unable to show the particular error message sent from the server. Now how can I get the particular error response sent from the server
extension ObservableType {
func convert<T: EVObject>(to observableType: T.Type) -> Observable<T> where E: DataRequest {
return self.flatMap({(request) -> Observable<T> in
let disposable = Disposables.create {
request.cancel()
}
return Observable<T>.create({observer -> Disposable in
request.validate().responseObject { (response: DataResponse<T>) in
switch response.result {
case .success(let value):
if !disposable.isDisposed {
observer.onNext(value)
observer.onCompleted()
}
case .failure(let error):
if !disposable.isDisposed {
observer.onError(NetworkingError(httpResponse: response.response,
networkData: response.data, baseError: error))
observer.onCompleted()
}
}
}
return disposable
})
})
}
}
let networkRetryPredicate: RetryPredicate = { error in
if let err = error as? NetworkingError, let response = err.httpResponse {
let code = response.statusCode
if code >= 400 && code < 600 {
return false
}
}
return true
}
// Use this struct to pass the response and data along with
// the error as alamofire does not do this automatically
public struct NetworkingError: Error {
let httpResponse: HTTPURLResponse?
let networkData: Data?
let baseError: Error
}
response from the server could be
{
"status" : "error",
"message" : " INSUFFICIENT_FUNDS"
}
or
{
"status" : "success",
"data" : " gghfgdgchf"
}
my response is handled like this
class MaxResponse<T: NSObject>: MaxResponseBase, EVGenericsKVC {
var data: T?
public func setGenericValue(_ value: AnyObject!, forUndefinedKey key: String) {
switch key {
case "data":
data = value as? T
default:
print("---> setGenericValue '\(value)' forUndefinedKey '\(key)' should be handled.")
}
}
public func getGenericType() -> NSObject {
return T()
}
}
the error is
return ApiClient.session.rx.request(urlRequest: MaxApiRouter.topupWall(userId: getUser()!.id!, data: body))
.convert(to: MaxResponse<Wall>.self)
In the official Alamofire docs it is mentioned that validate(), without any parameters:
Automatically validates status code within 200..<300 range, and that
the Content-Type header of the response matches the Accept header of
the request, if one is provided.
So if you do not include Alamofire's validate() you are saying that no matter the status code, if the request did get through, you will consider it successful, so that's why it shows nothing in the failure block.
However if you prefer to use it, yes, it will give you an ResponseValidationFailureReason error, but you still have access to the response.data. Try printing it, you should see the expected error response from the server:
if let responseData = response.data {
print(String(data: responseData, encoding: .utf8))
}
the ServiceKey I got for the api is mixed with complex characters, like D%2FFgugDIl1le9xiY7be1ge%2B0Q%3D%3D
and when I put the key in the params of alamofire and use the .get keyword, my key transforms and when the url is actually created, it becomes a totally different key.
is there any way to solve this problem?
This is the code I am using
Alamofire.request(BusURL, method: .get, parameters: ["cityCode": 25, "routeId":"DJB30300052ND", "ServiceKey": key])
.responseString { response in
print(" - API url: \(String(describing: response.request!))") // original url request
var statusCode = response.response?.statusCode
switch response.result {
case .success:
print("status code is: \(String(describing: statusCode))")
if let string = response.result.value {
print("XML: \(string)")
}
case .failure(let error):
statusCode = error._code // statusCode private
print("status code is: \(String(describing: statusCode))")
print(error)
}
}
Try below:
let params = ["cityCode": "25", "routeId":"DJB30300052ND"]
var urlParams = params.flatMap({ (key, value) -> String in
return "\(key)=\(value)"
}).joined(separator: "&")
let key = "&ServiceKey=D%2FFgugDIl1le9xiY7be1ge%2B0Q%3D%3D"
urlParams.append(key)
let url = "https://google.com?\(urlParams)"
print("url\(url)")
Alamofire.request(url, method: .get).validate().responseString(completionHandler: {response in
switch response.result{
case .success:
let s = response.result.value ?? "Empty Result"
print("response \(s)")
case .failure:
print("Call Failed")
debugPrint(response)
}
})
output: https://google.com?cityCode=25&routeId=DJB30300052ND&ServiceKey=D%2FFgugDIl1le9xiY7be1ge%2B0Q%3D%3D
I'm trying to use a while loop with Promisekit with Alamofire to chain four GET requests, return a value, and rerun the four requests with a new parameter. This is the current code that I'm using:
var index = 0
var count = classDictionary["class"]!.count-1
while index <= count {
firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}
index += 1
}
Each of the first three functions returns a promised string value which is then passed onto the next function until the fourth and final function calls another function to parse the downloaded HTML using Kanna.
Ideally I would like all four functions to be called and completed after which the index will increment and run the loop again using the new index number. As a note, the index in passed onto the functions as a way to identify which index in an array a value should be compared to.
For clarity, I have included the code for the parseBooksXML functions below:
func parseBooksXML(index: Int) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("[a-zA-Z]{2,4}", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.abrevCode = (xml["departments"]["department"].withAttr("abrev", result).element!.attribute(by: "id")!.text)
}
catch {
print("Error: \(error)")
}
fulfill(self.abrevCode)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML2(index: Int, key: String) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("\\d\\d\\d", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.courseNumber = (xml["courses"]["course"].withAttr("name", result).element?.attribute(by: "id")?.text)!
}
catch {
print("Error: \(error)")
}
fulfill(self.courseNumber)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML3(index: Int, key: String) -> Promise<String> {
return Promise {fulfill, reject in
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
let xml = SWXMLHash.parse(response.data!)
do {
let range = self.classDictionary["class"]![index].rangeOfString("[a-zA-Z]{1,3}?\\d?\\d?\\d?$", options: .RegularExpressionSearch)
let result = self.classDictionary["class"]![index].substringWithRange(range!)
try self.instructorCode = (xml["sections"]["section"].withAttr("instructor", self.classTeacher[index]).element?.attribute(by: "id")?.text)!
}
catch {
print("Error: \(error)")
}
fulfill(self.instructorCode)
case .Failure(let error):
print(error)
}
}
}
}
func parseBooksXML4(key: String) -> Void {
let headers = [
"Referer": "URL"
]
Alamofire.request(.GET, "URL", headers: headers)
.responseData { response in
switch response.result {
case .Success:
self.parseISBN(String(data: response.data!, encoding: NSUTF8StringEncoding)!)
case .Failure(let error):
print(error)
}
}
}
Any help would be appreciated!
You need to use when:
let count = classDictionary["class"]!.count-1
let promises = (0..<count).map { index -> Promise<ReplaceMe> in
return firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}
}
when(fulfilled: promises).then {
//…
}
Since parseBooksXML4 call is async, you should wrap parseBooksXML4() call to return promise and wait for that to finish before increment index.
firstly {
parseBooksXML(index)
}.then { abrevCode in
self.parseBooksXML2(index, key: abrevCode)
}.then { courseNumber in
self.parseBooksXML3(index, key: courseNumber)
}.then { instructorCode in
self.parseBooksXML4(instructorCode)
}.then { _ in
index += 1
}
}
I would like to accept classic status code range (2XX) but also some extra error status code. So, how can i do this with the validate method of Alamofire Request?
Something like that:
Alamofire.request(self)
.validate(statusCode: [ 200..<300 , 403 ])
.responseJSON { response in
switch response.result {
case .Success(let JSON):
...
case .Failure(let error):
...
}
})
Alamofire accepts a Range<Int> parameter with acceptable codes. A range requires that all elements are consecutive, so you'll have to code your own validator. This should work:
.validate { _, response in
let acceptableStatusCodes: [Range<Int>] = [200..<300, 403...403]
if acceptableStatusCodes.map({$0.contains(response.statusCode)}).reduce(false, combine: {$0 || $1}) {
return .Success
} else {
let failureReason = "Response status code was unacceptable: \(response.statusCode)"
return .Failure(Error.errorWithCode(.StatusCodeValidationFailed, failureReason: failureReason))
}
}
You can also declare it in a Request extension for better code reusability:
extension Request {
func validateStatusCode() -> Request {
return self.validate { _, response in
let acceptableStatusCodes: [Range<Int>] = [200..<300, 403...403]
if acceptableStatusCodes.map({$0.contains(response.statusCode)}).reduce(false, combine: {$0 || $1}) {
return .Success
} else {
let failureReason = "Response status code was unacceptable: \(response.statusCode)"
return .Failure(Error.errorWithCode(.StatusCodeValidationFailed, failureReason: failureReason))
}
}
}
}
And call it like this:
Alamofire.request(self)
.validateStatusCode()
.responseJSON { response in
switch response.result {
case .Success(let JSON):
...
case .Failure(let error):
...
}
})