Recently, I replace my old Alamofire requests with URLRequestConvertible protocol, for example, the country route is something like this:
enum CountryRoute: URLRequestConvertible {
case countries
var path: String {
switch self {
case .countries : return Path.Common.countries.rawValue
}
}
var method: HTTPMethod {
return .get
}
public var headers: HTTPHeaders {
return ["X-Mode" : "Light"]
}
func asURLRequest() throws -> URLRequest {
let url = try "example.com".asURL()
var request = URLRequest(url: url.appendingPathComponent(path))
request.httpMethod = method.rawValue
request.headers = headers
request.timeoutInterval = TimeInterval(10*1000)
return try URLEncoding.default.encode(request,with: nil)
}
}
and final the request is like this:
shared.session.request(convertible).validate().responseData { (response) in
// Do something
}
as I understand for each route we set headers, but what if we have global headers like "Content-Type" or "Accept-Language" or even Bearer Token?
Are there any ways to prevent add repetitious headers and add them globally? or we have to add these headers to each route?
Alamofire already generates some headers for the whole session when you using the default Session:
let session = Session.default
or the AF reference (which uses the default Session behind the scenes)
Those default headers include Accept-Encoding, Accept-Language and User-Agent. As for Content-Type header, URLEncoding or JSONEncoding are responsible for setting the correct values. (JSONParameterEncoder or URLEncodedFormParameterEncoder when you passing Encodable types as parameters)
However, you can override those headers by creating a new Session using your own URLSessionConfiguration:
let configuration = URLSessionConfiguration.af.default // Alamofire's default URLSessionConfiguration
configuration.headers["Content-Type"] = "application/json"
let session = Session(configuration: configuration)
Note: For Bearer token, there is a mechanism which requires the implementation of RequestInterceptor protocol in order to intercept every request and append the correct header. Using the same protocol you can also provide a refresh token functionality.
Related
Alamofire Version: 5.1
We are modifying our alamofire session configuration as following:
let apiManager: Session = {
let configuration = URLSessionConfiguration.af.default
configuration.timeoutIntervalForRequest = 20
configuration.requestCachePolicy = .reloadIgnoringCacheData
configuration.urlCache = nil
let manager = Alamofire.Session(
configuration: configuration,
cachedResponseHandler: ResponseCacher(behavior: .doNotCache)
)
return manager
}()
And we are making the request as following
apiManager.request(
url,
method: .post,
parameters: requestBodyParameters,
encoding: JSONEncoding.default,
headers: generateHeaders(enableAuth: authorisation) // to generate headers
)
.validate(statusCode: 200..<300)
.responseJSON { response in
print(response)
}
}
}
But the request configurations is not updated, they remain at default values
[Timeout]
60
[Cache policy]
UseProtocolCachePolicy
You're looking at the URLRequest generated by Alamofire which doesn't include the URLSession-level configuration, and which will never reflect the cache customization. If you want to see the actual URLRequest being performed after it passes through both Alamofire and URLSession, you can access the latest URLSessionTask that the Alamofire Request (in this case a DataRequest) performed by using request.task. However, like I said, this will never reflect the other behaviors you've attached, like a ResponseCacher, since those exist outside of the request pipeline.
I am trying to get a Client Credential token, which is needed for Spotify's public Web API to search for public tracks.
It is extremely simple to get a token using postman.
REQUEST BODY PARAMETER: VALUE: grant_type Set it to client_credentials.
The header of this POST request must contain the following parameter:
HEADER PARAMETER: VALUE: Authorization Base 64 encoded string that contains the client ID and client secret key. The field must have the format: Authorization: Basic <base64 encoded client_id:client_secret>
I need to get a Spotify token in Swift. I started with this decodable struct:
struct SpotifyAuth: Decodable {
var access_token: String
var expires_in: Int
}
I have then tried dozens of variations of the following code to no avail:
let headers : HTTPHeaders = ["Content-Type":"application/x-www-form-urlencoded"]
let paramaters : Parameters = ["grant_type": "client_credentials", ]
AF.request("https://accounts.spotify.com/api/token", method: .post, parameters: paramaters, encoding: URLEncoding.httpBody, headers: headers ).authenticate(username: "{clientID}", password:"{clientSecrent}").responseJSON { response in
if response.error == nil {
do {
let spotifyAuth = try JSONDecoder().decode(SpotifyAuth.self, from: response.data!)
completion(.success(spotifyAuth))
} catch let error {
completion(.failure(error))
}
} else {
completion(.failure(response.error!))
}
}
Is anyone aware of what I am doing wrong/the correct way of acquiring a simple token from Spotify?
authenticate() is used to respond to authentication challenges. It does not unconditionally add the header. If the server does not respond with a Basic challenge you'll need to add the header yourself. Alamofire has a convenience method to help: HTTPHeader.authorization(username:password:). This will properly generate the Basic header for you.
How to convert URL Request to cURL?
I know there are plugins available in Alamofire / Moya.
But I'm using native code.
Here is how I'm generating URLRequest.
var urlRequest = URLRequest(url: url) // URL is a param herel
var newHeaders: [String: String]
if let cookies = HTTPCookieStorage.shared.cookies {
newHeaders = HTTPCookie.requestHeaderFields(with: cookies)
} else {
newHeaders = [: ]
}
newHeaders = // add few more headers here
urlRequest.allHTTPHeaderFields = newHeaders
urlRequest.httpBody = data // set request body
How to log a cURL of above request?
You can simply try this
URLRequest to cURL
To use it in your code first import that extension and then simply call urlRequest.cURL() or urlRequest.cURL(pretty: true) to print pretty formatted curl.
EDIT: Now the above gist has Alamofire support also
My API includes authorization bearer token and three additional headers. My problem is I'm not sending the bearer token right (Postman return the correct data not my simulator). I see a lot of examples for using the request adapter but can I not use that? Thanks!
The auth is actually in the authorization tab not in the header.
**Updated:
Solved the problem by following the documentation.
HTTP Headers
Here is the Alamofire function with working codes:
func getBetsData(completion: ((Bool) -> ())? = nil) {
guard let token = defaults.string(forKey: "token") else {
return
}
let headers: HTTPHeaders = [
.authorization(bearerToken: token),
.init(name: "bet_type", value: type),
.init(name: "bet_status", value: status),
.init(name: "page", value: String(page))
]
AF.request("https://example.com", headers: headers).responseDecodable(of: Bets.self) { response in
switch response.result {
case .success:
if let data = response.data {
do {
let bets = try JSONDecoder().decode(Bets.self, from: data)
print("message: \(bets.message)")
self.setupTableData()
completion?(true)
} catch {
print("Error: \(error)")
completion?(false)
}
}
case.failure(let error):
print(error)
completion?(false)
}
}
}
You can add the headers directly:
let headers: HTTPHeaders = [.authorization(bearerToken: token)]
Additionally, if you're decoding a Decodable value from the response, you should not use responseJSON, as that decodes the Data using JSONSerialization and then you just parse it again. Instead, you should use responseDecodable.
AF.request(...).responseDecodable(of: Bets.self) { response in
// Use response.
}
That will be much more efficient and will capture errors for you automatically.
As mention by Dilan only token is not enought you will need to Bearer in the same Header parameter.
Here is one of the best method to handle Token Request and Retrier in all the request you send to server in the application
https://www.avanderlee.com/swift/authentication-alamofire-request-adapter/
By this you don't need to handle token manually in all the webservice.
I am trying to access my MAMP database webservice using Alamofire. Following is my code:
Following is my router to construct my URL:
enum Router: URLRequestConvertible {
static let baseURLString = "http://pushchat.local:44447/"
case PostJoinRequest(String,String,String,String,String)
var URLRequest: NSURLRequest {
let (path: String, parameters: [String: AnyObject]) = {
switch self {
case .PostJoinRequest (let addPath, let userID, let token, let nickName, let secretCode):
let params = ["cmd": "join", "user_id": "\(userID)", "token": "\(token)", "name": "\(nickName)", "code": "\(secretCode)"]
return (addPath, params)
}
}()
let URL = NSURL(string: Router.baseURLString)
let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: parameters).0
}
}
Following is my viewdidload code:
Alamofire.request(.POST,Router.PostJoinRequest("api.php","12345678901234","12345678901234","ABCDEF","TopSecret")).responseJSON()
{(request, response, JSON, error) in
println(JSON)
}
Following is the compile error:
Cannot invoke 'responseJSON' with an argument list of type '((,,,)->_)'
Following is the declaration from Almofire for your reference.
:param: method The HTTP method.
:param: URLString The URL string.
:param: parameters The parameters. `nil` by default.
:param: encoding The parameter encoding. `.URL` by default.
:returns: The created request.
*/
// public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
// return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
// }
Please let me know why am I facing this issue while chaining and what is it that I am not doing right?
Thanks for your help.
Dev
The compiler error message is really misleading – there is no problem with responseJSON but with request method itself.
In fact compiler does not like your second parameter. You are passing URLRequestConvertible but URLStringConvertible is expected (see the signature you posted).
Maybe you wanted to call another version of request method:
//:param: URLRequest The URL request
//:returns: The created request.
public func request(URLRequest: URLRequestConvertible) -> Request
In that case you have to adjust your Router class and set HTTP method into NSURLRequest created inside. For example:
let URLRequest = NSMutableURLRequest(URL: URL!.URLByAppendingPathComponent(path))
URLRequest.HTTPMethod = "POST"
Note you will also probably need to use another parameter/data encoding.