class DownloadOperation : Operation {
private var task : URLSessionDownloadTask!
enum OperationState : Int {
case ready
case executing
case finished
}
// default state is ready (when the operation is created)
private var state : OperationState = .ready {
willSet {
self.willChangeValue(forKey: "isExecuting")
self.willChangeValue(forKey: "isFinished")
}
didSet {
self.didChangeValue(forKey: "isExecuting")
self.didChangeValue(forKey: "isFinished")
}
}
override var isReady: Bool { return state == .ready }
override var isExecuting: Bool { return state == .executing }
override var isFinished: Bool { return state == .finished }
init(session: URLSession, downloadTaskURL: URL, completionHandler: ((URL?, URLResponse?, Error?) -> Void)?) {
super.init()
task = session.downloadTask(with: downloadTaskURL, completionHandler: { [weak self] (localURL, response, error) in
/*
if there is a custom completionHandler defined,
pass the result gotten in downloadTask's completionHandler to the
custom completionHandler
*/
if let completionHandler = completionHandler {
// localURL is the temporary URL the downloaded file is located
completionHandler(localURL, response, error)
}
/*
set the operation state to finished once
the download task is completed or have error
*/
self?.state = .finished
})
}
override func start() {
/*
if the operation or queue got cancelled even
before the operation has started, set the
operation state to finished and return
*/
if(self.isCancelled) {
state = .finished
return
}
// set the state to executing
state = .executing
print("downloading \(self.task.originalRequest?.url?.absoluteString ?? "")")
// start the downloading
self.task.resume()
}
override func cancel() {
super.cancel()
// cancel the downloading
self.task.cancel()
}
}
use it in vc:
class ViewController: UIViewController {
@IBOutlet weak var downloadButton: UIButton!
@IBOutlet weak var progressLabel: UILabel!
@IBOutlet weak var cancelButton: UIButton!
var session : URLSession!
var queue : OperationQueue!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
session = URLSession(configuration: URLSessionConfiguration.default, delegate: nil, delegateQueue: nil)
queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
self.cancelButton.isEnabled = false
}
@IBAction func downloadTapped(_ sender: UIButton) {
let completionOperation = BlockOperation {
print("finished download all")
DispatchQueue.main.async {
self.cancelButton.isEnabled = false
}
}
let urls = [
URL(string: "https:/...zip")!,
URL(string: "https:/...zip")!,
URL(string: "https:/...zip")!,
URL(string: "https:/...zip")!,
URL(string: "https:/...zip")!,
]
for (index, url) in urls.enumerated() {
let operation = DownloadOperation(session: self.session, downloadTaskURL: url, completionHandler: { (localURL, urlResponse, error) in
if error == nil {
DispatchQueue.main.async {
self.progressLabel.text = "\(index + 1) / \(urls.count) files downloaded"
}
}
})
completionOperation.addDependency(operation)
self.queue.addOperation(operation)
}
self.queue.addOperation(completionOperation)
self.cancelButton.isEnabled = true
}
@IBAction func cancelTapped(_ sender: UIButton) {
queue.cancelAllOperations()
self.progressLabel.text = "Download cancelled"
self.cancelButton.isEnabled = false
}
}