Need Similar option in Alamofire to setRequestProperty as in HttpURLConnection Android - swift

I am struggling a lot to set Authorisation token as a connection request property in Alamofire Swift.
I tried sending Auth token in headers and was able to achieve that.
In android we set auth token as HttpUrlConnection.setRequestProperty and the APIs are working fine, but in Swift I have to use same APIs but sending auth token in headers is not helping out.
Options I tried:-
Creating MutableURlRequest and setting HTTPAdditionalHeaders.
var defaultHeaders = Alamofire.Manager.sharedInstance.session.configuration.HTTPAdditionalHeaders ?? [:]
let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
configuration.HTTPAdditionalHeaders = defaultHeaders
Please anyone help me sending auth token as a request property rather than a header.
Thanks in advance.

You could send authorization token along with other request's parameters, something like this:
private var parameters: [String: AnyObject]? {
switch self {
case Profile(let token, ...):
return ["authCode" : token, ...]
...
}
}
However it's recommended to be send as header, so just for heads-up, this is how I do it:
import Alamofire
// Helper to build Alamofire Requests
enum APIRouter {
static let baseURL = "http://..."
case Authorize(token: String)
...
}
extension APIRouter: URLRequestConvertible {
var URLRequest: NSMutableURLRequest {
let URLRequest = NSMutableURLRequest(URL: URL)
URLRequest.HTTPMethod = method.rawValue
// Server token is set after authorisation and will be applied to the following requests
if let token = SecureManager.sharedInstance.serverToken {
URLRequest.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
}
let encoding = Alamofire.ParameterEncoding.URL
let request = encoding.encode(URLRequest, parameters: parameters).0
return request
}
private var URL: NSURL {
let baseURL = NSURL(string: APIRouter.baseURL)
return baseURL!.URLByAppendingPathComponent(path)
}
private var path: String {
switch self {
case Authorize:
return "rest/oauth/authorize"
...
}
}
private var method: Alamofire.Method {
switch self {
case Authorize:
return .POST
...
}
}
private var parameters: [String: AnyObject]? {
switch self {
...
}
}
}

Related

AF no member 'responseDecodable' with router

