Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NSURLSession delegates not called

Tags:

ios

swift

In the following code, the file downloads just fine. However none of the delegate methods seem to be called as I receive no output whatsoever. the progressView is not updated either. Any idea why?

import Foundation
import UIKit

class Podcast: PFQueryTableViewController, UINavigationControllerDelegate, MWFeedParserDelegate, UITableViewDataSource, NSURLSessionDelegate, NSURLSessionDownloadDelegate {

    func downloadEpisodeWithFeedItem(episodeURL: NSURL) {

    var request: NSURLRequest = NSURLRequest(URL: episodeURL)
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: config, delegate: self, delegateQueue: nil)

    var downloadTask = session.downloadTaskWithURL(episodeURL, completionHandler: { (url, response, error) -> Void in
        println("task completed")
        if (error != nil) {
            println(error.localizedDescription)
        } else {
            println("no error")
            println(response)
        }
    })
    downloadTask.resume()

}

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
    println("didResumeAtOffset")
}

    func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
          var downloadProgress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
    println(Float(downloadProgress))
    println("sup")

    epCell.progressView.progress = Float(downloadProgress)
}

     func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
    println(location)

}
}
like image 642
user2747220 Avatar asked Nov 02 '14 00:11

user2747220


3 Answers

From my testing, you have to choose whether you want to use a delegate or a completion handler - if you specify both, only the completion handler gets called. This code gave me running progress updates and the didFinishDownloadingToURL event:

func downloadEpisodeWithFeedItem(episodeURL: NSURL) {
    let request: NSURLRequest = NSURLRequest(URL: episodeURL)
    let config = NSURLSessionConfiguration.defaultSessionConfiguration()
    let session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
    
    let downloadTask = session.downloadTaskWithURL(episodeURL)
    downloadTask.resume()
}

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didResumeAtOffset fileOffset: Int64, expectedTotalBytes: Int64) {
    println("didResumeAtOffset: \(fileOffset)")
}

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    var downloadProgress = Double(totalBytesWritten) / Double(totalBytesExpectedToWrite)
    println("downloadProgress: \(downloadProgress)")
}

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) {
    println("didFinishDownloadingToURL: \(location)")
    println(downloadTask)
}

From the NSURLSession documentation, here's the relevant section:

Like most networking APIs, the NSURLSession API is highly asynchronous. It returns data in one of two ways, depending on the methods you call:

  • To a completion handler block that returns data to your app when a transfer finishes successfully or with an error.
  • By calling methods on your custom delegate as the data is received.
  • By calling methods on your custom delegate when download to a file is complete.

So by design it returns data to either a completion handler block or a delegate. But as evinced here, not both.

like image 135
Nate Cook Avatar answered Nov 17 '22 17:11

Nate Cook


Interestingly, Apple specifically explains this behavior in their NSURLSessionDataDelegate (but neither in the base delegate NSURLSessionTaskDelegate nor in NSURLSessionDownloadDelegate)

NOTE

An NSURLSession object need not have a delegate. If no delegate is assigned, when you create tasks in that session, you must provide a completion handler block to obtain the data.

Completion handler block are primarily intended as an alternative to using a custom delegate. If you create a task using a method that takes a completion handler block, the delegate methods for response and data delivery are not called.

like image 20
Manfred Urban Avatar answered Nov 17 '22 16:11

Manfred Urban


Swift 3

class ViewController: UIViewController {
    var urlLink: URL!
    var defaultSession: URLSession!
    var downloadTask: URLSessionDownloadTask!
}

// MARK: Button Pressed
    @IBAction func btnDownloadPressed(_ sender: UIButton) {
        let urlLink1 = URL.init(string: "https://github.com/VivekVithlani/QRCodeReader/archive/master.zip")
        startDownloading(url: urlLink!)
}
    @IBAction func btnResumePressed(_ sender: UIButton) {
    downloadTask.resume()
}

@IBAction func btnStopPressed(_ sender: UIButton) {
    downloadTask.cancel()
}

@IBAction func btnPausePressed(_ sender: UIButton) {
    downloadTask.suspend()
}

    func startDownloading (url:URL) {
        let backgroundSessionConfiguration = URLSessionConfiguration.background(withIdentifier: "backgroundSession")
        defaultSession = Foundation.URLSession(configuration: backgroundSessionConfiguration, delegate: self, delegateQueue: OperationQueue.main)
        downloadProgress.setProgress(0.0, animated: false)
        downloadTask = defaultSession.downloadTask(with: urlLink)
        downloadTask.resume()
    }

// MARK:- URLSessionDownloadDelegate
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
    print("File download succesfully")
}

func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    downloadProgress.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
}

func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
    downloadTask = nil
    downloadProgress.setProgress(0.0, animated: true)
    if (error != nil) {
        print("didCompleteWithError \(error?.localizedDescription)")
    }
    else {
        print("The task finished successfully")
    }
}
like image 3
Vivek Avatar answered Nov 17 '22 16:11

Vivek