I am using Async library found here in order to wait for first alamofire request to complete before running my second from the ViewDidLoad. The library seems simple to use but I can never get the first request to wait. My code is as follows:
let group = AsyncGroup()
group.utility
{
self.getToken()
}
group.wait()
self.getDevices()
I would like the getToken function to complete the Alamofire request and its completion handler before moving on to the getDevices request. Both are very simple Alamofire requests.
EDIT:
This is the getToken request. The token variable is not getting updated with the alamofire response before second alamofire request is being called.
func getToken()
{
let httpheader: HTTPHeaders =
[
"Content-Type": "application/json"
]
// Dev
let param = [params here]
Alamofire.request("url", method: .post, parameters: param, encoding: JSONEncoding.default, headers:httpheader).validate()
.responseData { response in
switch response.result {
case .success:
if let data = response.data {
let xml = SWXMLHash.parse(data)
token = (xml["authResponse"] ["authToken"].element?.text)!
}
case .failure:
print ("error")
}
}
}
Your getToken looks more like:
func getToken(whenDone:(String?)->()) {
let httpheader: HTTPHeaders = [
"Content-Type": "application/json"
]
// Dev
let param = [params here]
Alamofire.request("url", method: .post, parameters: param, encoding: JSONEncoding.default, headers:httpheader).validate()
.responseData { response in
switch response.result {
case .success:
if let data = response.data {
let xml = SWXMLHash.parse(data)
token = (xml["authResponse"] ["authToken"].element?.text)!
whenDone(token)
}
case .failure:
print ("error")
whenDone(nil)
}
}
}
and the calling sequence just becomes:
getToken() { token ->
guard let token = token else {
return
}
getDevices()
}
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'm working on a project where I need to upload files to channels in a slack workspace using slack's API. This is how I'm reading the file contents:
func readFile(localFilePath: String) -> Data? {
var contents: Data
let url = URL(fileURLWithPath: localFilePath)
do {
contents = try Data(contentsOf: url)
} catch {
return nil
}
return contents
}
However, this is what I get on slack:
I have also tried reading the file as a String, like this:
contents = try String(contentsOf: url, encoding: .ascii)
This way slack appears to recognize it's a PDF, but it's blank (see image below).
If I add an explicit Content-Type header and set it to multipart/form-data;, I get an invalid_form_data error in response.
Here's the request code:
let reqHeaders: HTTPHeaders = [
"Authorization": "",
"Accept": "application/json",
"Content-Type": "multipart/form-data;",
]
let params: [String:Any] = [
"channels": channelSelections.joined(separator: ","),
"content": fileData,
"initial_comment": messageText,
"filename": filename
]
AF.request(url, method: .post, parameters: params, headers: reqHeaders).responseJSON { response in
switch response.result {
case .success:
if let data = response.data {
do {
let json = try JSON(data: data)
if json["ok"].boolValue {
// some code
} else {
print("\n\n\n\(json)\n\n\n")
}
}
catch{
print("JSON Error")
}
}
case let .failure(error):
print(error)
}
}
What's the correct way to read the file and send as payload to slack's API?
i'n trying to do some request on the MapMyFitness API (Oauth2).
I can get my Authorization Code with the oauth2 identification, but now i need to request to some route.
Here is the request :
Alamofire.request(self.oauthClient.baseURL() + "workout/",
method: .get,
parameters: nil,
headers: authorizationHeader
)
.validate()
.responseData { [weak self] (response) in
switch response.result {
case .success(let data):
do {
let jsonResponse = try JSON(data: data)
success(items);
self?.isLoading = false;
} catch {
self?.isLoading = false;
failed(nil)
}
break;
case .failure(let error):
print(error);
self?.isLoading = false;
failed(error)
break;
}
}
In the header here is the authorization code like this :
"Authorization": "Bearer ***********************************"
Here is the doc about the route API : https://developer.underarmour.com/docs/v71_Workout
Alamofire error:
responseValidationFailed(Alamofire.AFError.ResponseValidationFailureReason.unacceptableStatusCode(403))
Am i missing something ? Thx
Problem founded !
If you are using MapMyFitness API you have to use https://www.mapmyfitness.com/v7.1/ url for the authentification but when you need to request route it's https://api.ua.com/v7.1/.
Moreover, you need to add in your header the "api-key".
["Authorization": "Bearer ***********************************", "api-key": client_id]
and you need to set an user_id in params.
var params = ["user" : self.userID, field_set: "time_series"] as [String : Any]
These informations are not in the documentation.
Here is my final request :
self.authorizationHeader.updateValue(self.clientID, forKey: "api-key")
var params = ["field_set" : "time_series",
"user" : self.userID] as [String : Any]
Alamofire.request(self.url + "/workout/",
method: .get,
parameters: params,
headers: self.authorizationHeader
)
.validate()
.responseData { [weak self] (response) in
switch response.result {
case .success(let data):
do {
let jsonResponse = try JSON(data: data)
success(items);
} catch {
failed(nil)
}
break;
case .failure(let error):
print(error);
failed(error)
break;
}
}
}
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)
}
}
I have recently upgraded to Alamofire 2.0, and now my Put request is failing with a 400 error, when it was previously working properly. I perform the call with the code:
Alamofire.request(Router.Put(query: url, params: params, encoding: .JSON))
.validate()
.responseJSON() {
(request, response, result) in
print("request: \(request)")
print("response: \(response)")
print("result: \(result)")
switch result {
case .Success(_):
// success
case .Failure(let data, _):
// error occured
}
}
and my custom Router class:
enum Router: URLRequestConvertible {
case Get(query: String, params: [String: AnyObject]?)
case Post(query: String, params: [String: AnyObject]?)
case Put(query: String, params: [String: AnyObject]?, encoding: ParameterEncoding)
case Delete(query: String, params: [String: AnyObject]?)
var URLRequest: NSMutableURLRequest {
var encodeMethod: Alamofire.ParameterEncoding = Alamofire.ParameterEncoding.URL
// Default to GET
var httpMethod: String = Alamofire.Method.GET.rawValue
let (path, parameters): (String, [String: AnyObject]?) = {
switch self {
case .Get(let query, let params):
// Set the request call
httpMethod = Alamofire.Method.GET.rawValue
// Return the query
return (query, params)
case .Post(let query, let params):
// Set the request call
httpMethod = Alamofire.Method.POST.rawValue
// Return the query
return (query, params)
case .Put(let query, let params, let encoding):
// Set the request call
httpMethod = Alamofire.Method.PUT.rawValue
// Set the encoding
encodeMethod = encoding
// Return the query
return (query, params)
case .Delete(let query, let params):
// Set the request call
httpMethod = Alamofire.Method.DELETE.rawValue
// Return the query
return (query, params)
}
}()
// Create the URL Request
let URLRequest = NSMutableURLRequest(URL: NSURL(string: Globals.BASE_URL + path)!)
// set header fields
if let key = NSUserDefaults.standardUserDefaults().stringForKey(Globals.NS_KEY_SESSION) {
URLRequest.setValue(key, forHTTPHeaderField: "X-XX-API")
}
// Add user agent
if let userAgent = NSUserDefaults.standardUserDefaults().stringForKey(Globals.NS_KEY_USER_AGENT) {
URLRequest.setValue(userAgent, forHTTPHeaderField: "User-Agent")
}
// Set the HTTP method
URLRequest.HTTPMethod = httpMethod
URLRequest.cachePolicy = NSURLRequestCachePolicy.ReloadIgnoringLocalCacheData
return encodeMethod.encode(URLRequest, parameters: parameters).0
}
}
Instead of the call being a success, the response is:
response: Optional(<NSHTTPURLResponse: 0x7fcee15b34d0> { URL: https://apiurl } { status code: 400, headers {
"Cache-Control" = "no-cache";
"Content-Length" = 26;
"Content-Type" = "application/json; charset=utf-8";
Date = "Tue, 15 Sep 2015 15:33:50 GMT";
Expires = "-1";
Pragma = "no-cache";
Server = "Microsoft-IIS/8.5";
} })
I looked into the problem and on the server side, Content-Type is coming in as blank for the request, when it should be coming in as application/json. The Content-Type should automatically get added when there is body data in the request. I have the params set:
// Save the profile
var params: [String: AnyObject] = ["indexPhoto": userProfile.indexPhoto,
"dob": df.stringFromDate(userProfile.dob) as NSString,
"identAs": userProfile.identAs]
// Add manually since creating the dictionary all at once is too much for swift to handle
params.updateValue(String(format:"%.2f", userProfile.heightIn), forKey: "heightIn")
params.updateValue(String(format:"%.2f", userProfile.weightLbs), forKey: "weightLbs")
params.updateValue(userProfile.eyes, forKey: "eyes")
params.updateValue(userProfile.hair, forKey: "hair")
...
Is there something I could be missing with this? Before I upgraded to Alamofire 2.0 this call was working just fine.
Maybe there is a different way but I fixed the same issue by sending empty params instead of nil. I don't use any parameters when using .PUT and because of that server doesn't know how to encode request and response (even if I explicitly set content type inside header, I receive blank content type on the server) so my solution was sending empty params like in the code below. Hope it helps someone.
var url = https://api.mysite.com/v1/issue/369613/delete
let params = ["":""]
let headers = NetworkConnection.addAuthorizationHeader(token, tokenType: tokenType)
manager.request(.PUT, url, parameters: params, encoding: .JSON, headers: headers)
.responseJSON { response in
if let JSONdata = response.result.value {
print("JSONdata: \(JSONdata)")
}
}