Please help and explain why the router doesn't have responseDecodable. I made a router for AF request call and one of the endpoint need to send up String: [String: Any]. I'm not sure what I did wrong. Thank you!
AFRouter
enum AFRouter: URLRequestConvertible {
case test([String: [String: Any]])
var base: URL {
return URL(string: "https://example.com")!
}
var method: HTTPMethod {
switch self {
case .test:
return .get
}
var path: String {
switch self {
case .test(_):
return "/v2/test"
}
}
func asURLRequest() throws -> URLRequest {
let urlString = baseURL.appendingPathComponent(path).absoluteString.removingPercentEncoding!
let removeSpace = urlString.replacingOccurrences(of: " ", with: "")
let url = URL(string: removeSpace)
var request = URLRequest(url: url!)
request.method = method
switch self {
case .test(_):
guard let token = defaults.string(forKey: "token") else {
return request
}
request.setValue("Bearer " + token , forHTTPHeaderField: "Authorization")
request = try JSONEncoding.default.encode(request)
return request
}
}
Codable
struct Test: Codable {
let success: String
let message: String
let data: [String]
}
Calling API
func getTest(testInfo: [String: Any]) {
AF.request(AFRouter.test(["Testing": testInfo]).responseDecodable(of: Test.self) { response in //got error here "Value of type 'AFRouter' has no member 'responseDecodable'"
//do something...
})
}
The error is saying that you want to use responseDecodable(of:) on a AFRouter instance.
But, in fact, you want to use it on a DataRequest instance.
But it "should work", so are you calling it on a mistaken instance? if we observe, there is a missing ):
AF.request(AFRouter.test(["Testing": testInfo]).responseDecodable(of:...
=>
AF.request(AFRouter.test(["Testing": testInfo])).responseDecodable(of:...

Alamofire request cancelled (-999)

I want to make use of the RequestAdapter and RequestRetrier protocols, so I created my own so called AuthenticationHandler class which implements both protocols. I do this because the refresh token may be expired so this mechanism comes in handy.
The RequestAdapter protocol method adapt does get called, but the should RequestRetrier protocol method does not. I have a separate class that does the actual request:
class TestRequest {
var authHandler: AuthenticationHandler?
func executeRequest() {
// For testing purposes a false access token is passed
self.authHandler = AuthenticationHandler(accessToken: "some_default_token")
let sessionManager = SessionManager()
sessionManager.adapter = authHandler
sessionManager.retrier = authHandler
var loginModel : LoginMessage = LoginMessage.init()
loginModel.username = "someUserName"
loginModel.password = "WrongPassword"
do {
let binaryData = try loginModel.serializedData()
// Create a file with this binary data in order to use it as part of the multipart formdata
guard let fileURL = createFileFrom(data: binaryData) else {
print("Error creating file")
return
}
// Note: custom headers have been set in the AuthenticationHandler
sessionManager.upload(multipartFormData: { multipartFormData in
multipartFormData.append(fileURL, withName: "content")
},
to: K.endpointLogin) { (encodingResult) in
switch encodingResult{
case .success(let upload, _, _):
upload.responseJSON { response in
print("Encoding result success...")
print("Statuscode: \(response.response?.statusCode)")
print(response)
}
case .failure(let encodingError):
print("Failure: \(encodingError)")
}
}
} catch {
print(error)
}
}
I have followed the example in the documentation here
I have read several previous posts saying it has to do with retaining the sessionManager. But I think that is also covered. My authentication handler looks like this:
class AuthenticationHandler: RequestAdapter, RequestRetrier {
private typealias RefreshCompletion = (_ succeeded: Bool, _ accessToken: String?) -> Void
private let sessionManager: SessionManager = {
let configuration = URLSessionConfiguration.default
configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders
return SessionManager(configuration: configuration)
}()
private let lock = NSLock()
private var accessToken: String
private var isRefreshing = false
private var requestsToRetry: [RequestRetryCompletion] = []
init(accessToken: String) {
self.accessToken = accessToken
}
// MARK: - RequestAdapter protocol method
func adapt(_ urlRequest: URLRequest) throws -> URLRequest {
var urlRequest = urlRequest
if let urlString = urlRequest.url?.absoluteString, urlString.hasPrefix(K.SERVER_URL) {
urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
urlRequest.setValue("multipart/form-data", forHTTPHeaderField: "Content-Type")
urlRequest.setValue("Bearer " + accessToken, forHTTPHeaderField: "Authorization")
}
return urlRequest
}
// MARK: - RequestRetrier protocol method
func should(_ manager: SessionManager, retry request: Request, with error: Error, completion: #escaping RequestRetryCompletion) {
lock.lock() ; defer { lock.unlock() }
}
}
My config is as follows:
Alamofire version: 4.7.2
Xcode version: 9.4.1
Swift version: 4
What am I doing wrong here? Why is the request cancelled and is the should method not called?
Any help is greatly appreciated.
Your core issue is that your SessionManager instance is being deinited, which cancels any ongoing tasks. You should keep it around in a singleton or something similar, which will fix the other issue, that of using a new SessionManager for each request, which is an anti pattern.

URLSession dataTask method returns 0 bytes of data

I am using URLSession's dataTask method with a completion handler. The error in response is nil, but the data object returns something, it returns 0 bytes of data.
I was using Alamofire library firstly, I thought there is something wrong with it because I started using newer version so I stated using my own implementation of Alamofire just so I don't have to rewrite all the calls I already have in my app.
It still returns 0 bytes of data.
When I use the same URL in the Playground with a simple URLSession call, it works and returns the data, do you have any idea what might go wrong?
My implementation of Alamofire (Srsly quick and dirty within 30 minutes):
import Foundation
typealias DataResponse = (data: Data?, response: URLResponse?, error: Error?, request: URLRequest?, result: Result)
public class Alamofire: NSObject {
enum Method: String {
case get = "GET"
case post = "POST"
}
fileprivate static let session = URLSession(configuration: URLSessionConfiguration.default)
public struct request {
var request: URLRequest? = nil
var url: URL? = nil
let method: Alamofire.Method?
let parameters: Parameters?
let headers: Headers?
init(_ request: URLRequest, method: Alamofire.Method? = nil, parameters: Parameters? = nil, headers: Headers? = nil) {
self.request = request
self.url = request.url
self.method = method
self.parameters = parameters
self.headers = headers
}
init(_ url: URL, method: Alamofire.Method? = nil, parameters: Parameters? = nil, headers: Headers? = nil) {
self.url = url
self.request = URLRequest(url: url)
self.method = method
self.parameters = parameters
self.headers = headers
}
}
}
typealias Parameters = [String: Any?]
typealias Headers = [String: String]
extension Alamofire.request {
func responseData(completionHandler: #escaping (_ dataResponse: DataResponse) -> Void) {
guard let request = request else { return }
Alamofire.session.dataTask(with: request) { (data, response, error) in
let result: Result
if let error = error {
result = Result.failure(error)
completionHandler((data, response, error, request, result))
return
}
completionHandler((data, response, error, request, Result.success))
}.resume()
}
}
enum Result {
case success
case failure(Error)
}
So the resolution to my problem was a realisation that the data provider cut me off of data :) Can happen too. That's probably not a "solution" but an option you have to consider as I will from now on :D Thank you all
Did you create an App Transport Security Execption for Allows Arbitrary loads in the info.plist?

How to read response cookies using Alamofire

I am trying to read the response cookies for a post request, as done by Postman below
The way I am trying without success right now is
var cfg = NSURLSessionConfiguration.defaultSessionConfiguration()
var cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage()
cfg.HTTPCookieStorage = cookies
cfg.HTTPCookieAcceptPolicy = NSHTTPCookieAcceptPolicy.Always
var mgr = Alamofire.Manager(configuration: cfg)
mgr.request(.POST, "http://example.com/LoginLocalClient", parameters: parameters).responseJSON { response in
print(response.response!.allHeaderFields)
print(NSHTTPCookieStorage.sharedHTTPCookieStorage().cookies)
}
The first print statement contains the 10 header fields without the cookies, the second one contains an empty array.
Any ideas?
You need to extract the cookies from the response using the NSHTTPCookie cookiesWithResponseHeaderFields(_:forURL:) method. Here's a quick example:
func fetchTheCookies() {
let parameters: [String: AnyObject] = [:]
Alamofire.request(.POST, "http://example.com/LoginLocalClient", parameters: parameters).responseJSON { response in
if let
headerFields = response.response?.allHeaderFields as? [String: String],
URL = response.request?.URL
{
let cookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headerFields, forURL: URL)
print(cookies)
}
}
}
Swift 5
func fetchTheCookies() {
let parameters: [String: AnyObject] = [:]
Alamofire.request(.POST, "http://example.com/LoginLocalClient", parameters: parameters).responseJSON { response in
if let headerFields = response.response?.allHeaderFields as? [String: String], let URL = response.request?.url
{
let cookies = HTTPCookie.cookies(withResponseHeaderFields: headerFields, for: URL)
print(cookies)
}
}
}
All the configuration customization you are attempting to do won't have any affect. The values you have set are already all the defaults.
Please be advised that the accepted answer does not work if the cookies are not posted within the header response. Apparently, some cookies are extracted in advance and stored in the shared cookie store and will not appear with the response.
You must use HTTPCookieStorage.shared.cookies instead.
Swift 3:
Alamofire.request(url, method: HTTPMethod.post, parameters: parameters).responseData { (responseObject) -> Void in
if let responseStatus = responseObject.response?.statusCode {
if responseStatus != 200 {
// error
} else {
// view all cookies
print(HTTPCookieStorage.shared.cookies!)
}
}
}
Credit goes to Travis M.
If you just want to read all the cookies against the domain you are interacting with, you can get all cookies with this method.
let cookies = Alamofire.Manager.sharedInstance.session.configuration.HTTPCookieStorage?.cookiesForURL(NSURL(string: "mydomain.com")! )
It returns an optional array of NSHTTPCookie items. (Swift 2.2 and Alamofire 3.4)
Swift 4.1:
let cookies = Alamofire.SessionManager.default.session.configuration.httpCookieStorage?.cookies(for: url)
the above code was correct I have used in this way -
var allCookies: [NSHTTPCookie]?
if let headerFields = aResponse.response?.allHeaderFields as? [String: String],
URL = aResponse.request?.URL {
allCookies = NSHTTPCookie.cookiesWithResponseHeaderFields(headerFields, forURL: URL)
for cookie in allCookies! {
print(cookie)
let name = cookie.name
if name == "nmSession" {
let value = cookie.value
print(value)
}
}
}
#lespommes
This is the only way I have received cookies. Now I can finally see Set-Cookie in a response:
let parameters = ["postLogin": ["login": "mymail#gmail.com", "password": "myPassword"]]
let url = NSURL(string: "your-website-with-cookies.com")
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(parameters, options: [])
Alamofire.request(request)
.responseJSON { response in
debugPrint(response)
if response.result.isSuccess {
...
}
}else if (response.result.isFailure){
...
}
}

Prevent redirect response with Alamofire in Swift

I'm looking for example code how to prevent redirect response (status code 3xx) when request web api. I'm using Swift with Alamofire 1.2.
I have tried:
delegate.taskWillPerformHTTPRedirection = { (session: NSURLSession!, task: NSURLSessionTask!, response: NSHTTPURLResponse!, request: NSURLRequest!) in
return nil
}
but not work
I've also tried: https://github.com/Alamofire/Alamofire/pull/350/files and have changed my own code to:
var acc = self.txtAccount.text
var pwd = self.txtPassword.text
var url : String = "http://10.1.0.2:8081/wordpress/wp-json/users/me"
let delegate = Alamofire.Manager.sharedInstance.delegate
delegate.taskWillPerformHTTPRedirection = { (session: NSURLSession!, task: NSURLSessionTask!, response: NSHTTPURLResponse!, request: NSURLRequest!) in
var request = NSMutableURLRequest(URL: NSURL(string: url)!)
request.HTTPMethod = "GET"
var credential = "\(acc):\(pwd)"
var authData = credential.dataUsingEncoding(NSUTF8StringEncoding)
var encodedAuthData = authData?.base64EncodedStringWithOptions(nil)
var authValue = "Basic \(encodedAuthData!)"
request.setValue(authValue, forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("application/json", forHTTPHeaderField: "Accept")
return request
}
//I've implemented URLRequestConvertible 'Router'. it also have call the same above url
Alamofire.request(Router.Authorize(acc, pwd))
.response({(request, response, data, error) in
println(request)
})
But it's not worked and seem like turned to infinite loop. I tested on Charles.
Alternative (code snippet) solution using AlamoFire 2.4 (Xcode7). In my case, I always expect a redirect. (I am unpacking a shortened link.) If the completion in the request.response call runs, that is an error to me.
func printRedirectUrl() {
// taskWillPerformHTTPRedirectionWithCompletion: ((NSURLSession, NSURLSessionTask, NSHTTPURLResponse, NSURLRequest, NSURLRequest? -> Void) -> Void)?
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirectionWithCompletion = { session, task, response, request, completion in
// request.URL has the redirected URL inside of it, no need to parse the body
print("REDIRECT Request: \(request)")
if let url = request.URL {
print("Extracted URL: \(url)")
}
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = nil // Restore redirect abilities
return
}
// We expect a redirect, so the completion of this call should never execute
let url = NSURL(string: "https://google.com")
let request = Alamofire.request(.GET, url!)
request.response { request, response, data, error in
print("Logic Error, response should NOT have been called for request: \(request)")
Alamofire.Manager.sharedInstance.delegate.taskWillPerformHTTPRedirection = nil // Restore redirect abilities - just in case
}
}
REDIRECT Request: { URL: https://www.google.com/ }
Extracted URL: https://www.google.com/
In Swift 4,
let delegate = Alamofire.SessionManager.default.delegate
delegate.taskWillPerformHTTPRedirection = { (session, task, response, request) -> URLRequest? in
// print("REDIRECT Request: \(request)")
return nil
}
Hello its actually pretty simple
Alamofire has a redirector that will
Example
let request = AF.request("https://google.com",method: .post,parameters: parameters)
.cURLDescription { description in
debugPrint(description)
}
let redirector = Redirector(behavior: .doNotFollow)
request.redirect(using: redirector)
with that it wont redirect
its also in the docs in the advanced usage section
It looks like returning nil can possibly cause a deadlock. Instead, try to create a new NSURLRequest with the same original URL. See #jhersh's notes in a previous Alamofire PR along with the comments and implementation in his tests.
How to Stop a Redirect
func disallowRedirect() {
let URL = "http://google.com/"
let delegate = Alamofire.Manager.sharedInstance.delegate
delegate.taskWillPerformHTTPRedirection = { session, task, response, request in
return NSURLRequest(URL: NSURL(string: URL)!)
}
let request = Alamofire.request(.GET, URL)
request.response { request, response, data, error in
println("Request: \(request)")
println("Response: \(response)")
println("Data: \(NSString(data: data as! NSData, encoding: NSUTF8StringEncoding))")
println("Error: \(error)")
}
}
disallowRedirect()
The fact that you cannot pass nil into the NSURLSessionTaskDelegate method's completionHandler looks like a bug. I'm going to file a radar for this and I'll post a link to the bug report once I'm finished.
I don't know if your version of Alamofire has a support for public delegate. Last time I checked delegate was private. I am using the changes made by #jhersh. You can check his additions and how to use delegate by followin github pr. https://github.com/Alamofire/Alamofire/issues/314