Ruslan Sabirov
Ruslan Sabirov

Reputation: 437

How to cancel previous Alamofire request?

I have an NetworkRequest class, where all my Alamofire requests are made:

class NetworkRequest {
    static let request = NetworkRequest()
    
    var currentRequest: Alamofire.Request?
        
    let dataManager = DataManager()
    let networkManager = NetworkReachabilityManager()
    let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
    
    func downloadData<T: Film>(slug: String, provider: String, section: String, dynamic: String, anyClass: T, completion: ([T]?) -> Void ) {
        var token: String = ""
        
        if LOGGED_IN == true {
            token = "\(NSUserDefaults.standardUserDefaults().valueForKey(TOKEN)!)"
        }
        
        let headers = [
            "Access": "application/json",
            "Authorization": "Bearer \(token)"
        ]
                
        let dataUrl = "\(BASE_URL)\(slug)\(provider)\(section)\(dynamic)"
        print(headers)
        print(dataUrl)
        
        if networkManager!.isReachable {
            
            currentRequest?.cancel()
            
            dispatch_async(dispatch_get_global_queue(priority, 0)) {
            
                if let url = NSURL(string: dataUrl) {
                    let request = Alamofire.request(.GET, url, headers: headers)
                    
                    request.validate().responseJSON { response in
                        switch response.result {
                        case .Success:
                            if let data = response.result.value as! [String: AnyObject]! {
                                let receivedData = self.dataManager.parseDataToFilms(data, someClass: anyClass)
                                completion(receivedData)
                            }
                            
                        case .Failure(let error):
                            print("Alamofire error: \(error)")
                            
                            if error.code == 1001 {
                                self.goToNoConnectionVC()
                            }
                            
                            print("canceled")
                        }
                        
                    }
                }
            }
        } else {
            goToNoConnectionVC()
        }
    }
}

And I need to cancel previous downloadData request, when the new one starts, tried to cancel using currentRequest?.cancel(), but it doesn't help.

Already tried to cancelOperations using NSOperationsBlock, but it doesn't cancels current operation.

I block UI now, so that user can't send another request. But this is not correct, causes some errors later.

Upvotes: 11

Views: 15793

Answers (5)

Coder ACJHP
Coder ACJHP

Reputation: 2194

For who using AF.request AF.Session.getTasksWithCompletionHandler or getAllTasks(completionHandler: <#T##([URLSessionTask]) -> Void#>) always returns empty so i found a solution to cancel previous task by storing previous task instance and then checking if it's retrying to same request, if it's contains same url cancelling stored DataRequest by calling DataRequestInstance.cancel()

For example:

private var isRetrying = false
private var latestDataRequest: DataRequest?

typealias ServiceCompletionHandler = (Swift.Result<ExampleResponse, Error>) -> ()

func requestTokenDataWith(completionHandler: @escaping ServiceCompletionHandler) {
            
    let url = // Your service url
    let parameters = // Required parameters
    
    // Cancel first request if isn't response until now
    if isRetrying, let latestDataRequest {
        
        // Find request task that contains exact same url with new request
        // Or you can directly cancel old DataRequest without looking for url
        let sameOldRequests = latestDataRequest.tasks.compactMap({ $0.originalRequest?.url }).filter({ $0 == url })
        if sameOldRequests.isEmpty == false {
            
            // Cancel first task and release it's instance
            latestDataRequest.cancel()
            self.latestDataRequest = nil
            
            // Log error
            let error = NetworkError(
                domain: Constants.bundleId(),
                code: -1,
                type: .stillWaitingForResponse
            )
            Logger.shared.info(from: "TokenService", message: error.localizedDescription)
        }
    }
    
    // No requests already in, mark as true to block coming requests
    isRetrying = true
    
    latestDataRequest = AF.request(
        url,
        method: .post,
        parameters: params,
        encoding: URLEncoding.httpBody,
        interceptor: self
    )
    .validate(contentType: ["application/json"])
    .validate(statusCode: 200..<300)
    .responseJSON(completionHandler: { response in
        // Stop blocking incoming requests
        self?.isRetrying = false
        completionHandler(response)
    }
}

Upvotes: 0

Ruslan Sabirov
Ruslan Sabirov

Reputation: 437

Found needed solution:

func stopAllSessions() {
    Alamofire.Manager.sharedInstance.session.getAllTasksWithCompletionHandler { tasks in
        tasks.forEach { $0.cancel() }
    }
}

Update for Alamofire 5

func stopAllSessions() {
    AF.session.getTasksWithCompletionHandler { (sessionDataTask, uploadData, downloadData) in
        sessionDataTask.forEach { $0.cancel() }
        uploadData.forEach { $0.cancel() }
        downloadData.forEach { $0.cancel() }
    }
}

Upvotes: 2

babatunde adewole
babatunde adewole

Reputation: 493

Swift 5

To cancel all requests use

Alamofire.Session.default.session.getTasksWithCompletionHandler({ dataTasks, uploadTasks, downloadTasks in
        dataTasks.forEach { $0.cancel() }
        uploadTasks.forEach { $0.cancel() }
        downloadTasks.forEach { $0.cancel() }
    })

To cancel a request with a particular url use

Alamofire.Session.default.session.getTasksWithCompletionHandler({ dataTasks, uploadTasks, downloadTasks in
    dataTasks.forEach {
            if ($0.originalRequest?.url?.absoluteString == "www.google.com") {
                $0.cancel()
            }
        
    }
    uploadTasks.forEach {
        if ($0.originalRequest?.url?.absoluteString == "www.google.com") {
            $0.cancel()
        }
    }
    downloadTasks.forEach {
        if ($0.originalRequest?.url?.absoluteString == "www.google.com") {
            $0.cancel()
        }
        
    }
})

Upvotes: 1

Constantin Saulenco
Constantin Saulenco

Reputation: 2383

Now on Alamofire 4 the Alamofire.Manager.sharedInstance.session is not available you should use this solution:

let sessionManager = Alamofire.SessionManager.default 
sessionManager.session.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in 
dataTasks.forEach { $0.cancel() } 
uploadTasks.forEach { $0.cancel() } 
downloadTasks.forEach { $0.cancel() } 
}

and if you want to cancel (suspend, resume) a particular request you can check the request url in your .forEach block like this:

dataTasks.forEach {
                if ($0.originalRequest?.url?.absoluteString == url) {
                    $0.cancel()
                }
            }

Upvotes: 23

ciccioska
ciccioska

Reputation: 1291

If you want to cancel the request, you need to trace the requests made and try to cancel it. You can store it in an array and cancel every previous request stored. In your code you create a request: let request = Alamofire.request(.GET, url, headers: headers) but you try to cancel the currentRequest?.cancel() that is never valued.

Upvotes: 0

Related Questions