Accept more than one status code range with Alamofirie request - swift

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):
...
}
})

Related

Swifui print value of data in response

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)
}
}

Compare Alamofire response with String

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)
}
}
}

Alamofire dealing with special character parameters

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

While Loop increments and re-runs before Promisekit can finish running and return the proper value in Swift

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
}
}

Swift Alamofire response status code

How can I get status code from the Alamofire response?
In the latest version I can use validate but I need to check what the status code is.
Code:
Alamofire.request(.GET, "http://example.com/url")
.responseJSON { response in
}
This works (after #MirzaDelic pointed out my mistake):
Alamofire.request(.get, "http://www.google.com")
.responseJSON { response in
if response.response.statusCode == 404 {
// do something
} else {
// do something else
}
}
In Alamofire 4.0 swift3 branch, I needed exactly:
Alamofire.request(request, withMethod: .get, parameters: parameters, encoding: .url, headers: nil)
.validate()
.responseJSON { response in
switch response.result {
case .success:
let statusCode = (response.response?.statusCode)!
print("...HTTP code: \(statusCode)")
}
}
While the above methods will work, there is a more efficient way to do this. response.response.statusCode returns a status code that could very in large amounts. Alamofire includes two functions that are useful for this case, isSuccess and isFailure.
Alamofire.request(.get, "http://example.com/url").responseJSON { response in
let responseErrorCode = response.response!.statusCode
if response.result.isSuccess {
print("Successful HTTP code: \(responseErrorCode)")
// Run code if request is successful
} else if response.result.isFailure {
print("Failure HTTP code: \(responseErrorCode)")
// Run code if request isn't successful
}
}
By using the code above you can easily know whether the HTTP code is good or bad.
With Alamofire 5.6, if you add .validate(statusCode: [200]) to your AF request you can then inspect the AFError of the result.
func request(_ requestConvertible: any URLRequestConvertible) async throws {
switch await AF
.request(requestConvertible, interceptor: interceptor)
.validate(statusCode: [200])
.serializingDecodable(Empty.self, emptyResponseCodes: [200])
.result {
case .failure(let error):
if error.responseCode == 401 {
// Handle 401 statusCode
}
throw error
case .success:
break
}
}
Or for a Decodable type (from a JSON response):
func requestDecodable<Value: Decodable>(_ requestConvertible: any URLRequestConvertible, to type: Value.Type) async throws -> Value {
let request = AF.request(requestConvertible, interceptor: interceptor)
.validate(statusCode: [200])
let response = request.serializingData(emptyResponseCodes: [200])
let result = await response.result
switch result {
case .failure(let error):
if request.response?.statusCode == 401 {
// Handle 401 statusCode
}
throw error
case .success(let data):
return try JSONDecoder().decode(type.self, from: data)
}
}