How to load image in swift using Alamofire - swift

I have a collection view controller, which load image Async by URL.
(Something like Instegram)
I am looking for the best way to implement the part of the loading image.
please tell me what do you think
First way - without any external library:
let downloadQueue = dispatch_queue_create("com.pro.asyncImages",nil)
dispatch_async(downloadQueue){
var data = NSData(contentsOfURL: NSURL(string: pictureUrl!)!)
var image: UIImage?
if (data != nil){
image = UIImage(data: data!)
}
dispatch_async(dispatch_get_main_queue()){
uiImageView.image = image
}
}
Second way - using Alamofire (the request belong to Alamofire)
if let mediaUrl = info.mediaUrl {
request(.GET,mediaUrl).response(){
(_, _, data, _) in
let image = UIImage(data: data! as NSData)
imageView.image = image
}
}
And lastly, I read that AFNetworking, is doing a great job in loading url async to urlImage, Which this is what I want to do.
So is AFNetworking is better for this task? (then Alamofire)
and if not, I did not understand how to add AFNetworking to my swift project, beside adding the #import “AFNetworking.h” which files do I need to add?
please explain which method is the best, my needs are performance, accusation, caching.
the view collection controller acts like Instegram and is loading images, while scrolling down.
I hope I was clear enough about what I need,
thank you

As authors mention in Alamofire README.md:
When should I use AFNetworking?
UIKit extensions, such as asynchronously loading images to UIImageView
So answer to your question :
So is AFNetworking is better for this task?
Yes!
But if still want to use vanilla Alamofire project, you can fetch image this way:
Alamofire.request(.GET, "https://robohash.org/123.png").response { (request, response, data, error) in
self.myImageView.image = UIImage(data: data, scale:1)
}
P.S.
But if you just want to load image (of course async) - you don't need to use Alamofire or AFNetworking at all.
Just add this small extension in your code:
extension UIImageView {
public func imageFromUrl(urlString: String) {
if let url = NSURL(string: urlString) {
let request = NSURLRequest(URL: url)
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue()) {
(response: NSURLResponse!, data: NSData!, error: NSError!) -> Void in
self.image = UIImage(data: data)
}
}
}
}
And use it:
myImageView.imageFromUrl("https://robohash.org/123.png")

I created a Swift version of UIImageView+AFNetworking here : Github.
You don't have to use AFNetworking (and a bridging header) or Alamofire in your project. Just add the file to your project and use it.
Example usage:
myImageView.setImageWithUrl(imageUrl, placeHolderImage: somePlaceHolderImage)

Taken from Alamofire/README.md
"In order to keep Alamofire focused specifically on core networking implementations, additional component libraries have been created by the Alamofire Software Foundation to bring additional functionality to the Alamofire ecosystem.
AlamofireImage - An image library including image response serializers, UIImage and UIImageView extensions, custom image filters, an auto-purging in-memory cache and a priority-based image downloading system."
https://github.com/Alamofire/AlamofireImage

In swift 5 & Alamofire 5,You can use like bellow..
AF.request( "https://robohash.org/123.png",method: .get).response{ response in
switch response.result {
case .success(let responseData):
self.myImageView.image = UIImage(data: responseData!, scale:1)
case .failure(let error):
print("error--->",error)
}
}

If you want to use AFNetworking's UImageView framework, you have to do #import "UIImageView+AFNetworking.h" in your bridge-header file.
AFNetworking has a shared cache, so you won’t need to manually cache or cancel your requests. The setImageWithURL from AFNetworking can easily convert a URL to an image in ImageView.
Sample code using UIImage+AFNetworking:
cell.imageView.setImageWithURL(NSURL(string:imageSource), placeholderImage: UIImage(named:"placeholder"))
If you are using Alamofire, you have to manually convert NSData to UIImage. For Alamofire, you can create a NSCache() object to cache your images. The performance of both library should be similar. You can use Alamofire for your server API calls and then use AFNetworking to display images asynchronously.
Sample code using Alamofire for image cache:
let imageCache = NSCache()
if let image = self.imageCache.objectForKey(imageURL) as? UIImage {
cell.imageView.image = image
} else {
// 3
cell.imageView.image = nil
// 4
cell.request = Alamofire.request(.GET, imageURL).validate(contentType: ["image/*"]).responseImage() {
(request, _, image, error) in
if error == nil && image != nil {
// 5
self.imageCache.setObject(image!, forKey: request.URLString)
// 6
if request.URLString == cell.request?.request.URLString {
cell.imageView.image = image
}
} else {
/*
If the cell went off-screen before the image was downloaded, we cancel it and
an NSURLErrorDomain (-999: cancelled) is returned. This is a normal behavior.
*/
}
}
}
You can read this tutorial for more detail about using Alamofire.

Here's an approach using RxAlamofire:
import Alamofire
import RxAlamofire
extension UIImageView {
public func imageFromUrl(urlString: String) {
requestData(.GET, urlString)
.observeOn(MainScheduler.instance)
.subscribeNext { self.image = UIImage(data: $0.1) }
}
}

Actually there are multiple ways to load your image from URL using Alamofire.
and this is a way that i've developed and i'm using it already in one of my Swift projects. This way i'm loading the images Asyncrounsly and display them into UITableView and from performance wise it's fine and there is no blocking while scrolling UITableView because it's care about cashing the images loaded.
First you have to create a GET request using Alamofire to download the image, and you have to use Request+AlamofireImage from AlamofireImage Library so you have to
import AlamofireImage
And then in
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
}
you have to call your image download request like this:
//Download image
// We should perform this in a background thread
Alamofire.request(.GET, deshItem.dish_imageUrl!)
.responseImage { response in
debugPrint(response)
print(response.request)
print(response.response)
debugPrint(response.result)
if let image = response.result.value {
print("image downloaded: \(image)")
// Store the commit date in to our cache
self.ImageCache[dishName!] = image
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
let dishImageView:UIImageView = cellToUpdate.viewWithTag(104) as! UIImageView
dishImageView.image = image
}
})
}
}
For Image Caching i've created a dictionary that will contain image for every cell or item(String), So this way of caching images will avoid the locking that appear while scrolling.
Complete Sample code:
import AlamofireImage
class MainFeedTableViewController: UITableViewController, FloatRatingViewDelegate {
var ImageCache = [String:UIImage]()
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MainFeedCellID", forIndexPath: indexPath)
// Configure the cell...
let dishName = deshItem.dish_name
//This is a workaround to cash the image and improve the performance while user scrolling UITableView.
// If this image is already cached, don't re-download
if let dishImage = ImageCache[dishName!] {
let dishImageView:UIImageView = cell.viewWithTag(104) as! UIImageView
dishImageView.image = dishImage
}
else {
//Download image
// We should perform this in a background thread
Alamofire.request(.GET, deshItem.dish_imageUrl!)
.responseImage { response in
debugPrint(response)
print(response.request)
print(response.response)
debugPrint(response.result)
if let image = response.result.value {
print("image downloaded: \(image)")
// Store the commit date in to our cache
self.ImageCache[dishName!] = image
// Update the cell
dispatch_async(dispatch_get_main_queue(), {
if let cellToUpdate = tableView.cellForRowAtIndexPath(indexPath) {
let dishImageView:UIImageView = cellToUpdate.viewWithTag(104) as! UIImageView
dishImageView.image = image
}
})
}
}
}
return cell
}

You can download image by using responseData(queue:completionHandler:)
SWIFT 4
func downloadImages(imageURL: String) {
Alamofire.request(imageURL, method: .get)
.validate()
.responseData(completionHandler: { (responseData) in
self.yourImageVIew.image = UIImage(data: responseData.data!)
DispatchQueue.main.async {
// Refresh you views
}
})
}

