Error 403 on MapMyFitness API request - swift

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

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

Multipart form data upload with multiple images + parameters (Alamofire 5.2)

I am trying to upload multiple images and parameters to the server using 'Alamofire 5.2)'
This is what the server expects
[{"key":"media1","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
[{"key":"media2","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
[{"key":"media3","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
{"key":"model","value":"{\"emp_Id\": \"6\",\"device_Identifier\":\"Device 01\",\"timestamp\":\"123\,”\description\”:\”description\",”handoverStatus”:”false”}","type":"text"}]
//Please note the array part
This is how i add picked image to an image array
var dictImage : [UIImage]?
if pickedImage.pngData() != nil
{
dictImage!.append(pickedImage)
}
This is how im trying to call the alamofire upload
func xyz()
{
let param : [String: Any] = [
"emp_Id" : "",
"device_Identifier" : "",
"timestamp" : "",
"description" : "",
"handoverStatus" : ""
]
let urlString = URL(string: APIDetails.BaseUrl + "api/123")!
let url = (try? URLRequest(url: urlString, method: .post, headers: APIDetails.header))!
AF.upload(multipartFormData: { (multipartData) in
let img1 = arrImage![0]
let img2 = arrImage![1]
let img3 = arrImage![2]
multipartData.append(img1.pngData(), withName:"media1", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
multipartData.append(img2.pngData(), withName:"media2", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
multipartData.append(img3.pngData(), withName:"media3", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
for (key, value) in param {
multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, with: url)
}
How do i upload every image and its details as a dictionary?
How do i add upload progress and result that shows if anything failed or not ?
I tried with
}, with: url,encodingCompletion:{
EncodingResult in
switch EncodingResult{
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint("SUCCESS RESPONSE: \(response)")
}
case .failure(let encodingError):
print("ERROR RESPONSE: \(encodingError)")
} })
But it gives error Extra argument 'encodingCompletion' in call
(This is my first time trying to upload multipart form data, the code/part of it was taken from another answer)
try this code, I have not tested in Alamofire(5.2)
let baseUrl = "your URL"
let fullUrl = baseUrl + strUrl
var headers : HTTPHeaders = HTTPHeaders(["Content-type" : "multipart/form-data"])
if let header = header{
headers = header
}
guard let url = try? URLRequest(url: fullUrl, method: .post, headers: headers) else {return}
AF.upload(multipartFormData: { (multipartData) in
for i in 0 ..< arrImage.count{
if let imageData = arrImage[i].pngData(){
let mediaName = "media\(i + 1)"
multipartData.append(imageData, withName:mediaName, fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
}
}
for (key, value) in parameter {
multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, to: url).responseJSON(queue: .main, options: .allowFragments) { (response) in
switch response.result{
case .success(let value):
print("Json: \(value)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}.uploadProgress { (progress) in
print("Progress: \(progress.fractionCompleted)")
}

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

Running Two Alamofire Requests in Sequence Swift

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

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