send alamofire request in background, when view controller should be dissmissed - swift

I have the function, that should send video position time, when user closes avplayercontroller, here is it:
let concurrentQueue = DispatchQueue(label: "networkRequest", attributes: .concurrent)
fileprivate func sendBackgroundRequest(urlString: String, method: HTTPMethod, parameters: Parameters?) {
let headers = createHeader()
print(headers)
if networkManager!.isReachable {
concurrentQueue.async {
let request = Alamofire.request(urlString, method: method, parameters: parameters, encoding: JSONEncoding.default, headers: headers)
request.responseJSON { data in
if data.result.error == nil {
if data.response?.statusCode == 200 {
print("Success")
} else {
print(data.response?.statusCode)
}
} else {
print(data.result.error)
}
}
}
}
}
func sendSeriesDurationTime(provider: String, fileID: String, timePosition: Int) {
let parameters: Parameters = [
"time_position": timePosition
]
let dataUrl = "\(BASE_URL)\(PLAYED_DURATION_SLUG)\(provider)/\(fileID)"
print(dataUrl)
sendBackgroundRequest(urlString: dataUrl, method: .post, parameters: parameters)
}
I call it in viewWillDisappear method. But when I click "done" button of player, it takes some time, before it dissappears, because system waits before alamofire gets response to completion.
I know, that I can call it in viewDidDisappear method, but this way my UI freezes in another controller....

I found the answer, just needed to delete
request.responseJSON { data in
if data.result.error == nil {
if data.response?.statusCode == 200 {
print("Success")
} else {
print(data.response?.statusCode)
}
} else {
print(data.result.error)
}
}
so request made, but UI is not freezing, and system is not waiting server response.

Related

Swift Alamofire Async Issue

new to swift here.
I'm trying to make an AF.request call inside another AF.request call and everything works fine.
The issue is that the fetchAllUsers() gets called after everything loads up. So instead of getting all the users right away, I have to refresh the page in order to get the fetchAllUsers() to execute.
I thought using closures would avoid this problem but it's still occurring.
Am I doing something wrong?
func fetchAllUsers(completionHandler: #escaping ([User]) -> Void) {
let allUsersUrl = baseUrl + "users/"
let headers: HTTPHeaders = [
.authorization(bearerToken: NetworkManager.authToken)
]
if let url = URL(string: allUsersUrl) {
AF.request(url, headers: headers).responseDecodable(of: [User].self) { response in
if let users = response.value {
completionHandler(users)
} else {
print("Users is empty")
}
}
} else {
print("URL error")
}
}
func login(param: [String:String], completionHandler: #escaping (Int) -> Void) {
let loginUrl = baseUrl + "auth/login"
// Sets the Logged In User
AF.request(loginUrl, method: .post, parameters: param, encoding: JSONEncoding.default, headers: nil).responseString { response in
if let data = response.data {
let authTokenString = String(decoding: data, as:UTF8.self).components(separatedBy: " ")
// Sets the authentication token
NetworkManager.authToken = authTokenString[1]
self.fetchAllUsers { users in
for user in users {
if param["username"] == user.username {
HomeViewModel.loggedInUser = user
}
}
}
completionHandler(response.response!.statusCode)
}
}
}
Pass completion handler of main function and status code to fetchAllUsers and call it there after it's own completion handler
completionHandler(response.response!.statusCode) was being executed before self.fetchAllUsers closure because it was waiting for api response to complete. completionHandler(response.response!.statusCode) was destroying self.fetchAllUsers closure before it is executed, so I called completionHandler(response.response!.statusCode) inside self.fetchAllUsers after it's closure
completionHandler(users)
mainCompletion(statusCode)
Complete code works following
func fetchAllUsers(statusCode:Int, mainCompletion: #escaping (Int) -> Void, completionHandler: #escaping ([User]) -> Void) {
let allUsersUrl = baseUrl + "users/"
let headers: HTTPHeaders = [
.authorization(bearerToken: NetworkManager.authToken)
]
if let url = URL(string: allUsersUrl) {
AF.request(url, headers: headers).responseDecodable(of: [User].self) { response in
if let users = response.value {
completionHandler(users)
mainCompletion(statusCode)
} else {
print("Users is empty")
}
}
} else {
print("URL error")
}
}
func login(param: [String:String], completionHandler: #escaping (Int) -> Void) {
let loginUrl = baseUrl + "auth/login"
// Sets the Logged In User
AF.request(loginUrl, method: .post, parameters: param, encoding: JSONEncoding.default, headers: nil).responseString { response in
if let data = response.data {
let authTokenString = String(decoding: data, as:UTF8.self).components(separatedBy: " ")
// Sets the authentication token
NetworkManager.authToken = authTokenString[1]
self.fetchAllUsers(statusCode: response.response!.statusCode, mainCompletion: completionHandler) { users in
for user in users {
if param["username"] == user.username {
HomeViewModel.loggedInUser = user
}
}
}
}
}
}

