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.
Related
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.
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 using Alamofire for calling my API .
below is is the code.
func alamofirePost() {
let headers: HTTPHeaders = [ "content-type": "x-www-form-urlencoded"]
let todosEndpoint: String = "http://54.244.108.186:4000/api/post_upc"
let newTodo: [String: Any] = ["UPC": codeTextView.text, "DATE_TIME": currentTime() ]
print("i am in alamo")
Alamofire.request(todosEndpoint, method: .post, parameters: newTodo ,encoding: JSONEncoding.default,headers: headers )
.responseJSON { response in
guard response.result.error == nil else {
// got an error in getting the data, need to handle it
print("error calling POST on /todos/1")
print(response)
print(response.result.error!)
return
}
when i call the function , it is trying to inserting null values in the database
INSERT INTO `UPC`(`UPC`,`DATE_TIME`) VALUES (NULL,NULL)
Below is the response when i do in postman app.
can someone please help
Firstly in your Postman request you are POSTing your body as x-www-form-urlencoded but in your Swift example you are specifying that header as well. BUT you're actually submitting your POST body as a JSON payload. In contrast, your Postman request is a set of key/value pairs.
Additionally, the two keys appear to be named differently from your Swift example and your Postman example.
Swift uses UPC and DATE_TIME while Postman has upc_app and dttm_app so at a minimum you'll want to ensure you send along what your API expects
I'm trying to get tweets from a specific user using Alamofire and the twitter API. I have successfully made the call with a cURL command, but I can't make it with AlamoFire. It gives a 215 error for Bad Authentication data.
curl --get 'https://api.twitter.com/1.1/statuses/user_timeline.json' --data 'count=2&screen_name=twitterapi' --header 'Authorization: OAuth oauth_consumer_key="lfa3XmdguuAqu3MoEQ6Fo01s0", oauth_nonce="0d50d7938a68be1ed73bcf02d4cc57e8", oauth_signature="8wZF1F%2B70okWLOi4s7dwKlJKc5w%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1460320508", oauth_version="1.0"' --verbose
let key = "lfa3XmdguuAqu3MoEQ6Fo01s0"
let secret = "25EUfqUstgdjZFgYnecrYuPkG6fDkruCjRXr905kM7475W2G63"
let data = ["screen_name": "twitterapi", "count": 2]
let path = "https://api.twitter.com/1.1/statuses/user_timeline.json"
Alamofire.request(.GET, path, parameters: data)
.authenticate(user: key, password: secret)
.responseJSON { response in
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}
How do I give the custom header Authorization in AlamoFire? I think I need an auth token, but I'm not sure where to start getting those. thanks
To give a custom Authorization, you have to do this:
let headers = ["Authorization": "Bearer \(Token.sharedInstance.value!)"]
Alamofire.request(.PUT, url, headers: headers, parameters: data, encoding: .JSON).responseJSON { response in
}
Token Class:
class Token {
var value:String?
static let sharedInstance = Token()
private init() { }
}
I am using Alamofire in swift to send http request/post to server. Below is the code I used in swift.
Alamofire.request(.POST, "http://localhost:8080/hello", headers: [ACCESS_TOKEN:token, "Content-Type":"application/x-www-form-urlencoded" ],
parameters:["friend_id" : friendId, "skill_id" : skillId]).response(completionHandler: {
(request, response, data, error) in
print(request)
print(response)
print(data)
print(error)
})
Below is the code defined in server side:
#POST
#Path("/hello")
#Produces(MediaType.APPLICATION_JSON)
public Response nominateSkill(#Context HttpServletRequest request, #FormParam("friend_id") long friendId, #FormParam("skill_id") int skillId) {
// ...
}
When I run the swift code, I always got below error message in server side:
A servlet request to the URI http://localhost:8080/hello contains form parameters in the request body but the request body has been consumed by the servlet or a servlet filter accessing the request parameters. Only resource methods using #FormParam will work as expected. Resource methods consuming the request body by other means will not work as expected.
I think the problem would be coursed by the swift code which didn't set the parameter correctly. But I don't know how to set them correctly?
I found the solution after some search. I need to add "encoding: .URL" on the request method like below:
Alamofire.request(.POST, "http://localhost:8080/hello", headers: [ACCESS_TOKEN:token, "Content-Type":"application/x-www-form-urlencoded" ],
parameters:["friend_id" : friendId, "skill_id" : skillId],
encoding: .URL).response(completionHandler: {
(request, response, data, error) in
print(request)
print(response)
print(data)
print(error)
})
Your swift code seems fine. Make sure on which side problem is occur. You can try https://chrome.google.com/webstore/detail/postman/fhbjgbiflinjbdggehcddcbncdddomop?hl=en to test the api and make sure api doen't have any issues.
You may also try to change data type of friend_id and skill_id to string in server side and run again.