In this episode we examine the asynchronous (a.k.a concurrent) type of NSOperation where we are doing things that involve callback blocks or delegates.
Episode Links Source Code Hubble Telescope Images - Here you can download extremely high resolution images from the Hubble space telescope. Creating an Asynchronous-friendly Operation Base Class import Foundation class Operation : NSOperation { override var asynchronous: Bool { return true } private var _executing = false { willSet { willChangeValueForKey("isExecuting") } didSet { didChangeValueForKey("isExecuting") } } override var executing: Bool { return _executing } private var _finished = false { willSet { willChangeValueForKey("isFinished") } didSet { didChangeValueForKey("isFinished") } } override var finished: Bool { return _finished } override func start() { _executing = true execute() } func execute() { fatalError("You must override this") } func finish() { _executing = false _finished = true } } Implementing DownloadImageOperation class DownloadImageOperation : Operation, NSURLSessionDownloadDelegate { let imageURL: NSURL let targetPath: String var downloadTask: NSURLSessionDownloadTask? var progressBlock: (Float) -> () = { _ in } init(imageURL: NSURL, targetPath: String) { self.imageURL = imageURL self.targetPath = targetPath } lazy var session: NSURLSession = { let config = NSURLSessionConfiguration.ephemeralSessionConfiguration() return NSURLSession(configuration: config, delegate: self, delegateQueue: nil) }() override func execute() { if NSFileManager.defaultManager().fileExistsAtPath(targetPath) { NSFileManager.defaultManager().removeItemAtPath(targetPath, error: nil) } downloadTask = session.downloadTaskWithURL(imageURL) downloadTask?.resume() } func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) { let progress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite) dispatch_async(dispatch_get_main_queue()) { self.progressBlock(progress) } } func URLSession(session: NSURLSession, downloadTask: NSURLSessionDownloadTask, didFinishDownloadingToURL location: NSURL) { let targetURL = NSURL(fileURLWithPath: targetPath)! var copyError : NSError? if NSFileManager.defaultManager().copyItemAtURL(location, toURL: targetURL, error: ©Error) { finish() } else { fatalError("Could not copy: \(copyError)") } } } Running the operation from the view controller @IBAction func downloadButtonTapped(sender: AnyObject) { let url = NSURL(string: "http://imgsrc.hubblesite.org/hu/db/images/hs-2006-10-a-hires_jpg.jpg")! let docsDir = NSSearchPathForDirectoriesInDomains(.CachesDirectory, .UserDomainMask, true).first! as! String let targetPath = docsDir.stringByAppendingPathComponent("hubble.jpg") let downloadOperation = DownloadImageOperation(imageURL: url, targetPath: targetPath) downloadOperation.progressBlock = { self.downloadProgressView.progress = $0 } downloadOperation.completionBlock = { dispatch_async(dispatch_get_main_queue()) { if let image = UIImage(contentsOfFile: targetPath) { self.displayImage(image) } } } operationQueue.addOperation(downloadOperation) }