Last active
June 14, 2020 08:12
-
-
Save ajjames/3863a5bf516175f8d33f to your computer and use it in GitHub Desktop.
Cancelable UIImageView async download extension (with simple NSCache-backed ImageManager class, and cancelable Downloader class)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // Downloader.swift | |
| // Copyright (c) 2015 Andrew James. All rights reserved. | |
| // | |
| import Foundation | |
| import UIKit | |
| public class Downloader : NSObject | |
| { | |
| private let operation: NSOperation | |
| public init(operation:NSOperation) | |
| { | |
| self.operation = operation | |
| } | |
| public convenience init(imageAtUrl imageUrl:NSURL, completion: ((image:UIImage?,error:NSError?)->())?) | |
| { | |
| var newOperation = NSBlockOperation() | |
| newOperation.addExecutionBlock { () -> Void in | |
| ImageManager.getImageSynchronously(imageUrl, cancelableOperation:newOperation) { (image, error) -> () in | |
| if let callback = completion | |
| { | |
| callback(image: image, error: error) | |
| } | |
| } | |
| } | |
| self.init(operation: newOperation) | |
| } | |
| public func cancel() | |
| { | |
| if !operation.cancelled | |
| { | |
| operation.cancel() | |
| } | |
| } | |
| public func start() | |
| { | |
| var blockOperation = operation | |
| if blockOperation.ready | |
| { | |
| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) { | |
| blockOperation.start() | |
| } | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // ImageManager.swift | |
| // Copyright (c) 2015 Andrew James. All rights reserved. | |
| // | |
| import Foundation | |
| import UIKit | |
| public class ImageManager | |
| { | |
| private class var imageCache: NSCache | |
| { | |
| struct Static | |
| { | |
| static var instance: NSCache? | |
| static var token: dispatch_once_t = 0 | |
| } | |
| dispatch_once(&Static.token) { | |
| Static.instance = NSCache() | |
| } | |
| return Static.instance! | |
| } | |
| public class func getImageAsynchronously(url:NSURL, completionHandler:(image:UIImage?, error: NSError?)->()) | |
| { | |
| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in | |
| ImageManager.getImageSynchronously(url, cancelableOperation: nil, completionHandler: completionHandler) | |
| }) | |
| } | |
| public class func getImageSynchronously(url:NSURL, cancelableOperation:NSOperation?, completionHandler:(image:UIImage?, error: NSError?)->()) | |
| { | |
| let isCancelled = cancelableOperation?.cancelled ?? false | |
| if isCancelled | |
| { | |
| self.hideNetworkActivity() | |
| dispatch_async(dispatch_get_main_queue()) { | |
| completionHandler(image: nil, error: nil) | |
| } | |
| } | |
| showNetworkActivity() | |
| var image:UIImage? | |
| var returnError:NSError? | |
| if let cachedImage = self.imageCache.objectForKey(url) as? UIImage | |
| { | |
| image = cachedImage | |
| } | |
| else if let data = self.fetchDataSyncronously(url) | |
| { | |
| image = UIImage(data: data) | |
| if image != nil | |
| { | |
| self.imageCache.setObject(image!, forKey: url) | |
| } | |
| } | |
| if image == nil | |
| { | |
| returnError = NSError(domain:"Error: unable to load image", code:0, userInfo:nil) | |
| } | |
| self.hideNetworkActivity() | |
| dispatch_async(dispatch_get_main_queue()) { | |
| completionHandler(image:image,error:returnError) | |
| } | |
| } | |
| public class func fetchDataSyncronously(url:NSURL) -> NSData? | |
| { | |
| var request: NSURLRequest = NSURLRequest(URL: url) | |
| var urlConnection: NSURLConnection = NSURLConnection(request: request, delegate: self)! | |
| var response: NSURLResponse? | |
| var error:NSError? | |
| let data = NSURLConnection.sendSynchronousRequest(request, returningResponse:&response, error:&error) | |
| if (error != nil) | |
| { | |
| println(error!.description) | |
| } | |
| return data | |
| } | |
| private class func showNetworkActivity() | |
| { | |
| dispatch_async(dispatch_get_main_queue()) { | |
| UIApplication.sharedApplication().networkActivityIndicatorVisible = true | |
| } | |
| } | |
| private class func hideNetworkActivity() | |
| { | |
| dispatch_async(dispatch_get_main_queue()) { | |
| UIApplication.sharedApplication().networkActivityIndicatorVisible = false | |
| } | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // UIImageView-Extension.swift | |
| // Copyright (c) 2015 Andrew James. All rights reserved. | |
| // | |
| import Foundation | |
| import UIKit | |
| import ObjectiveC | |
| var AssociatedObjectHandle_downloader: UInt8 = 0 | |
| var AssociatedObjectHandle_downloaderUrl: UInt8 = 0 | |
| public extension UIImageView | |
| { | |
| public func downloadImageAsync(url:NSURL, completion:(imageView:UIImageView)->()) | |
| { | |
| downloaderUrl = url | |
| cancelDownload() | |
| var imageView = self | |
| downloader = Downloader(imageAtUrl: url, completion: { (image, error) -> () in | |
| if error != nil | |
| { | |
| println(error!.description) | |
| } | |
| if imageView.downloaderUrl == url | |
| { | |
| imageView.downloader = nil | |
| dispatch_async(dispatch_get_main_queue(), { () -> Void in | |
| imageView.image = image | |
| completion(imageView: imageView) | |
| }) | |
| } | |
| else | |
| { | |
| completion(imageView: imageView) | |
| } | |
| }) | |
| self.downloader?.start() | |
| } | |
| public func cancelDownload() | |
| { | |
| self.downloader?.cancel() | |
| } | |
| //-------------------------------------------------------------------------------------------------------- | |
| // BELOW: | |
| // Contains objective-C voodoo until Swift offers first-class support for stored properties in extensions | |
| //-------------------------------------------------------------------------------------------------------- | |
| private var downloader: Downloader? | |
| { | |
| get | |
| { | |
| if let downloaderObject = objc_getAssociatedObject(self, &AssociatedObjectHandle_downloader) as? Downloader | |
| { | |
| return downloaderObject | |
| } | |
| else | |
| { | |
| return nil | |
| } | |
| } | |
| set | |
| { | |
| objc_setAssociatedObject(self, &AssociatedObjectHandle_downloader, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) | |
| } | |
| } | |
| private var downloaderUrl: NSURL? | |
| { | |
| get | |
| { | |
| if let downloaderUrlObject = objc_getAssociatedObject(self, &AssociatedObjectHandle_downloaderUrl) as? NSURL | |
| { | |
| return downloaderUrlObject | |
| } | |
| else | |
| { | |
| return nil | |
| } | |
| } | |
| set | |
| { | |
| objc_setAssociatedObject(self, &AssociatedObjectHandle_downloaderUrl, newValue, objc_AssociationPolicy(OBJC_ASSOCIATION_RETAIN_NONATOMIC)) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment