I have been using Nuke up until last week but I switched to SDWebImage because it is a few times more popular than Nuke and seems more built out.
However, after switching to SDWebImage I have noticed significantly longer build times. I am not asking for an opinion here. In terms of tangible metrics, is Nuke more performant and lighter than SDWebImage?
Although I do not know SDWebImage inside and out, it seems SDWebImage has tons of feature but lacks a key one: I have not found how to set a failure image in case of network failure while loading image. Although, it is extremely easy to do in Nuke.
let options = ImageLoadingOptions(
failureImage: #imageLiteral(resourceName: "image-not-found")
)
Nuke.loadImage(with: URL(string: products[indexPath.row].imageLink)!, options: options, into: cell.productImage)
It's not neccesary/recommended to use external frameworks just for loading an image from an URL.
If you use an external framework you are adding unnecesary codes to your project because there is no framework that just add a function to load an image, it's probably that you are adding more unnecesary stuff with that framework.
I recommend you to implement it by yourself! It going to be more performant and easy than adding an external framework.
This is a code to load an image without external frameworks:
import UIKit
typealias GMImageServiceSuccess = (UIImage) -> Void
typealias GMImageServiceFail = (Error) -> Void
class GMImageService {
// MARK: - Class vars.
private static let imageServiceCache = NSCache<NSString, UIImage>()
// MARK: - Vars.
private var currentDataTask: URLSessionDataTask?
// MARK: - Fetch images functions.
func gmImageFromURL(_ urlString: String, sucess: GMImageServiceSuccess?, fail: GMImageServiceFail?) {
if let imageFromCache = GMImageService.imageServiceCache.object(forKey: urlString as NSString) {
sucess?(imageFromCache)
return
}
guard let imageURL = URL(string: urlString) else {
// ERROR.
return
}
URLSession.shared.dataTask(with: imageURL) { data, response, error in
guard let imageData = data else {
// ERROR.
return
}
if let imageToCache = UIImage(data: imageData) {
DispatchQueue.main.async {
GMImageService.imageServiceCache.setObject(imageToCache, forKey: urlString as NSString)
sucess?(imageToCache)
}
} else {
// ERROR.
}
}.resume()
}
// MARK: - Cancel images functions.
func mpImageCancelCurrent() {
if let currentTask = self.currentDataTask {
currentTask.cancel()
}
}
}
Let me know if you need to implement an image cache, it's easy in swift!
Related
I am developing Widgets for iOS and I really don't know how to download images for the widgets.
The widget currently downloads an array of Objects, and every object has a URL of an image. The idea is that every object makes a SimpleEntry for the Timeline.
What's the best way of achieving this? I read that Widgets shouldn't use the ObservableObject. I fetch the set of objects in the timeline provider, which seems to be what Apple recommends. But do I also download the images there and I wait until all are done to send the timeline?
Any advice would be very helpful,
Yes, you should download the images in the timeline provider and send the timeline when they are all done. Refer to the following recommendation by an Apple frameworks engineer.
I use a dispatch group to achieve this.
Something like:
let imageRequestGroup = DispatchGroup()
var images: [UIImage] = []
for imageUrl in imageUrls {
imageRequestGroup.enter()
yourAsyncUIImageProvider.getImage(fromUrl: imageUrl) { image in
images.append(image)
imageRequestGroup.leave()
}
}
imageRequestGroup.notify(queue: .main) {
completion(images)
}
I then use SwiftUI's Image(uiImage:) initializer to display the images
I dont have a good solution, but I try to use WidgetCenter.shared.reloadAllTimelines(), and it make sence.
In the following code.
var downloadImage: UIImage?
func downloadImage(url: URL) -> UIImage {
var picImage: UIImage!
if self.downloadImage == nil {
picImage = UIImage(named: "Default Image")
DispatchQueue.global(qos: .background).async {
do {
let data = try Data(contentsOf: url)
DispatchQueue.main.async {
self.downloadImage = UIImage.init(data: data)
if self.downloadImage != nil {
DispatchQueue.main.async {
WidgetCenter.shared.reloadAllTimelines()
}
}
}
} catch { }
}
} else {
picImage = self.downloadImage
}
return picImage
}
Also you have to consider when to delete this picture.
This like tableView.reloadData().
I am a fairly decent Objective C developer, and I am now learning Swift (of which I am finding quite difficult, not only because of new concepts, such as optionals, but also because Swift is continually evolving, and much of the available tutorials are severely outdated).
Currently I am trying parse a JSON from a url into an NSDictionary and then use one of its value to display an image (which is also a url). Something like this:
URL -> NSDictionary -> init UIImage from url -> display UIImage in UIImageView
This is quite easy in Objective C (and there may even be a shorter answer):
NSURL *url = [NSURL URLWithString:#"https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY"];
NSData *apodData = [NSData dataWithContentsOfURL:url];
NSDictionary *apodDict = [NSJSONSerialization JSONObjectWithData:apodData options:0 error:nil];
The above code snippet gives me back a standard NSDictionary, in which I can refer to the "url" key to get the address of the image I want to display:
"url" : "https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg"
This I then convert into a UIImage and give it to a UIImageView:
NSURL *imageURL = [NSURL URLWithString: [apodDict objectForKey:#"url"]];
NSData *imageData = [NSData dataWithContentsOfURL:imageURL];
UIImage *apodImage = [UIImage imageWithData:imageData];
UIImageView *apodView = [[UIImageView alloc] initWithImage: apodImage];
Now, I am basically trying to replicate the above Objective C code in Swift but continuously run into walls. I have tried several tutorials (one of which actually did the exact same thing: display a NASA image), as well as find a few stack overflow answers but none could help because they are either outdated or they do things differently than what I need.
So, I would like to ask the community to provide the Swift 4 code for the these problems:
1. Convert data from url into a Dictionary
2. Use key:value pair from dict to get url to display an image
If it is not too much already, I would also like to ask for detailed descriptions alongside the code because I would like the answer to be the one comprehensive "tutorial" for this task that I believe is currently not available anywhere.
Thank you!
First of all I'm pretty sure that in half a year you will find Objective-C very complicated and difficult. 😉
Second of all even your ObjC code is discouraged. Don't load data from a remote URL with synchronous Data(contentsOf method. Regardless of the language use an asynchronous way like (NS)URLSession.
And don't use Foundation collection types NSArray and NSDictionary in Swift. Basically don't use NS... classes at all if there is a native Swift counterpart.
In Swift 4 you can easily decode the JSON with the Decodable protocol directly into a (Swift) struct,
the URL string can be even decoded as URL.
Create a struct
struct Item: Decodable {
// let copyright, date, explanation: String
// let hdurl: String
// let mediaType, serviceVersion, title: String
let url: URL
}
Uncomment the lines if you need more than the URL.
And load the data with two data tasks.
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
let task = URLSession.shared.dataTask(with: url) { (data, _, error) in
if let error = error { print(error); return }
do {
let decoder = JSONDecoder()
// this line is only needed if all JSON keys are decoded
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Item.self, from: data!)
let imageTask = URLSession.shared.dataTask(with: result.url) { (imageData, _, imageError) in
if let imageError = imageError { print(imageError); return }
DispatchQueue.main.async {
let apodImage = UIImage(data: imageData!)
let apodView = UIImageView(image: apodImage)
// do something with the image view
}
}
imageTask.resume()
} catch { print(error) }
}
task.resume()
You can use this extension
extension UIImage {
public static func loadFrom(url: URL, completion: #escaping (_ image: UIImage?) -> ()) {
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
completion(UIImage(data: data))
}
} else {
DispatchQueue.main.async {
completion(nil)
}
}
}
}
}
Using
guard let url = URL(string: "http://myImage.com/image.png") else { return }
UIImage.loadFrom(url: url) { image in
self.photo.image = image
}
Since image loading is a trivial and at the same time task which could be implemented in many different ways, I would recommend you to not "reinvent the wheel" and have a look to an image loading library such as Nuke, since it already covers most of the cases you might need during your development process.
It allows you to load and show image asynchronously into your view, using simple api:
Nuke.loadImage(with: url, into: imageView)
And also if you need - to specify how image should be loaded and presented:
let options = ImageLoadingOptions(
placeholder: UIImage(named: "placeholder"),
failureImage: UIImage(named: "failure_image"),
contentModes: .init(
success: .scaleAspectFill,
failure: .center,
placeholder: .center
)
)
Nuke.loadImage(with: url, options: options, into: imageView)
Create an UIIimageView Extension and the following code
extension UIImageView {
public func imageFromServerURL(urlString: String) {
self.image = nil
let urlStringNew = urlString.replacingOccurrences(of: " ", with: "%20")
URLSession.shared.dataTask(with: NSURL(string: urlStringNew)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error as Any)
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
self.image = image
})
}).resume()
}}
and
self.UploadedImageView.imageFromServerURL(urlString: imageURLStirng!)
I have just extended on vadian's answer, separated some concerns to clearly understand the basics. His answer should suffice.
First, you have to build your structure. This will represent the JSON structure you retrieved from the webservice.
struct Item: Codable {
let url, hdurl : URL,
let copyright, explanation, media_type, service_version, title : String
}
Then make you request methods. I usually create a separate file for it. Now, vadian mentioned about completion handlers. These are represented by escaping closures. Here, closure ()-> is passed on both functions and called having the decoded data as argument.
struct RequestCtrl {
func fetchItem(completion: #escaping (Item?)->Void) {
let url = URL(string: "https://api.nasa.gov/planetary/apod?api_key=DEMO_KEY")!
//URLSessionDataTask handles the req and returns the data which you will decode based on the Item structure we defined above.
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
let jsonDecoder = JSONDecoder()
if let data = data,
let item = try? jsonDecoder.decode(Item.self, from: data){
//jsonDecoder requires a type of our structure represented by .self and the data from the request.
completion(item)
} else {
completion(nil)
}
}
task.resume()
}
func fetchItemPhoto(usingURL url: URL, completion: #escaping (Data?)-> Void) {
let task = URLSession.shared.dataTask(with: url) { (data, _, _) in
if let data = data { completion(data) } else { completion(nil) }
}
task.resume()
}
}
Now in you ViewController, call your request and handle the execution of your closure.
class ViewController: UIViewController {
let requestCtrl = RequestCtrl()
override func viewDidLoad() {
super.viewDidLoad()
requestCtrl.fetchItem { (fetchedItem) in
guard let fetchedItem = fetchedItem else { return }
self.getPhoto(with: fetchedItem)
}
}
func getPhoto(with item: Item) {
requestCtrl.fetchItemPhoto(usingURL: item.url) { (fetchedPhoto) in
guard let fetchedPhoto = fetchedPhoto else { return }
let photo = UIImage(data: fetchedPhoto)
//now you have a photo at your disposal
}
}
}
These are not the best of practices since I am also still learning, so by all means do some research on topics especially closures, ios concurrency and URLComponents on Apple's documentation :)
you need to convert url into string and data to add in imageview
let imageURL:URL=URL(string: YourImageURL)!
let data=NSData(contentsOf: imageURL)
Yourimage.image=UIImage(data: data! as Data)
First add the pod in Podfile
pod 'Alamofire',
pod 'AlamofireImage'
you can check this link for install pods => https://cocoapods.org/pods/AlamofireImage
// Use this function for load image from URL in imageview
imageView.af_setImage(
withURL: url,
placeholderImage: placeholderImage //its optional if you want to add placeholder
)
Check this link for method of alamofireImage
https://github.com/Alamofire/AlamofireImage/blob/master/Documentation/AlamofireImage%203.0%20Migration%20Guide.md
Update for Xcode 13.3 , Swift 5
To load the Image asynchronously from a URL string, use this extension:
extension UIImageView {
public func getImageFromURLString(imageURLString: String) {
guard let imageURL = URL(string: imageURLString) else { return}
Task {
await requestImageFromURL(imageURL)
}
}
private func requestImageFromURL(_ imageURL: URL) async{
let urlRequest = URLRequest(url: imageURL)
do {
let (data, response) = try await URLSession.shared.data(for: urlRequest)
if let httpResponse = response as? HTTPURLResponse{
if httpResponse.statusCode == 200{
print("Fetched image successfully")
}
}
// Loading the image here
self.image = UIImage(data: data)
} catch let error {
print(error)
}
}
}
Usage:
imageView.getImageFromURLString(imageURLString: "https://apod.nasa.gov/apod/image/1811/hillpan_apollo15_4000.jpg")
I have a TableView with ImageViews inside each cell. I want the images to get loaded once and remain like that but it seems that the images get loaded (downloaded, I'm getting them from an external API) as they get into visible area for user. It seems like a lazy load or something like that and I would like to disable it because if I scroll down then come back up most of the images get misplaced.
TableViewController.swift
cell?.mainChampImageView.image = businessLayer.getChampionThumbnailImage(championId: mainChampion.key)
BusinessLayer.swift
func getChampionThumbnailImage (championId: Int) -> UIImage {
return dataLayerRiot.getChampionThumbnailImage(championId: championId)
}
DataLayerRiot.swift
func getChampionThumbnailImage (championId: Int) -> UIImage {
var image: UIImage!
let urlString = ApiHelper.getChampionThumbnailImageApiLink(championId: championId)
let url = URL(string: urlString)
let session = URLSession.shared
let semaphore = DispatchSemaphore(value: 0)
session.dataTask(with: url!) {(data, response, error) in
if error != nil {
print("ERROR")
semaphore.signal()
}
else {
image = UIImage(data: data!)!
semaphore.signal()
}
}.resume()
semaphore.wait()
session.finishTasksAndInvalidate()
return image
}
Anyone know how to disable them loading as they get into visible area for the user and just have them "stored"?
EDIT
I am dequeuing the cell using the default way
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "Match", for: indexPath) as? TableViewCell
...
}
Is there a better way of doing it?
EDIT 2
I also need to specify that I am unable to install libraries because this is a university project and I am able to work only on university's MACs (because I don't own one) therefore I am unable to install packages without administrator privileges.
You should save a task at memory like:
let task = = session.dataTask() {}
And after you can cancel it anywhere by:
task.cancel()
Alternatively, if the object session is a URLSession instance, you can cancel it by:
session.invalidateAndCancel()
Try SDWebImage for lazy loading the images in the UITableViewCell or UICollectionViewCell. Install it through cocoapods into your project.
It is an asynchronous memory + disk image caching with automatic cache expiration handling.
https://github.com/SDWebImage/SDWebImage
Code:
let urlString = ApiHelper.getChampionThumbnailImageApiLink(championId: championId)
let url = URL(string: urlString)
cell?.mainChampImageView.sd_setImage(with: url, placeholderImage: UIImage(named: "placeholder.png"))
It sounds like you could benefit from doing some image caching. There are multiple ways to go about doing so, but from your example, it doesn't look like you need to go through the trouble of adding an entire library to do so. You can do it in a simple manner using NSCache.
I created a class called ImageCache, and in this case it is a singleton, so that the cache is accessible throughout the entire application.
import UIKit
class ImageCache: NSObject {
static let sharedImageCache = ImageCache()
// Initialize cache, specifying that your key type is AnyObject
// and your value type is AnyObject. This is because NSCache requires
// class types, not value types so we can't use <URL, UIImage>
let imageCache = NSCache<AnyObject, AnyObject>()
// Here we store the image, with the url as the key
func add(image: UIImage, for url: URL) {
// we cast url as AnyObject because URL is not a class type, it's a value type
imageCache.setObject(image, forKey: url as AnyObject)
}
// This allows us to access the image from cache with the URL as the key
// (e.g. cache[URL])
func fetchImage(for url: URL) -> UIImage? {
var image: UIImage?
// Casting url for the same reason as before, but we also want the result
// as an image, so we cast that as well
image = imageCache.object(forKey: url as AnyObject) as? UIImage
return image
}
}
So now we have some relatively simple caching in place. Now for how to use it:
func getChampionThumbnailImage (championId: Int) -> UIImage {
var image: UIImage!
let urlString = ApiHelper.getChampionThumbnailImageApiLink(championId: championId)
let url = URL(string: urlString)
// Before, downloading the image, we check the cache to see if it exists and is stored.
// If so, we can grab that image from the cache and avoid downloading it again.
if let cachedImage = ImageCache.sharedImageCache.fetchImage(for: url) {
image = cachedImage
return image
}
let session = URLSession.shared
let semaphore = DispatchSemaphore(value: 0)
session.dataTask(with: url!) {(data, response, error) in
if error != nil {
print("ERROR")
semaphore.signal()
}
else {
image = UIImage(data: data!)!
// Once the image is successfully downloaded the first time, add it to
// the cache for later retrieval
ImageCache.sharedImageCache.add(image: image, for: url!)
semaphore.signal()
}
}.resume()
semaphore.wait()
session.finishTasksAndInvalidate()
return image
}
The reason the images are re-downloading is because a table view doesn't have unlimited cells. What happens is, as you scroll down, the cells that go off the screen are then recycled and re-used, so when you scroll back up, the images have to be grabbed again because they've been emptied out.
You can avoid downloading the images again by implementing caching.
Another way you can avoid having incorrect images is setting your image view to nil before you re-download the image. For example:
cell?.mainChampImageView = nil
cell?.mainChampImageView.image = businessLayer.getChampionThumbnailImage(championId: mainChampion.key)
All of the above, along with making sure that you are dequeuing cells properly should address your issue.
I'm using UIImageView.image in order to change the visible image on my screen.
iv.image = images[index]
The array 'images' is currently filled with local image files. However, I wish to download images from my server and then append them to the array.
private var images = [img1, img2, img3]
I have been recommended using SDWebImage (particularly SDWebImageManager or SDWebImageDownloader) to do this, however, when exploring the download and caching tutorials, all of them downloaded to a UIImageView. I cannot pass in a UIImageView into the .image extension. I couldn't find any tutorials or examples to help me achieve this. I am fairly new to swift so I do not have a vast amount of experience or understanding.
You can donwload image Async without any library as well.
Following will create a extension for imageView and it will async down image from the server just pass the respective url as parameter it will return an image.
extension UIImageView {
public func imageFromServerURL(urlString: String) {
URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error!)
return
}
DispatchQueue.main.async(execute: { () -> Void in
if let imageData = data {
let image = UIImage(data: imageData)
self.image = image
}
})
}).resume()
}
}
// And you can call like this where ever required
cell.cellImageView.imageFromServerURL(urlString: banner.imageURL)
I have images in my collectionViewCell's that are fetched and parsed via NSURLRequest, how do I cache these images so they don't have to start a new request with every single appearance/disappearance of the view?
here is my code that fetches the images:
class funnyPicture: NSObject {
var pfPicture : PFObject
var coverImage : UIImage!
init(pfPicture: PFObject) {
self.pfPicture = pfPicture
}
func fetchCoverImage(completion: (image: UIImage?, error: NSError?) -> Void) {
let urlString = self.pfPicture["funnyPictures"] as! String
let url = NSURL(string: urlString)
let request = NSURLRequest(URL: url!)
let queue = dispatch_get_main_queue()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) { (response: NSURLResponse?, data: NSData?, error: NSError?) in
if error == nil {
self.coverImage = UIImage(data: data!)
completion(image: self.coverImage, error: nil)
} else {
completion(image: nil, error: error)
}
}
}
}
and here is my collectionView code that parse the images to the collectionViewCell's:
override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCellWithReuseIdentifier("Cell", forIndexPath: indexPath) as! MyCollectionViewCell
// Configure the cell
let book = self.books[indexPath.row]
let coverImage = book.coverImage
if coverImage == nil {
book.fetchCoverImage({ (image, error) -> Void in
if self.collectionView != nil {
collectionView.reloadItemsAtIndexPaths([indexPath])
}
})
} else {
dispatch_async(dispatch_get_main_queue()){
let imageView = cell.imageView
imageView.image = book.coverImage
}
};
if book.coverImage == nil {
cell.imageView.userInteractionEnabled = false
cell.userInteractionEnabled = false
}else {
cell.imageView.userInteractionEnabled = true
cell.userInteractionEnabled = true
}
return cell
}
While I've received references to third party frameworks, I haven't received any answer on how to implement them with the code I have provided in the question, or even an answer using apples already implemented caching mechanism.. The reason I put the code in the question was for use in an answer.. Thank you.
Here is an example for your collection view cell:
import UIKit
let imageCache = NSCache<AnyObject, AnyObject>.sharedInstance
class myCell: UICollectionViewCell {
#IBOutlet public weak var myImageView: UIImageView?
private var imageUrlString: String?
private var downloadTask: URLSessionDownloadTask?
public var imageURL: URL? {
didSet {
self.downloadItemImageForSearchResult(imageURL: imageURL)
}
}
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
public func downloadItemImageForSearchResult(imageURL: URL?) {
if let urlOfImage = imageURL {
if let cachedImage = imageCache.object(forKey: urlOfImage.absoluteString as NSString){
self.myImageView!.image = cachedImage as? UIImage
} else {
let session = URLSession.shared
self.downloadTask = session.downloadTask(
with: urlOfImage as URL, completionHandler: { [weak self] url, response, error in
if error == nil, let url = url, let data = NSData(contentsOf: url), let image = UIImage(data: data as Data) {
DispatchQueue.main.async() {
let imageToCache = image
if let strongSelf = self, let imageView = strongSelf.myImageView {
imageView.image = imageToCache
imageCache.setObject(imageToCache, forKey: urlOfImage.absoluteString as NSString , cost: 1)
}
}
} else {
//print("ERROR \(error?.localizedDescription)")
}
})
self.downloadTask!.resume()
}
}
}
override public func prepareForReuse() {
self.downloadTask?.cancel()
myImageView?.image = UIImage(named: "ImagePlaceholder")
}
deinit {
self.downloadTask?.cancel()
myImageView?.image = nil
}
}
Don't forget to make an extension for NSCache
Like this:
import Foundation
extension NSCache {
class var sharedInstance: NSCache<NSString, AnyObject> {
let cache = NSCache<NSString, AnyObject>()
return cache
}
}
Use NSCache and NSOperationQueue to manage your image loading. There's a good post outlining the technique at https://stackoverflow.com/a/12721899/5271191 (It's Objective-C, but the technique is the same for Swift.)
I highly recommend you to use a clean in place replacement/extension for UIImageView, that will manage caching of the image all transparently to you and avoid unwanted complexity of maintaining operation queues, etc.
If in memory caching suffices your needs - check this out-
https://github.com/nicklockwood/AsyncImageView
If you want persistent caching, then this one will do-
https://github.com/rs/SDWebImage
HTH.
I have images in my collectionViewCell's that are fetched and parsed
via NSURLRequest, how do I cache these images so they don't have to
start a new request with every single appearance/disappearance of the
view?
The URL loading system already provides a cache. Take a look at the docs for NSURLCache. If the resources you need aren't already being sufficiently cached, you probably only need to adjust the disk space allocated to the URL cache for your app.
You should also take a look at the headers (cache-control, expires, etc.) that come back with your resources to make sure that they're not preventing caching. Here's a short tutorial on cache-related headers.
You should use a specialized framework for that. I would not recommend using SDWebImage, it is very outdated and is not stable.
Take a look at those two libraries that are up to date with iOS platform:
DFImageManager - advanced framework written in Objective-C but featuring nullability annotations (works great with Swift). Here's a list of things that make it better, than SDWebImage. Disclosure: it's written by me, opinion might be biased.
Kingfisher - lightweight library written in Swift. Similar to SDWebImage, but has much less features that SDWebImage and DFImageManager.
I have created a library using swift 2 to do the request for image and cache it. it's very simple just give it a try.
https://github.com/georgehadly/GHImageCaching
all you can do is something like this ,
viewImg.getCachedImage("geo", URI: NSURL(string: "https://s-media-cache-ak0.pinimg.com/236x/8e/5a/98/8e5a98795dc2c5322cac97343a6cad6d.jpg")!) { (done) -> Void in
if(done){
// your extra
}
}
in case you want to delete all cached images
UIImageView.deleteAllCaching()