48

Is it possible to add timeout handler for Alamofire request?

In my project I use Alamofire this way:

init() {
    let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
    configuration.timeoutIntervalForRequest = 30

    self.alamofireManager = Alamofire.Manager(configuration: configuration)
}

func requestAuthorizationWithEmail(email:NSString, password:NSString, completion: (result: RequestResult) -> Void) {

    self.alamofireManager!.request(.POST, "myURL", parameters:["email": email, "password":password])
        .responseJSON { response in
            switch response.result {
            case .Success(let JSON):
                //do json stuff
            case .Failure(let error):
                print("\n\nAuth request failed with error:\n \(error)")
                completion(result: .ConnectionFailed)
            }
    }
}

EDIT:

request fail message

Error Domain=NSURLErrorDomain Code=-1001 "The request timed out." UserInfo={NSUnderlyingError=0x7fc10b937320 {Error Domain=kCFErrorDomainCFNetwork Code=-1001 "(null)" UserInfo={_kCFStreamErrorCodeKey=-2102, _kCFStreamErrorDomainKey=4}}, NSErrorFailingURLStringKey=url, NSErrorFailingURLKey=url, _kCFStreamErrorDomainKey=4, _kCFStreamErrorCodeKey=-2102, NSLocalizedDescription=The request timed out.}

IgorNikolaev
  • 1,053
  • 1
  • 12
  • 28
  • Doesn't the timeout also trigger the `.Failure`? Never tested it with Alamofire, but most other systems I use fallback to the error/failure like that. What have you tested? –  Apr 14 '16 at 14:25
  • @Allendar you're right, my fault that i have not mentioned that. I've edited my question. – IgorNikolaev Apr 14 '16 at 14:30
  • 2
    The response object will contain the HTTP status. If it is 408 (408 Request Timeout), then you can check that inside the `.Failure` call and handle it appropriately. There are probably even macros for the http-statuses so you can simply check something like `HTTP_STATUS_408` as an integer placeholder. –  Apr 14 '16 at 14:32

11 Answers11

99

You can compare error._code and if it is equal to -1001 which is NSURLErrorTimedOut then you know this was a timeout.

let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 120

manager.request("yourUrl", method: .post, parameters: ["parameterKey": "value"])
        .responseJSON {
            response in
            switch (response.result) {
            case .success: // succes path 
            case .failure(let error):
                if error._code == NSURLErrorTimedOut {
                    print("Request timeout!")
                }
            }
        }
kamwysoc
  • 6,709
  • 2
  • 34
  • 48
  • 3
    This is true, but not promoted to do so, because if you work in teams these weird checks on exact "under-water" codes will make code very unreadable. Nonetheless a correct answer tho, so +1. –  Apr 14 '16 at 14:36
  • 1
    Totally agree with you, I've made some update and make this snippet more human readable :) – kamwysoc Apr 14 '16 at 14:38
  • 1
    I've made some research and we can use `NSURLErrorTimedOut` from `NSURLError` class. – kamwysoc Apr 14 '16 at 14:43
  • 1
    I use this snippet to handle timeout error. My platform is Xcode 8, Swift 3. the enum result value seems to be `.success` and `.failure` instead of `.Success` and `.Failure`. – Veck Hsiao Sep 26 '16 at 13:45
  • 2
    can anyone tell me how does one know to use `error._code`? This does not seem to appear in any documentation – daisura99 May 02 '17 at 08:22
21

Swift 3

The accepted answer didn't work for me.

After a lot of research, I did it like this:

let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 120

manager.request("yourUrl", method: .post, parameters: ["parameterKey": "value"])
Shark Lasers
  • 441
  • 6
  • 15
Anil Kukadeja
  • 1,348
  • 1
  • 14
  • 27
13

Swift 3, Alamofire 4.5.0

I wanted to set the same timeout for every HTTP call in my project.

The key idea is to declare the Alamofire Session Manager as a global variable. Then to create a URLSessionConfiguration variable, set its timeout in seconds and assign it to the manager.

