How can I download datas from multiple URL in concurrency mode?

1.4k views Asked by At

I have tried this link for download only one URL. Successfully working for pause and resume too.

Now I am trying for multiple URL (i.e, for 5 URL). If 2nd URL is progressing, on that time if I start third URL means, 2nd is stopped.

I don't know to run all url in concurrency. I have tried with NSOperationQueue. But I don't know exact syntax and also I don't know how to add task in Queue.

There should not be any interruption between my URL links. How to do that?

My code:

var dict = [NSURLSessionTask:Int]()

lazy var session : NSURLSession = {
        let config = NSURLSessionConfiguration.ephemeralSessionConfiguration()

        config.allowsCellularAccess = false
        let session = NSURLSession(configuration: config, delegate: self, delegateQueue: NSOperationQueue.mainQueue())

        println(session)
        return session

        }()

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten writ: Int64, totalBytesExpectedToWrite exp: Int64) {
        if let numberOfTask = dict[downloadTask]
    {
        println("which task is this\(dict[downloadTask])")

        println("downloaded \(100*writ/exp)")
        taskTotalBytesWritten = Int(writ)
        taskTotalBytesExpectedToWrite = Int(exp)
        percentageWritten = Float(taskTotalBytesWritten) / Float(taskTotalBytesExpectedToWrite)

        downLoadTblVw.delegate = self
        downLoadTblVw.reloadData()
    }
     }

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


        // unused in this example
    }

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        println("completed: error: \(error)")
    }

    // this is the only required NSURLSessionDownloadDelegate method

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

        let documentsDirectoryURL =  NSFileManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask).first as NSURL
        println("Finished downloading!")
        println(documentsDirectoryURL)
        var err:NSError?

        // Here you can move your downloaded file
        if NSFileManager().moveItemAtURL(location, toURL: documentsDirectoryURL.URLByAppendingPathComponent(downloadTask.response!.suggestedFilename!), error: &err) {
            println("File saved")
        } else {
            if let err = err {
                println("File not saved.\n\(err.description)")

            }
        }

    }



    @IBAction func startDownload(sender: UIButton) {
        var btnPos: CGPoint = sender.convertPoint(CGPointZero, toView: downLoadTblVw)
        var indePath: NSIndexPath = downLoadTblVw.indexPathForRowAtPoint(btnPos)!

        println("INDE\(indePath.row)")

        buttonTag = indePath.row

        if self.task != nil {

            println("PRESSED TASK NIL")
            return
        }

        switch(buttonTag)
        {
        case 0:

            var myQueue = NSOperationQueue()
            myQueue.addOperationWithBlock({

                let s = "https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/MobileHIG.pdf"
                let url = NSURL(string:s)!
                let req = NSMutableURLRequest(URL:url)
                let task = self.session.downloadTaskWithRequest(req)
                self.task = task

                dict[task] = 0
                println("SESSION -> task \(task)")

                task.resume()
                println("PRESSED SECOND TIME")
            })
            break

        case 1:

  let s = "https://developer.apple.com/library/ios/documentation/iphone/conceptual/iphoneosprogrammingguide/iphoneappprogrammingguide.pdf"
            let url = NSURL(string:s)!
            let req = NSMutableURLRequest(URL:url)

            var myQueue = NSOperationQueue()
            myQueue.addOperationWithBlock({

                let task = self.session.downloadTaskWithRequest(req)
                self.task_1 = task
                dict[task_1] = 1
                println("SESSION _1-> task \(task)")
                task.resume()

                })
            break

        case 2:

            let s = "http://manuals.info.apple.com/MANUALS/1000/MA1565/en_US/iphone_user_guide.pdf"
            let url = NSURL(string:s)!
            let req = NSMutableURLRequest(URL:url)
            let task = self.session.downloadTaskWithRequest(req)
            self.task_2 = task
            dict[task_2] = 2
            println("SESSION _2-> task \(task)")
            task.resume()
            break

        case 3:

            let s = "https://developer.apple.com/library/ios/documentation/AudioVideo/Conceptual/AVFoundationPG/AVFoundationPG.pdf"
            let url = NSURL(string:s)!
            let req = NSMutableURLRequest(URL:url)
            let task = self.session.downloadTaskWithRequest(req)
            self.task_3 = task
            dict[task_3] = 3
            println("SESSION _3-> task \(task)")
            task.resume()

            break
        default:
            println("WRONG BUTTON PRESSED")
            break
        }
      }
2

There are 2 answers

2
xgdgsc On

You can use the library Alamofire to handle this situation. The essence is to use multiple threads to fetch data from URLs and call some callback function to handle the data. You can read the docs of the library.

5
nRewik On

Your download code look fine. When the button is pressed the download task will begin. ( For example, If I click at button_2 and button_3, the download task of 2 and 3 will be downloaded simultaneously )

The flaw in your code is at

func URLSession(session: NSURLSession, 
                downloadTask: NSURLSessionDownloadTask, 
                didWriteData bytesWritten: Int64, 
                totalBytesWritten writ: Int64, 
                totalBytesExpectedToWrite exp: Int64)

Since you want to keep track the progress of each task, you have to find which downloadTask that send this delegate call.

To keep track which task fire the delegate, you can do something like this

let dict = [NSURLSessionTask:Int]()

let task1 = session.DownloadTaskWithURL( ..... ) 
let task2 = session.DownloadTaskWithURL( ..... ) 

dict[task1] = 0
dict[task2] = 1

and in the delegate call, you can get the task correspond to number you set like this

func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten writ: Int64, totalBytesExpectedToWrite exp: Int64) {
    if let numberOfTask = dict[downloadTask]{
        progress[numberOfTask] = Float(taskTotalBytesWritten) / Float(taskTotalBytesExpectedToWrite)
        downLoadTblVw.reloadData()
    }
}