Есть ли способ получить прогресс от dataTaskWithURL в режиме быстрого при загрузке данных?
NSURLSession.sharedSession().dataTaskWithURL(...)
Мне нужно показать индикатор выполнения во время загрузки данных.
Есть ли способ получить прогресс от dataTaskWithURL в режиме быстрого при загрузке данных?
NSURLSession.sharedSession().dataTaskWithURL(...)
Мне нужно показать индикатор выполнения во время загрузки данных.
вы можете использовать этот код для показа процесса загрузки с индикатором выполнения с его функциями делегата.
import UIKit
class ViewController: UIViewController,NSURLSessionDelegate,NSURLSessionDataDelegate{
    @IBOutlet weak var progress: UIProgressView!
    var buffer:NSMutableData = NSMutableData()
    var session:NSURLSession?
    var dataTask:NSURLSessionDataTask?
    let url = NSURL(string:"http://i.stack.imgur.com/b8zkg.png" )!
    var expectedContentLength = 0
    override func viewDidLoad() {
        super.viewDidLoad()
        progress.progress = 0.0
        let configuration = NSURLSessionConfiguration.defaultSessionConfiguration()
        let manqueue = NSOperationQueue.mainQueue()
        session = NSURLSession(configuration: configuration, delegate:self, delegateQueue: manqueue)
        dataTask = session?.dataTaskWithRequest(NSURLRequest(URL: url))
        dataTask?.resume()
        // Do any additional setup after loading the view, typically from a nib.
    }
    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveResponse response: NSURLResponse, completionHandler: (NSURLSessionResponseDisposition) -> Void) {
        //here you can get full lenth of your content
        expectedContentLength = Int(response.expectedContentLength)
        println(expectedContentLength)
        completionHandler(NSURLSessionResponseDisposition.Allow)
    }
    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        buffer.appendData(data)
        let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
        progress.progress =  percentageDownloaded
    }
    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        //use buffer here.Download is done
        progress.progress = 1.0   // download 100% complete
    }
}
Обновление для  Swift4: 
Поддерживает выполнение нескольких одновременных операций.
Файл: DownloadService.swift. Содержит ссылки на URLSession и отслеживает выполнение задач.
final class DownloadService: NSObject {
   private var session: URLSession!
   private var downloadTasks = [GenericDownloadTask]()
   public static let shared = DownloadService()
   private override init() {
      super.init()
      let configuration = URLSessionConfiguration.default
      session = URLSession(configuration: configuration,
                           delegate: self, delegateQueue: nil)
   }
   func download(request: URLRequest) -> DownloadTask {
      let task = session.dataTask(with: request)
      let downloadTask = GenericDownloadTask(task: task)
      downloadTasks.append(downloadTask)
      return downloadTask
   }
}
extension DownloadService: URLSessionDataDelegate {
   func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse,
                   completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
      guard let task = downloadTasks.first(where: { $0.task == dataTask }) else {
         completionHandler(.cancel)
         return
      }
      task.expectedContentLength = response.expectedContentLength
      completionHandler(.allow)
   }
   func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
      guard let task = downloadTasks.first(where: { $0.task == dataTask }) else {
         return
      }
      task.buffer.append(data)
      let percentageDownloaded = Double(task.buffer.count) / Double(task.expectedContentLength)
      DispatchQueue.main.async {
         task.progressHandler?(percentageDownloaded)
      }
   }
   func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
      guard let index = downloadTasks.index(where: { $0.task == task }) else {
         return
      }
      let task = downloadTasks.remove(at: index)
      DispatchQueue.main.async {
         if let e = error {
            task.completionHandler?(.failure(e))
         } else {
            task.completionHandler?(.success(task.buffer))
         }
      }
   }
}
Файл: DownloadTask.swift. Легкий интерфейс, чтобы скрыть конкретную реализацию.
protocol DownloadTask {
   var completionHandler: ResultType<Data>.Completion? { get set }
   var progressHandler: ((Double) -> Void)? { get set }
   func resume()
   func suspend()
   func cancel()
}
Файл: GenericDownloadTask.swift. Конкретная реализация интерфейса DownloadTask.
class GenericDownloadTask {
   var completionHandler: ResultType<Data>.Completion?
   var progressHandler: ((Double) -> Void)?
   private(set) var task: URLSessionDataTask
   var expectedContentLength: Int64 = 0
   var buffer = Data()
   init(task: URLSessionDataTask) {
      self.task = task
   }
   deinit {
      print("Deinit: \(task.originalRequest?.url?.absoluteString ?? "")")
   }
}
extension GenericDownloadTask: DownloadTask {
   func resume() {
      task.resume()
   }
   func suspend() {
      task.suspend()
   }
   func cancel() {
      task.cancel()
   }
}
Файл: ResultType.swift. Многоразовый тип для сохранения результата или ошибки.
public enum ResultType<T> {
   public typealias Completion = (ResultType<T>) -> Void
   case success(T)
   case failure(Swift.Error)
}
Использование: пример запуска two download tasks in parallel (приложение macOS):
class ViewController: NSViewController {
   @IBOutlet fileprivate weak var loadImageButton1: NSButton!
   @IBOutlet fileprivate weak var loadProgressIndicator1: NSProgressIndicator!
   @IBOutlet fileprivate weak var imageView1: NSImageView!
   @IBOutlet fileprivate weak var loadImageButton2: NSButton!
   @IBOutlet fileprivate weak var loadProgressIndicator2: NSProgressIndicator!
   @IBOutlet fileprivate weak var imageView2: NSImageView!
   fileprivate var downloadTask1:  DownloadTask?
   fileprivate var downloadTask2:  DownloadTask?
   override func viewDidLoad() {
      super.viewDidLoad()
      loadImageButton1.target = self
      loadImageButton1.action = #selector(startDownload1(_:))
      loadImageButton2.target = self
      loadImageButton2.action = #selector(startDownload2(_:))
   }
}
extension ViewController {
   @objc fileprivate func startDownload1(_ button: NSButton) {
      let url = URL(string: "http://localhost:8001/?imageID=01&tilestamp=\(Date.timeIntervalSinceReferenceDate)")!
      let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
      downloadTask1 = DownloadService.shared.download(request: request)
      downloadTask1?.completionHandler = { [weak self] in
         switch $0 {
         case .failure(let error):
            print(error)
         case .success(let data):
            print("Number of bytes: \(data.count)")
            self?.imageView1.image = NSImage(data: data)
         }
         self?.downloadTask1 = nil
         self?.loadImageButton1.isEnabled = true
      }
      downloadTask1?.progressHandler = { [weak self] in
         print("Task1: \($0)")
         self?.loadProgressIndicator1.doubleValue = $0
      }
      loadImageButton1.isEnabled = false
      imageView1.image = nil
      loadProgressIndicator1.doubleValue = 0
      downloadTask1?.resume()
   }
   @objc fileprivate func startDownload2(_ button: NSButton) {
      let url = URL(string: "http://localhost:8002/?imageID=02&tilestamp=\(Date.timeIntervalSinceReferenceDate)")!
      let request = URLRequest(url: url, cachePolicy: .reloadIgnoringLocalCacheData, timeoutInterval: 30)
      downloadTask2 = DownloadService.shared.download(request: request)
      downloadTask2?.completionHandler = { [weak self] in
         switch $0 {
         case .failure(let error):
            print(error)
         case .success(let data):
            print("Number of bytes: \(data.count)")
            self?.imageView2.image = NSImage(data: data)
         }
         self?.downloadTask2 = nil
         self?.loadImageButton2.isEnabled = true
      }
      downloadTask2?.progressHandler = { [weak self] in
         print("Task2: \($0)")
         self?.loadProgressIndicator2.doubleValue = $0
      }
      loadImageButton2.isEnabled = false
      imageView2.image = nil
      loadProgressIndicator2.doubleValue = 0
      downloadTask2?.resume()
   }
}
  Бонус 1. Файл StartPHPWebServer.command. Пример script для запуска 2 встроенных серверов PHP для имитации одновременной загрузки.