Every call in the project can use this configured session manager.

In my case the global Alamofire Session Manager variable was set in AppDelegate file (globally) and its configuration was managed in its didFinishLaunchingWithOptions method

AppDelegate.swift

import UIKit

var AFManager = SessionManager()

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {

        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 4 // seconds
        configuration.timeoutIntervalForResource = 4 //seconds
        AFManager = Alamofire.SessionManager(configuration: configuration)

        return true
    }
    ...
}

From now the Alamofire request function can be called from any part of the app using the afManager.

For example:

AFManager.request("yourURL", method: .post, parameters: parameters, encoding: JSONEncoding.default).validate().responseJSON { response in
    ...
}
Community
  • 1
  • 1
ivoroto
  • 925
  • 12
  • 12
5

Swift 3.x

class NetworkHelper {
    static let shared = NetworkHelper()
    var manager: SessionManager {
        let manager = Alamofire.SessionManager.default
        manager.session.configuration.timeoutIntervalForRequest = 10
        return manager
    }
    func postJSONData( withParams parameters: Dictionary<String, Any>, toUrl urlString: String, completion: @escaping (_ error: Error,_ responseBody: Dictionary<String, AnyObject>?)->()) {
        manager.request(urlString, method: .post, parameters: parameters, encoding: JSONEncoding.default).responseJSON { response in 
            if let error = response.result.error {
                if error._code == NSURLErrorTimedOut {
                    print("Time out occurs!")
                }
            }
        }
    }
}
Gurjit Singh
  • 1,723
  • 21
  • 27
5

Swift 5, Alamofire 5

The cleanest way I found, that works with the latest version of Alamofire is the following:

AF.request(url).response { (dataResponse: AFDataResponse<Data?>) in
    switch dataResponse.result {
    case .success(let data):
        // succes path
    case .failure(let error):
        switch error {
        case .sessionTaskFailed(URLError.timedOut):
            print("Request timeout!")
        default:
            print("Other error!")
        }
    }
}
gcharita
  • 7,729
  • 3
  • 20
  • 37
2

Swift 3.x

Accepted answer didn't worked for me too.

This work for me!

let url = URL(string: "yourStringUrl")!
var urlRequest = URLRequest(url: url)
urlRequest.timeoutInterval = 5 // or what you want

And after:

Alamofire.request(urlRequest).response(completionHandler: { (response) in
    /// code here
}
ventuz
  • 1,121
  • 1
  • 7
  • 9
2

Swift 4

This my way and timeout feature is workable, meanwhile practices singleton for api class. reference from here

struct AlamofireManager {
    static let shared: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = 5
        let sessionManager = Alamofire.SessionManager(configuration: configuration, delegate: SessionDelegate(), serverTrustPolicyManager: nil)
        return sessionManager
    }()
}

class Auth {
    static let api = Auth()

    private init() {}

    func headers() -> HTTPHeaders {
        return [
            "Accept": "XXX",
            "Authorization": "XXX",
            "Content-Type": "XXX"
        ]
    }