I have developed a small and straightforward CocoaPods library to work with Alamofire and Images.
CocoaPods is a great way of addressing your library dependencies needs. It is easy to install once, and easy to add and update dependencies to your projects.
The library is open source and can be found at https://github.com/gchiacchio/AlamoImage
It has great documentation with examples, so you will be enjoying it in minutes.
I have it working in a couple of project myself, including collection views and scrolling.

it is recommended to use urlsession as urlconnection deprecated in swift 5
public func imageFromUrl(urlString: String){
if let url = URL(string: urlString) {
let request = URLRequest(url: url)
let session = URLSession(configuration: .default)
session.dataTask(with: request,completionHandler: {(data,response,error) in
self.image = UIImage(data: data!)
})
}
}

Related

how to display image in collection view using mockapi? [duplicate]

In Swift 3, I am trying to capture an image from the internet, and have these lines of code:
var catPictureURL = NSURL(fileURLWithPath: "http://i.imgur.com/w5rkSIj.jpg")
var catPictureData = NSData(contentsOf: catPictureURL as URL) // nil
var catPicture = UIImage(data: catPictureData as! Data)
What am I doing wrong here?
There's a few things with your code as it stands:
You are using a lot of casting, which is not needed.
You are treating your URL as a local file URL, which is not the case.
You are never downloading the URL to be used by your image.
The first thing we are going to do is to declare your variable as let, as we are not going to modify it later.
let catPictureURL = URL(string: "http://i.imgur.com/w5rkSIj.jpg")! // We can force unwrap because we are 100% certain the constructor will not return nil in this case.
Then we need to download the contents of that URL. We can do this with the URLSession object. When the completion handler is called, we will have a UIImage downloaded from the web.
// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)
// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
let image = UIImage(data: imageData)
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
Finally you need to call resume on the download task, otherwise your task will never start:
downloadPicTask.resume().
All this code may look a bit intimidating at first, but the URLSession APIs are block based so they can work asynchronously - If you block your UI thread for a few seconds, the OS will kill your app.
Your full code should look like this:
let catPictureURL = URL(string: "http://i.imgur.com/w5rkSIj.jpg")!
// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)
// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
let image = UIImage(data: imageData)
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadPicTask.resume()
let url = URL(string: "http://i.imgur.com/w5rkSIj.jpg")
let data = try? Data(contentsOf: url)
if let imageData = data {
let image = UIImage(data: imageData)
}
Swift
Good solution to extend native functionality by extensions
import UIKit
extension UIImage {
convenience init?(url: URL?) {
guard let url = url else { return nil }
do {
self.init(data: try Data(contentsOf: url))
} catch {
print("Cannot load image from url: \(url) with error: \(error)")
return nil
}
}
}
Usage
Convenience initializer is failable and accepts optional URL – approach is safe.
imageView.image = UIImage(url: URL(string: "some_url.png"))
You could also use Alamofire\AlmofireImage for that task:
https://github.com/Alamofire/AlamofireImage
The code should look something like that (Based on the first example on link above):
import AlamofireImage
Alamofire.request("http://i.imgur.com/w5rkSIj.jpg").responseImage { response in
if let catPicture = response.result.value {
print("image downloaded: \(image)")
}
}
While it is neat yet safe, you should consider if that worth the Pod overhead.
If you are going to use more images and would like to add also filter and transiations I would consider using AlamofireImage
Use this extension and download image faster.
extension UIImageView {
public func imageFromURL(urlString: String) {
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicator.frame = CGRect.init(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
activityIndicator.startAnimating()
if self.image == nil{
self.addSubview(activityIndicator)
}
URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error ?? "No Error")
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
activityIndicator.removeFromSuperview()
self.image = image
})
}).resume()
}
}
Using Alamofire worked out for me on Swift 3:
Step 1:
Integrate using pods.
pod 'Alamofire', '~> 4.4'
pod 'AlamofireImage', '~> 3.3'
Step 2:
import AlamofireImage
import Alamofire
Step 3:
Alamofire.request("https://httpbin.org/image/png").responseImage { response in
if let image = response.result.value {
print("image downloaded: \(image)")
self.myImageview.image = image
}
}
The easiest way according to me will be using SDWebImage
Add this to your pod file
pod 'SDWebImage', '~> 4.0'
Run pod install
Now import SDWebImage
import SDWebImage
Now for setting image from url
imageView.sd_setImage(with: URL(string: "http://www.domain/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
It will show placeholder image but when image is downloaded it will show the image from url .Your app will never crash
This are the main feature of SDWebImage
Categories for UIImageView, UIButton, MKAnnotationView adding web image and cache management
An asynchronous image downloader
An asynchronous memory + disk image caching with automatic cache expiration handling
A background image decompression
A guarantee that the same URL won't be downloaded several times
A guarantee that bogus URLs won't be retried again and again
A guarantee that main thread will never be blocked
Performances!
Use GCD and ARC
To know more https://github.com/rs/SDWebImage
Use extension for UIImageView to Load URL Images.
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func imageURLLoad(url: URL) {
DispatchQueue.global().async { [weak self] in
func setImage(image:UIImage?) {
DispatchQueue.main.async {
self?.image = image
}
}
let urlToString = url.absoluteString as NSString
if let cachedImage = imageCache.object(forKey: urlToString) {
setImage(image: cachedImage)
} else if let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
DispatchQueue.main.async {
imageCache.setObject(image, forKey: urlToString)
setImage(image: image)
}
}else {
setImage(image: nil)
}
}
}
}
We are able to fetch image directly without using Third Party SDK like 'AlamofireImage', 'Kingfisher' and 'SDWebImage'
Swift 5
DispatchQueue.global(qos: .background).async {
do{
let data = try Data.init(contentsOf: URL.init(string:"url")!)
DispatchQueue.main.async {
let image: UIImage? = UIImage(data: data)
yourImageView.image = image
}
}
catch let errorLog {
debugPrint(errorLog.localizedDescription)
}
}
let url = ("https://firebasestorage.googleapis.com/v0/b/qualityaudit-678a4.appspot.com/o/profile_images%2FBFA28EDD-9E15-4CC3-9AF8-496B91E74A11.png?alt=media&token=b4518b07-2147-48e5-93fb-3de2b768412d")
self.myactivityindecator.startAnimating()
let urlString = url
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url)
{
(data, response, error) in
if error != nil {
print("Failed fetching image:", error!)
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
print("error")
return
}
DispatchQueue.main.async {
let image = UIImage(data: data!)
let myimageview = UIImageView(image: image)
print(myimageview)
self.imgdata.image = myimageview.image
self.myactivityindecator.stopanimating()
}
}.resume()
I use AlamofireImage it works fine for me for Loading url within ImageView, which also has Placeholder option.
func setImage (){
let image = “https : //i.imgur.com/w5rkSIj.jpg”
if let url = URL (string: image)
{
//Placeholder Image which was in your Local(Assets)
let image = UIImage (named: “PlacehoderImageName”)
imageViewName.af_setImage (withURL: url, placeholderImage: image)
}
}
Note:- Dont forget to Add AlamofireImage in your Pod file as well as in Import Statment
Say Example,
pod 'AlamofireImage' within Your PodFile and in ViewController import AlamofireImage