#!/bin/bash
AWLScriptDirPath=$(cd "$(dirname "$0")"; pwd)
cd "$AWLScriptDirPath"
php -S localhost:8001 &
php -S localhost:8002 &
ps -afx | grep php
echo "Press ENTER to exit."
read
killall php
  Бонус 2. Файл index.php. Пример PHP скрипт для медленной загрузки.
<?php
$imageID = $_REQUEST["imageID"];
$local_file = "Image-$imageID.jpg";
$download_rate = 20.5; // set the download rate limit (=> 20,5 kb/s)
if (file_exists($local_file) && is_file($local_file)) {
    header('Cache-control: private');
    header('Content-Type: image/jpeg');
    header('Content-Length: '.filesize($local_file));
    flush();
    $file = fopen($local_file, "r");
    while(!feof($file)) {
        // send the current file part to the browser
        print fread($file, round($download_rate * 1024));
        flush(); // flush the content to the browser
        usleep(0.25 * 1000000);
    }
    fclose($file);}
else {
    die('Error: The file '.$local_file.' does not exist!');
}
?>
Разное: содержимое каталога, имитирующего 2 сервера PHP.
Image-01.jpg
Image-02.jpg
StartPHPWebServer.command
index.php
Вы можете просто наблюдать свойство progress объекта URLSessionDataTask.
Пример:
import UIKit
class SomeViewController: UIViewController {
  private var observation: NSKeyValueObservation?
  deinit {
    observation?.invalidate()
  }
  override func viewDidLoad() {
    super.viewDidLoad()
    let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
    let task = URLSession.shared.dataTask(with: url)
    observation = task.progress.observe(\.fractionCompleted) { progress, _ in
      print("progress: ", progress.fractionCompleted)
    }
    task.resume()
  }
}
Пример игровой площадки:
import Foundation
import PlaygroundSupport
let page = PlaygroundPage.current
page.needsIndefiniteExecution = true
let url = URL(string: "https://source.unsplash.com/random/4000x4000")!
let task = URLSession.shared.dataTask(with: url) { _, _, _ in
  page.finishExecution()
}
// Don't forget to invalidate the observation when you don't need it anymore.
let observation = task.progress.observe(\.fractionCompleted) { progress, _ in
  print(progress.fractionCompleted)
}
task.resume()
в классе declare
class AudioPlayerViewController: UIViewController, URLSessionDelegate, URLSessionDataDelegate, URLSessionDownloadDelegate{
         var defaultSession: URLSession!
         var downloadTask: URLSessionDownloadTask!
в режиме загрузки
            defaultSession = Foundation.URLSession(configuration: .default, delegate: self, delegateQueue: nil)
            downloadProgress.setProgress(0.0, animated: false)
            downloadTask = defaultSession.downloadTask(with: audioUrl)
            downloadTask.Resume()
И добавьте следующих делегатов:
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
    DispatchQueue.main.async {
        self.downloadProgressBar.setProgress(Float(totalBytesWritten)/Float(totalBytesExpectedToWrite), animated: true)
    }
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        DispatchQueue.main.async {
//DOWNLOAD SUCCESSFUL AND FILE PATH WILL BE IN URL.
}
}
Что касается работы с Дхармешем, в методах делегатов URLSessionDelegate и URLSessionDataDelegate было довольно много незначительных изменений. Вот код совместимости Swift 4.2.
import UIKit
class ViewController: UIViewController, URLSessionDelegate, URLSessionDataDelegate {
    // MARK: - Variables
    // MARK: - IBOutlet
    @IBOutlet weak var progress: UIProgressView!
    // MARK: - IBAction
    @IBAction func goTapped(_ sender: UIButton) {
        let url = URL(string: "http://www.example.com/file.zip")!
        fetchFile(url: url)
    }
    // MARK: - Life cycle
    override func viewDidLoad() {
        super.viewDidLoad()
    }
    func fetchFile(url: URL) {
        progress.progress = 0.0
        let configuration = URLSessionConfiguration.default
        let mainQueue = OperationQueue.main
        session = URLSession(configuration: configuration, delegate: self, delegateQueue: mainQueue)
        dataTask = session?.dataTask(with: URLRequest(url: url))
        dataTask?.resume()
    }
    var buffer: NSMutableData = NSMutableData()
    var session: URLSession?
    var dataTask: URLSessionDataTask?
    var expectedContentLength = 0
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        buffer.append(data)
        let percentageDownloaded = Float(buffer.length) / Float(expectedContentLength)
        progress.progress =  percentageDownloaded
    }
    func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: (URLSession.ResponseDisposition) -> Void) {
        expectedContentLength = Int(response.expectedContentLength)
        completionHandler(URLSession.ResponseDisposition.allow)
    }
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        progress.progress = 1.0
    }
}
для загрузки данных вам нужно установить NSURLSessionDownloadDelegate и реализовать URLSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)
Есть хороший учебник об этом здесь, но находится в объекте-c.