    func querySample() {

        AlamofireManager.shared.request("api_post_url", method: .post, parameters: ["parametersKey": "value"], encoding: JSONEncoding.default, headers: headers())
            .responseJSON(queue: DispatchQueue.global(), options: []) { (response) in
            switch response.result {
            case .success(let value):
                // do your statement
            case .failure(let error):
                if error._code == NSURLErrorTimedOut {
                    // timeout error statement
                } else {
                    // other error statement
                }
            }
        })
    }

    func queryOtherSample() {

        AlamofireManager.shared.request("api_get_url", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers())
            .responseJSON(queue: DispatchQueue.global(), options: []) { (response) in
            switch response.result {
            case .success(let value):
                // do your statement
            case .failure(let error):
                if error._code == NSURLErrorTimedOut {
                    // timeout error statement
                } else {
                    // other error statement
                }
            }
        })
    }

}
Joshpy
  • 560
  • 8
  • 21
  • this throws `Error Domain=NSURLErrorDomain Code=-999 "cancelled"` – Siempay Mar 22 '19 at 09:13
  • @brahimm pls try to improve the thread queue use the DispatchQueue.global() as my update – Joshpy Mar 24 '19 at 15:05
  • can you tell me why specifying the background thread there solves this problem ! – Siempay Mar 24 '19 at 19:51
  • I think this error due to multiple request to query on serial queue at same time. improved the DispatchQueue.global() to concurrent queue. – Joshpy Mar 25 '19 at 03:03
  • the problem is still there – Siempay Mar 26 '19 at 15:36
  • @brahimm could you detail the reproduced procedure, I cannot replicate – Joshpy Mar 27 '19 at 15:17
  • I did the same like you said but I keep getting canceled, in the end I just created a `URLRequest` and passed it to `Alamofire.request` and solved the problem – Siempay Mar 27 '19 at 15:37
  • @brahimm I saw your issue, meanwhile find solution from [here](https://stackoverflow.com/questions/41803856/set-timeout-in-alamofire/44298030#44298030). – Joshpy Mar 31 '19 at 18:09
1

For Swift 3.x / Swift 4.0 / Swift 5.0 users with Alamofire >= 5.0

Used request modifier to increase and decrease the timeout interval.

Alamofire's request creation methods offer the most common parameters for customization but sometimes those just aren't enough. The URLRequests created from the passed values can be modified by using a RequestModifier closure when creating requests. For example, to set the URLRequest's timeoutInterval to 120 seconds, modify the request in the closure.

var manager = Session.default
 manager.request(urlString, method: method, parameters: dict, headers: headers, requestModifier: { $0.timeoutInterval = 120 }).validate().responseJSON { response in

OR

RequestModifiers also work with trailing closure syntax.

var manager = Session.default
     manager.request("https://httpbin.org/get") { urlRequest in
    urlRequest.timeoutInterval = 60
    urlRequest.allowsConstrainedNetworkAccess = false
}
.response(...)

You can also check it here

0

Make extension of SessionManager and write a public static variable like this, "requestTimeOutInterval" this is a public variable. it has time.

extension SessionManager {
    public static let custom: SessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.timeoutIntervalForRequest = requestTimeOutInterval
        configuration.httpAdditionalHeaders = SessionManager.defaultHTTPHeaders

        return SessionManager(configuration: configuration)
    }()
}
Waqas Ali
  • 51
  • 5
0

Swift 5.0, Alamofire 5.4.2

The error code when time out always equal to NSURLErrorTimedOut, so I try to retrieve Error object from AFError and upcast to NSError.

extension AFError {
    var isTimeout: Bool {
        if isSessionTaskError,
           let error = underlyingError as NSError?,
           error.code == NSURLErrorTimedOut //-1001
        {
            return true
        }
        return false
    }
}

Invoke on response closure.

let request = URLRequest(url: URL(string: "https://httpbin.org/delay/10")!, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 2)
AF.request(request).responseString(completionHandler: { response in
    switch response.result {
    case .success(_):
        print("success")
    case .failure(let error):
        if error.isTimeout {
            print("Timeout!")
        }
    }
})
Ben K.
  • 125
  • 2
  • 5
0

In Alamofire 5.5 SessionManager has been renamed Session

Here is the documentation link https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%205.0%20Migration%20Guide.md#breaking-api-changes

Also the example of user

let manager = Alamofire.Session.default
        manager.session.configuration.timeoutIntervalForRequest = 15
        let headers: HTTPHeaders? = token == nil ? nil : [.authorization(bearerToken: token!),.accept("application/json")]
        manager.request(path, method: method, parameters: parameter, headers: headers).responseJSON { (response) in
            switch response.result {
            case .success:

            case .failure:

            }
        }
Md. Shofiulla
  • 2,135
  • 1
  • 13
  • 19