How to pause NSURLSessionDownloadTask? - ios

This is how I use NSURLSessionDownloadTask:
let sessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
let sessionManager = AFURLSessionManager(sessionConfiguration: sessionConfiguration)
if let url = NSURL(string: URLString) {
let request = NSURLRequest(URL: url)
let sessionDownloadTask = sessionManager.downloadTaskWithRequest(request, progress: nil, destination: { (url, response) -> NSURL in
self.createDirectoryAtPath(destinationPath)
return destinationPath.URLByAppendingPathComponent(fileName)
}, completionHandler: { response, url, error in
completionBlock(error)
})
sessionDownloadTask.resume()
}
I know that I can call sessionDownloadTask.cancel() and sessionDownloadTask.resume(), but I need to call there .pause(), and then .resume() to continue download. Is it possible?

This method of NSURLSessionTask
.suspend()
Doc
A task, while suspended, produces no network traffic and is not subject to timeouts. A download task can continue transferring data at a later time. All other tasks must start over when resumed.

Don't call the sessionDownloadTask.cancel() instead use the call sessionDownloadTask.cancelByProducingResumeData and whenever you want to resume downloading use the method of NSURLSession
sessionManager.downloadTaskWithResumeData:resumeDataObject

Related

URLSessionTask instance variable vs local variable - who owns the reference?

Please refer to the code snippet below (certain parts not relevant to question are omitted)
In WebService1, dataTask is an instance variable/property whereas in WebService2, dataTask is a local variable inside the function callWebService.
final class WebService1 {
let urlSession = URLSession(configuration: .default)
// 1. data task is a private property of PNWebService here
private var dataTask: URLSessionDataTask?
func callWebService(completion: () -> ()) {
var urlRequest = URLRequest(url: url)
dataTask = urlSession.dataTask(with: urlRequest) {
// task complete
completion()
}
dataTask?.resume()
}
}
final class WebService2 {
let urlSession = URLSession(configuration: .default)
func callWebService(completion: () -> ()) {
var urlRequest = URLRequest(url: url)
// 2. data task is a local variable here
var dataTask = urlSession.dataTask(with: url) {
// task complete
completion()
}
dataTask.resume()
}
}
Clients an call these two services in the usual way:
let ws1 = WebService1()
ws1.callWebService() {
print("1. complete")
}
let ws2 = WebService2()
ws2.callWebService() {
print("2. complete")
}
Q1) Who owns a strong reference to dataTask in WebService2 so that it is not deallocated before the completion handler is called?
Q2) From a client perspective what is the difference at runtime between WebService1 & WebService2?
Are you asking which pattern is correct? Neither. The URLSession owns the data task and manages its memory as soon as you resume it for the first time, so there is no need for you to keep any reference to it, unless you plan to do something else with that reference such as configuring the task further or cancelling the operation later. Generally it is sufficient and quite usual to say
urlSession.dataTask(with:url) { data, resp, err in
// whatever
}.resume()

Swift 3: Which is better for webservice calls? URLSession.shared.dataTask(with:URL) OR session.dataTask(with: request)

Swift 3:
I am trying to understand the difference between the 2 approaches to make network calls, and if one is better than the other in terms of performance or any other parameter.
I have tested both approaches and results are the same. Trying to implement best practises/ most commonly followed 'style' .
let url = "https://api.flickr.com/services/rest/?method=flickr.photos.getrecent&api_key=APIKEY&extras=url_h,date_taken&format=json&nojsoncallback=1"
Approach 1: dataTaskwithURL
let task = URLSession.shared.dataTask(with: url, completionHandler: { (theData, theResponse, theError) in
})
task.resume()
Approach 2: dataTaskwithRequest with NSURLSession(configuration: config)
let request = URLRequest(url: url)
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { (data, response, error) in
})
task.resume()
Many books/blogs have their own theory to use one of the 2 approaches

How to know PDF file size before opened by UIWebview on iOS

Is that possible? Maybe there is some PDF api can get some information without downloading the whole PDF file?
You can use NSURLSession to request for the content length of the remote file. This can be done by setting the request.HTTPMethod = "HEAD"
This method gets the file size for the specified remoteFileURL
func getRemoteFileSize(remoteFileURL : String) -> Int64{
var contentLength: Int64 = NSURLSessionTransferSizeUnknown
if let remoteURL = NSURL(string: remoteFileURL) {
// Create a semaphore to wait for NSURLSession's dataTask to complete
// before returning from function
let semaphore = dispatch_semaphore_create(0)
let request = NSMutableURLRequest(URL: remoteURL)
request.HTTPMethod = "HEAD"
let session = NSURLSession.sharedSession().dataTaskWithRequest(request,
completionHandler:
{ (data, response, error) -> Void in
contentLength = response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
//Signal the semaphore to continue execution
dispatch_semaphore_signal(semaphore)
})
session.resume()
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
}
print("File size is \(contentLength)")
return contentLength
}
Please note : This method should be called on some other thread as this blocks the thread it's called on.
For e.g. :
Method call
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue) {
self.getRemoteFileSize("https://pmcdeadline2.files.wordpress.com/2014/06/apple-logo.jpg")
}

Handle losing connection

I download data from Internet in this way using Swift:
let postEndpoint: String = "http://webisitetest.com"
guard let url = NSURL(string: postEndpoint) else {
print("Error: cannot create URL")
return
}
let urlRequest = NSURLRequest(URL: url)
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: config)
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) in
// this is where the completion handler code goes
print(response)
print(error)
})
task.resume()
Because I download also images and download can during various seconds (depends connection), if during download the user loses connection, how I do handle this situation?
I want to show him a message for example "Connection lost, download cancelled, try again", but how I do catch this event?
Check for error presence in your completion handler:
let task = session.dataTaskWithRequest(urlRequest, completionHandler: { (maybeData: NSData?, maybeResponse: NSURLResponse?, maybeError: NSError?) in
// check for error
guard maybeError = nil else {
print(maybeError!)
}
// otherwise process the request
print(maybeResponse)
})
Any failure can be handled in the completion block. You can also specify the seconds before timeout for request and resource in the configuration.
- (NSURLSessionConfiguration*) requestConfigurations
{
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
configuration.timeoutIntervalForRequest = 120;
configuration.timeoutIntervalForResource = 980.0;
return configuration;
}
Error codes from the error can help you distinguish the type of failure.

NSURLSessionDownloadTask API call clarification on syntax in Swift

I'm learning to do a basic networking call, and following a tutorial. The tutorial goes through concurrency, and downloading the JSON data in the background thread. I was wondering why in the sharedSession.downloadTaskWithURL method the queryURL would be passed as the NSURL object as opposed to the baseURL. I feel like I'm missing something pretty obvious!
func searchRecipeData() {
let baseURL = NSURL(string: "http://api.recipes.com/v1/api/recipes?_app_id=\(apiID)&_app_key=\(apiKey)")
let queryURL = NSURL(string: "&q=onion+soup", relativeToURL: baseURL)!
let sharedSession = NSURLSession.sharedSession()
let downloadData: NSURLSessionDownloadTask = sharedSession.downloadTaskWithURL(queryURL, completionHandler: { (location: NSURL!, response: NSURLResponse!, error: NSError!) -> Void in
if (error == nil) {
let data = NSData(contentsOfURL: baseURL!)
println(data)
}
})
// Resumes it even though it hasn't started yet
downloadData.resume()
}
Take a look at the second parameter where you create queryURL. You are passing the baseURL constant. What happens is that the '&q=onion+soup' query parameter is told to be relative to the baseURL. The queryURL constant is the full URL, and it is then passed to downloadTaskWithURL.

Resources