To cancle any api request Using Alamofire in swift

I am calling api using alamofire in swift.it is a search api, when I am searching any text in searchbar I am calling the api. I am calling the api after a delay of 0.75 seconds. I want to cancel the previous request, when new request is there. But I don’t know how to cancel the request. Can anyone help me?
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
NSObject.cancelPreviousPerformRequests(withTarget: self, selector: #selector(self.reload(_:)), object: searchBar)
perform(#selector(self.reload(_:)), with: searchBar, afterDelay: 0.75)
}
#objc func reload(_ searchBar: UISearchBar) {
guard let query = searchBar.text, query.trimmingCharacters(in: .whitespaces) != "" else {
self.searchLcodeWithText("")
print("nothing to search")
return
}
self.searchLcodeWithText(query)
print(query)
}
private func searchLcodeWithText(_ newText: String){
//Show
startActivityIndicator()
apiService.searchlcode(searchText: newText) { [self] (lcodeData) in
allLcodeArray = []
//Hide
if let lcde = lcodeData{
if lcde.count > 0{
allLcodeArray.append(contentsOf: lcde)
stopActivityIndicator()
}
}
else{
}
stopActivityIndicator()
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
import Foundation
import Alamofire
class ApiService{
func searchlcode(searchText: String, completion: #escaping searchLcodeTypeAliasCompletion){
guard let urlrequest = URL(string: SEARCH_URL) else {return}
var parameters = [String:Any]()
parameters.updateValue(searchText, forKey: "lcode_name")
Alamofire.request(urlrequest, method: .post, parameters: parameters, encoding: URLEncoding.default, headers: nil).responseJSON { [self] (response : DataResponse<Any>) in
if let error = response.result.error {
debugPrint(error.localizedDescription)
return
}else{
guard let data = response.data else { return completion(nil)}
let jsonDecoder = JSONDecoder()
do {
let searchUser = try jsonDecoder.decode(searchLcodeTypeAlias.self, from: data)
print(searchUser)
completion(searchUser)
} catch {
debugPrint(error)
completion(nil)
}
}
}
}
}
Return DataRequest in function then you can cancel specific request.
for Example.
#discardableResult
func login(params: loginRequestModel, completion: #escaping (RequestResult<UserDataResponse, String>) -> Void) -> DataRequest {
params.printJson()
let url = baseUrl + endPoints.login.rawValue
let request = sessionManager.request(url,method: .post, parameters: params, headers: headers)
request.responseDecodable(of: UserDataResponse.self){ (response) in
completion(self.getValidResponse(response))
}
return request
}
usage in viewController
step1 declaration: var loginDataRequest: DataRequest?
step2: initialise data request in function call
step3: action event cancel this request
loginDataRequest.cancel
another approach
you can achieve by using dispatch work Item or operation Queue

Call request by request Alamofire swift

I wonder how I could make the request executed one by one. It's about image processing. And the server cannot process more than one image. The problem is that I need to send 10 pictures at a time, and if I send everything at once, I have a problem with the timeout, and now I'm interested in how to send the next request when response from the previous one arrives?
func clearingImage() {
if clearingImageArray.count < 0 || indexOfClearImage >= clearingImageArray.count
{
guard let imageName = ImageFilenameUtilities.getNameOfImageFromPath(filePath: clearingImageArray[indexOfClearImage]) else
{
indexOfClearImage+=1
clearingImage()
return
}
guard let clearType = ImageFilenameUtilities.getClearTypeFromFilename(filename: imageName) else
{
indexOfClearImage+=1
clearingImage()
return
}
guard let image = gallery?.getImageForPath(path: clearingImageArray[indexOfClearImage]) else
{
indexOfClearImage+=1
clearingImage()
return
}
sendImageToServer(image: image, imageName: imageName)
}
}
func sendImageToServer(image:UIImage,imageName:String)
{
let url = "example.com"
let headers : HTTPHeaders = [
"Connection": "Keep-Alive"
// "Content-Type": "application/x-www-form-urlencoded"
]
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 12000
manager.session.configuration.timeoutIntervalForResource = 12000
manager.upload(multipartFormData: { multipleData in
for (key, value) in parameter {
multipleData.append(value.data(using: String.Encoding.utf8)!, withName: key)
}
multipleData.append(
imageData, withName: "image", fileName: imageName, mimeType: "image/jpg")}, to: url, method: .post, headers: headers){
(result) in
switch result {
case .success(let upload, _, _):
upload.responseJSON { response in
self.indexOfClearImage += 1
if let image = UIImage(data: response.data!) {
//save image to galery
}
else
{
if let error = response.result.error {
if error._code == NSURLErrorTimedOut {
self.indexOfClearImage -= 1
}
}
}
}
case .failure(let encodingError):
//send next image to server
self.indexOfClearImage += 1
}
}
self.clearingImage()
}
Just send the 2nd request on the 1st completion block, when you get the response, and so on.
func firstRequest() {
AF.request("url/here").response { response in
self.secondRequest()
}
}
func secondRequest() {
AF.request("url/here").response { response in
// call 3rd request and so on...
}
}

How to get notified after looping through Alamofire requests using DispatchQueue in Swift?

I have this code to get a list of ids and names that are parsed from a JSON through an iteration of calls.
Problem is I don't know how to get notified, a simples print("finished"), would do. I tried to use print command after the 'for' loop but it also iterates.
Anyone with any idea?
Here's the code:
override func viewDidLoad() {
super.viewDidLoad()
//Manager
let manager = SessionManager.default.startRequestsImmediately = false
//País
let paisRequest = Alamofire.request(self.cadastro_pais_url, method: .post, parameters: self.cadastro_pais_params).responseString { response in
do { } catch { print("error") }
}
for i in 0...2000 {
DispatchQueue.main.async {
let patrocinadorRequest = Alamofire.request(self.buscaPatrocinador, method: .post, parameters: ["patrocinador":"\(i)"]).responseJSON { (responseData) -> Void in
if((responseData.result.value) != nil) {
let swiftyJsonVar = JSON(responseData.result.value!)
if !(swiftyJsonVar["integracao"] == JSON.null){
print("\(swiftyJsonVar["integracao"]),\(swiftyJsonVar["nome"]),")
} else {}
} else {
print("Error")
}
}
//Requests Chain
let chain = RequestChain(requests: [paisRequest, patrocinadorRequest])
chain.start { (done, error) in
}
}
}
}
The network request should not be done on the main thread, but instead on the background one, sync or async. The main thread is reserved only for the UI stuff, except if you want to force blocking the User interface.
You can use Dispatch Group and DispatchQueue to organise you code and notification after completion. The same result could be achieved with the Semaphore...
Sample:
let dispatchGroup = DispatchGroup()
// change the quality of service based on your needs
let queue = DispatchQueue(label: "com.stackoverflow", qos: .background)
for i in 0...2000 {
dispatchGroup.enter()
// Perform on background thread, async
queue.async {
Alamofire.request { response in
dispatchGroup.leave()
// ...
}
}
}
dispatchGroup.notify(queue: .main, execute: {
print("DONE WITH ALL REQUESTS")
})
Hope it helps.

swift, alamofire cancel previous request

I have an NetworkRequest class, where all my alamofire requests 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...
Pls, help
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()
}
}
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() }
}
}
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.
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()
}
}
})