How to save multi images from URLs to local storage in swift?

I am developing Restaurant app.
There are 340 foods data.
I am getting this data from backend that developed with Laravel.
App is working well in online.
But, although network is turned off, All foods data should be displayed in app.
So, I tried to save foods data to local.
It is good to save text data to local(exactly, UserDefaults and FileSystem).
But, when I try to save images to local from urls, It occur error and don't save to local exactly.
Have you ever seen such problems?
If yes, I appreciate your help.
Thanks
I don't know exactly what error you faced, but I think the answer to the link can help you.
How do I make JSON data persistent for offline use (Swift 4)
Additionally, image data would be good to be cached.
If you communicate based on URLSession, you can process caching as below.
Saving an Image to a Cache
Cache.imageCache.setObject(image, forKey: url.absoluteString as NSString)
Bring up cached images
let cacheImage = Cache.imageCache.object(forKey: url.absoluteString as NSString)
Code
class Cache {
static let imageCache = NSCache<NSString, UIImage>()
}
extension UIImageView {
func imageDownload(url: URL, contentMode mode: UIView.ContentMode = .scaleAspectFit) {
if let cacheImage = Cache.imageCache.object(forKey: url.absoluteString as NSString) {
DispatchQueue.main.async() { [weak self] in
self?.contentMode = mode
self?.image = cacheImage
}
}
else {
var request = URLRequest(url: url)
request.httpMethod = "GET"
URLSession.shared.dataTask(with: request) { data, response, error in
guard
let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200,
let mimeType = response?.mimeType, mimeType.hasPrefix("image"),
let data = data, error == nil,
let image = UIImage(data: data)
else {
print("Download image fail : \(url)")
return
}
DispatchQueue.main.async() { [weak self] in
print("Download image success \(url)")
Cache.imageCache.setObject(image, forKey: url.absoluteString as NSString)
self?.contentMode = mode
self?.image = image
}
}.resume()
}
}
}
To make caching more convenient, use a library called Kingfisher.
Kingfisher
You need to manage lazy loadings, you can use SDWebImages instead of manage it manually
SDWebImages will automatically manage your images caches and will also load them without internet here is a simple usage of it
add pod in podfile
pod 'SDWebImage'
usagae:
import SDWebImage
imageView.sd_setImage(with: URL(string: "http://www.example.com/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))

Why does it take so long for the images to load into my tableView if the image files being downloaded have a maximum size of 30kb? [duplicate]

I have an extension to print image URL on UIImageView. But I think the problem is my tableView is so slow because of this extension. I think I need to open thread for it. How can I create a thread in this extension or do you know another solution to solve this problem?
My code :
extension UIImageView{
func setImageFromURl(stringImageUrl url: String){
if let url = NSURL(string: url) {
if let data = NSData(contentsOf: url as URL) {
self.image = UIImage(data: data as Data)
}
}
}
}
You can use the frameworks as suggested here, but you could also consider "rolling your own" extension as described in this article
"All" you need to do is:
Use URLSession to download your image, this is done on a background thread so no stutter and slow scrolling.
Once done, update your image view on the main thread.
Take one
A first attempt could look something like this:
func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
guard let url = URL(string: urlString) else {
return
}
//Fetch image
URLSession.shared.dataTask(with: url) { (data, response, error) in
//Did we get some data back?
if let data = data {
//Yes we did, update the imageview then
let image = UIImage(data: data)
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume() //remember this one or nothing will happen :)
}
And you call the method like so:
loadImage(fromURL: "yourUrlToAnImageHere", toImageView: yourImageView)
Improvement
If you're up for it, you could add a UIActivityIndicatorView to show the user that "something is loading", something like this:
func loadImage(fromURL urlString: String, toImageView imageView: UIImageView) {
guard let url = URL(string: urlString) else {
return
}
//Add activity view
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
imageView.addSubview(activityView)
activityView.frame = imageView.bounds
activityView.translatesAutoresizingMaskIntoConstraints = false
activityView.centerXAnchor.constraint(equalTo: imageView.centerXAnchor).isActive = true
activityView.centerYAnchor.constraint(equalTo: imageView.centerYAnchor).isActive = true
activityView.startAnimating()
//Fetch image
URLSession.shared.dataTask(with: url) { (data, response, error) in
//Done, remove the activityView no matter what
DispatchQueue.main.async {
activityView.stopAnimating()
activityView.removeFromSuperview()
}
//Did we get some data back?
if let data = data {
//Yes we did, update the imageview then
let image = UIImage(data: data)
DispatchQueue.main.async {
imageView.image = image
}
}
}.resume() //remember this one or nothing will happen :)
}
Extension
Another improvement mentioned in the article could be to move this to an extension on UIImageView, like so:
extension UIImageView {
func loadImage(fromURL urlString: String) {
guard let url = URL(string: urlString) else {
return
}
let activityView = UIActivityIndicatorView(activityIndicatorStyle: .gray)
self.addSubview(activityView)
activityView.frame = self.bounds
activityView.translatesAutoresizingMaskIntoConstraints = false
activityView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
activityView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
activityView.startAnimating()
URLSession.shared.dataTask(with: url) { (data, response, error) in
DispatchQueue.main.async {
activityView.stopAnimating()
activityView.removeFromSuperview()
}
if let data = data {
let image = UIImage(data: data)
DispatchQueue.main.async {
self.image = image
}
}
}.resume()
}
}
Basically it is the same code as before, but references to imageView has been changed to self.
And you can use it like this:
yourImageView.loadImage(fromURL: "yourUrlStringHere")
Granted...including SDWebImage or Kingfisher as a dependency is faster and "just works" most of the time, plus it gives you other benefits such as caching of images and so on. But I hope this example shows that writing your own extension for images isn't that bad...plus you know who to blame when it isn't working ;)
Hope that helps you.
I think, that problem here, that you need to cache your images in table view to have smooth scrolling. Every time your program calls cellForRowAt indexPath it downloads images again. It takes time.
For caching images you can use libraries like SDWebImage, Kingfisher etc.
Example of Kingfisher usage:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "identifier", for: indexPath) as! CustomCell
cell.yourImageView.kf.setImage(with: URL)
// next time, when you will use image with this URL, it will be taken from cache.
//... other code
}
Hope it helps
Your tableview slow because you load data in current thread which is main thread. You should load data other thread then set image in main thread (Because all UI jobs must be done in main thread). You do not need to use third party library for this just change your extension with this:
extension UIImageView{
func setImageFromURl(stringImageUrl url: String){
if let url = NSURL(string: url) {
DispatchQueue.global(qos: .default).async{
if let data = NSData(contentsOf: url as URL) {
DispatchQueue.main.async {
self.image = UIImage(data: data as Data)
}
}
}
}
}
}
For caching image in background & scroll faster use SDWebImage library
imageView.sd_setImage(with: URL(string: "http://image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
https://github.com/rs/SDWebImage

Swift: Display Image from URL

In Swift 3, I am trying to capture an image from the internet, and have these lines of code:
var catPictureURL = NSURL(fileURLWithPath: "http://i.imgur.com/w5rkSIj.jpg")
var catPictureData = NSData(contentsOf: catPictureURL as URL) // nil
var catPicture = UIImage(data: catPictureData as! Data)
What am I doing wrong here?
There's a few things with your code as it stands:
You are using a lot of casting, which is not needed.
You are treating your URL as a local file URL, which is not the case.
You are never downloading the URL to be used by your image.
The first thing we are going to do is to declare your variable as let, as we are not going to modify it later.
let catPictureURL = URL(string: "http://i.imgur.com/w5rkSIj.jpg")! // We can force unwrap because we are 100% certain the constructor will not return nil in this case.
Then we need to download the contents of that URL. We can do this with the URLSession object. When the completion handler is called, we will have a UIImage downloaded from the web.
// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)
// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
let image = UIImage(data: imageData)
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
Finally you need to call resume on the download task, otherwise your task will never start:
downloadPicTask.resume().
All this code may look a bit intimidating at first, but the URLSession APIs are block based so they can work asynchronously - If you block your UI thread for a few seconds, the OS will kill your app.
Your full code should look like this:
let catPictureURL = URL(string: "http://i.imgur.com/w5rkSIj.jpg")!
// Creating a session object with the default configuration.
// You can read more about it here https://developer.apple.com/reference/foundation/urlsessionconfiguration
let session = URLSession(configuration: .default)
// Define a download task. The download task will download the contents of the URL as a Data object and then you can do what you wish with that data.
let downloadPicTask = session.dataTask(with: catPictureURL) { (data, response, error) in
// The download has finished.
if let e = error {
print("Error downloading cat picture: \(e)")
} else {
// No errors found.
// It would be weird if we didn't have a response, so check for that too.
if let res = response as? HTTPURLResponse {
print("Downloaded cat picture with response code \(res.statusCode)")
if let imageData = data {
// Finally convert that Data into an image and do what you wish with it.
let image = UIImage(data: imageData)
// Do something with your image.
} else {
print("Couldn't get image: Image is nil")
}
} else {
print("Couldn't get response code for some reason")
}
}
}
downloadPicTask.resume()
let url = URL(string: "http://i.imgur.com/w5rkSIj.jpg")
let data = try? Data(contentsOf: url)
if let imageData = data {
let image = UIImage(data: imageData)
}
Swift
Good solution to extend native functionality by extensions
import UIKit
extension UIImage {
convenience init?(url: URL?) {
guard let url = url else { return nil }
do {
self.init(data: try Data(contentsOf: url))
} catch {
print("Cannot load image from url: \(url) with error: \(error)")
return nil
}
}
}
Usage
Convenience initializer is failable and accepts optional URL – approach is safe.
imageView.image = UIImage(url: URL(string: "some_url.png"))
You could also use Alamofire\AlmofireImage for that task:
https://github.com/Alamofire/AlamofireImage
The code should look something like that (Based on the first example on link above):
import AlamofireImage
Alamofire.request("http://i.imgur.com/w5rkSIj.jpg").responseImage { response in
if let catPicture = response.result.value {
print("image downloaded: \(image)")
}
}
While it is neat yet safe, you should consider if that worth the Pod overhead.
If you are going to use more images and would like to add also filter and transiations I would consider using AlamofireImage
Use this extension and download image faster.
extension UIImageView {
public func imageFromURL(urlString: String) {
let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .gray)
activityIndicator.frame = CGRect.init(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
activityIndicator.startAnimating()
if self.image == nil{
self.addSubview(activityIndicator)
}
URLSession.shared.dataTask(with: NSURL(string: urlString)! as URL, completionHandler: { (data, response, error) -> Void in
if error != nil {
print(error ?? "No Error")
return
}
DispatchQueue.main.async(execute: { () -> Void in
let image = UIImage(data: data!)
activityIndicator.removeFromSuperview()
self.image = image
})
}).resume()
}
}
Using Alamofire worked out for me on Swift 3:
Step 1:
Integrate using pods.
pod 'Alamofire', '~> 4.4'
pod 'AlamofireImage', '~> 3.3'
Step 2:
import AlamofireImage
import Alamofire
Step 3:
Alamofire.request("https://httpbin.org/image/png").responseImage { response in
if let image = response.result.value {
print("image downloaded: \(image)")
self.myImageview.image = image
}
}
The easiest way according to me will be using SDWebImage
Add this to your pod file
pod 'SDWebImage', '~> 4.0'
Run pod install
Now import SDWebImage
import SDWebImage
Now for setting image from url
imageView.sd_setImage(with: URL(string: "http://www.domain/path/to/image.jpg"), placeholderImage: UIImage(named: "placeholder.png"))
It will show placeholder image but when image is downloaded it will show the image from url .Your app will never crash
This are the main feature of SDWebImage
Categories for UIImageView, UIButton, MKAnnotationView adding web image and cache management
An asynchronous image downloader
An asynchronous memory + disk image caching with automatic cache expiration handling
A background image decompression
A guarantee that the same URL won't be downloaded several times
A guarantee that bogus URLs won't be retried again and again
A guarantee that main thread will never be blocked
Performances!
Use GCD and ARC
To know more https://github.com/rs/SDWebImage
Use extension for UIImageView to Load URL Images.
let imageCache = NSCache<NSString, UIImage>()
extension UIImageView {
func imageURLLoad(url: URL) {
DispatchQueue.global().async { [weak self] in
func setImage(image:UIImage?) {
DispatchQueue.main.async {
self?.image = image
}
}
let urlToString = url.absoluteString as NSString
if let cachedImage = imageCache.object(forKey: urlToString) {
setImage(image: cachedImage)
} else if let data = try? Data(contentsOf: url), let image = UIImage(data: data) {
DispatchQueue.main.async {
imageCache.setObject(image, forKey: urlToString)
setImage(image: image)
}
}else {
setImage(image: nil)
}
}
}
}
We are able to fetch image directly without using Third Party SDK like 'AlamofireImage', 'Kingfisher' and 'SDWebImage'
Swift 5
DispatchQueue.global(qos: .background).async {
do{
let data = try Data.init(contentsOf: URL.init(string:"url")!)
DispatchQueue.main.async {
let image: UIImage? = UIImage(data: data)
yourImageView.image = image
}
}
catch let errorLog {
debugPrint(errorLog.localizedDescription)
}
}
let url = ("https://firebasestorage.googleapis.com/v0/b/qualityaudit-678a4.appspot.com/o/profile_images%2FBFA28EDD-9E15-4CC3-9AF8-496B91E74A11.png?alt=media&token=b4518b07-2147-48e5-93fb-3de2b768412d")
self.myactivityindecator.startAnimating()
let urlString = url
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url)
{
(data, response, error) in
if error != nil {
print("Failed fetching image:", error!)
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
print("error")
return
}
DispatchQueue.main.async {
let image = UIImage(data: data!)
let myimageview = UIImageView(image: image)
print(myimageview)
self.imgdata.image = myimageview.image
self.myactivityindecator.stopanimating()
}
}.resume()
I use AlamofireImage it works fine for me for Loading url within ImageView, which also has Placeholder option.
func setImage (){
let image = “https : //i.imgur.com/w5rkSIj.jpg”
if let url = URL (string: image)
{
//Placeholder Image which was in your Local(Assets)
let image = UIImage (named: “PlacehoderImageName”)
imageViewName.af_setImage (withURL: url, placeholderImage: image)
}
}
Note:- Dont forget to Add AlamofireImage in your Pod file as well as in Import Statment
Say Example,
pod 'AlamofireImage' within Your PodFile and in ViewController import AlamofireImage

Swift: image cache not working as expected

I am trying to use image cache in swift but nothing works as expected. I have tried NSCache and AlamofireImage but both didn't work for me. I did not get any error with code but when I tried to fetch image from cache it always returns nil.
I guess the image is not saved in cache. Can someone here share some links with example on how to use image caching in swift or ios?
What you can do is download images in background thread. When image is downloaded save it an Object and update you'r main thread too. For displaying image next time you can directly load image from the cache.
Below I'm sharing my code
var imageCache = NSMutableDictionary()
if let img = imageCache.valueForKey("key") as? UIImage{
imageView.image = img
}
else{
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(NSURL(string: imgURL!)!, completionHandler: { (data, response, error) -> Void in
if(error == nil){
let img = UIImage(data: data!)
imageCache.setValue(img, forKey: "key") // Image saved for cache
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Update your image view
})
}
})
task.resume()
}
Hope so this may help you
You can use this as a disk and memory cache, if you implemented downloading image mechanism. You can specify cache size, expire time
https://github.com/huynguyencong/DataCache
Or you can use Kingfisher as a image caching library