How to load image in swift using Alamofire

2019-01-22 07:03发布

问题:

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

回答1:

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")


回答2:

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)


回答3:

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



回答4:

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.



回答5:

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) }
    }
}


回答6:

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
}


回答7:

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.



回答8:

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
            }
        })
}