How to go back to DispatchQueue.main from URLSession.shared.dataTask (macOS framework) - swift

I'm building a macOS framework and at some point, I need to make a request to some API
When I got the response I want to update the UI. I'm using URLSession.shared.dataTask to make the call and as I know the call is made in the background thread
For some reason when I try to go back to the main thread nothing happens
I'm using a virtual machine to run my framework
Any help?
Thanks
Here how I doing the request:
URLSession.shared.dataTask(with: request) { data, response, error in
if error != nil {
DispatchQueue.main.async {
//Display error message on the UI
//This never happens
//Never go back to the main thread
//Framework stop working
}
}
}.resume()

Are you sure that your task is called?
let dataTask = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
....
DispatchQueue.main.async {
completion(nil, nil)
}
}
dataTask.resume() // You should add this.

Related

How to handle URLSessiosn UploadTask ResumeData when delegate method got fired?

I was implementing an upload task using URLSession Uploadtask with the below code:
lazy var urlSession = URLSession(
configuration: .background(withIdentifier: "com.test.xxxxx"),
delegate: self,
delegateQueue: .main
)
var uploadTask = URLSessionUploadTask()
/// Calling uploadtask using 'fileURL' of the asset
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "PUT"
uploadTask = urlSession.uploadTask(with: request, fromFile: fileURL)
uploadTask.resume()
And uploading works as expected, my concern is if I want to use resume data whenever user removes the app from multitask window or any error happens in between uploading a file, how can i achieve it using below delegate method, this delegate method is firing for me, but we don't have any methods to use resume data like func downloadTask(withResumeData resumeData: Data) -> URLSessionDownloadTask for upload task or is it not possible for upload task, please guide me on this. Thank you.
func urlSession(_: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
// Here comes when background upload failed with errors
// Such as app closed from the tray ,switched off ,crash occured, etc.
// Then, handle with resumeData
os_log("Download error: %#", type: .error, String(describing: error))
} else {
// Here comes when background upload completed with no error
os_log("Task finished: %#", type: .info, task)
}
}
Edit: I can't see anything related to resume data for upload task in Apple doc also.

Swift - Network Requests and Background Queue

just wanted some clarification on the best practices to make network api calls in Swift 2.
Here is how my typical network requests looks like to download JSON data:
let session = NSURLSession(configuration: .defaultSessionConfiguration())
let url = NSURL(string: my_url_string)
let request = NSURLRequest(URL: url)
let dataTask = session.dataTaskWithRequest(request) { data, response, error in
do {
self.tableData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! [NSDictionary]
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
} catch let error {
print(error)
}
}
dataTask.resume()
My question is: should I wrap all of this code block into a background queue? Should I do as follows:
let download_queue = dispatch_queue_create("download", nil)
dispatch_async(download_queue) { () -> Void in
previous code here
}
Or should I use one of the given high priority queues such as:
dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INTERACTIVE, 0)
Also, when making additional network requests on subsequent view controllers, should I use the same queue I use here or should I create a new one?
By default NSURLSession API is highly asynchronous. Usefull information from the Apple docs.
There is no visible issues that indicate to wrap you're code block with GCD and also completion block runs on background thread so there is right usage of the GCD to update UITableview

View shown after programmatically triggered segue doesn't update immediately

I created two views and then connect one to the other by ctrl-dragging and creating a segue. Gave this segue the identifier "functionOutput1"
I then created a function and programmatically triggered the segue created above based on certain output in a function.
performSegue(withIdentifier: "functionOuput1", sender: self)
The view segues okay but the contents of the view don't appear until a minute later.
Is there something else I need to be doing here?
EDIT:
I'm performing this segue in a callback function for a URLRequest
let task = session.dataTask(with: request as URLRequest, completionHandler: {
data, response, error -> Void in
if (error != nil) {
// there is an error with the request
print("error: ", error)
} else {
// Request was successful. Check the contents of the response.
print("response: ", response)
let httpResponse = response as! HTTPURLResponse
if httpResponse.statusCode != 200 {
print("problems with server request")
} else {
print("request successful")
//go to next page
performSegue(withIdentifier: "functionOuput1", sender: self)
}
}
})
This could be a threading issue. Because the data is returned asynchronously. You need to run the view updates on the main thread. You can do this with Swift 3.0 with the following code:
DispatchQueue.main.async {
// Your view updates here
}

Swift UITableView reloadData() UI behavior

We are loading data via REST call:
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
//Update data store….
self.tableView.reloadData()
})
task.resume()
The data in the grid is not shown however. You have to click on the grid in the simulator - when you do this the data is shown. What are we missing here to get the data to automatically load without any UI interaction?
The reloadData() method must run in the main thread. dataTaskWithRequest(_ request: NSURLRequest) runs in a background thread:
let task = NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
dispatch_async(dispatch_get_main_queue(),{
//Update data store….
self.tableView.reloadData()
})
})
task.resume()
The completion handler gets called on a background thread. You must call reloadData on the main thread/queue.

Go to another view when NSURLSession finishes its job

My problem is when I try to go another view after some api call finishes, it wont go to the next view. Here is the code below. Thanks in advance.
var task = NSURLSession.sharedSession().dataTaskWithURL(url!, completionHandler: {
(data, response, error) in //println(response)
//println(error)
self.myWeather.apiCallData = JSON(data: data)
self.myWeather.apiCallError = error
println("api call finished")
if error == nil {
println("no error")
let weatherView = self.storyboard?.instantiateViewControllerWithIdentifier("WeatherView") as WeatherViewController
weatherView.myWeather = self.myWeather
self.navigationController?.pushViewController(weatherView, animated: false)
}
}).resume()
It does print api call finished and no error on console. But it doesn't go to the other scene.
The problem is that the completion handler code of the dataTaskWithURL method runs in a background secondary thread, not in the main thread (where view controller transition can happen).
Wrap the call to pushViewController in a main thread queue closure:
...
dispatch_async(dispatch_get_main_queue(), {
let navigationVC = self.navigationController
navigationVC?.pushViewController(weatherView, animated: false)
})
...
I have written the code in two lines to avoid a swift compiler bug (single statement and closure return value: see this). You can also write it as:
...
dispatch_async(dispatch_get_main_queue(), {
(self.navigationController)?.pushViewController(weatherView, animated: false)
return
})
...