How to asynchronous load image from a web-server in UICollectionView using NSCache

I have some issues when loading images from a web-server in UICollectionView using NScache.

The problem:

The images are not proper displayed:

  1. sometimes they are not showned in the corresponding cell

or

  1. the image is changing on scroll

Situation:

  1. I have 3 arrays whitch are properly loaded from the web-server in function viewDidLoad(). These arrays are: vPrice, vTitle and vImages_api

  2. my custom class for cell have:

    • label for price: cell.lblPrice

    • label for title: cell.lblTitle

    • image: cell.imgPicture

I belive that the problem is in function func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell

It could be related either to the way I use NSCache or to the way I use and when I use DispatchQueue.

The code:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = self.collectionViewPRODUSE.dequeueReusableCell(withReuseIdentifier: "customCell", for: indexPath) as! clsCustomCell

    cell.lblPrice.text = vPrice[indexPath.item]
    cell.lblTitle.text = vTitle[indexPath.item]

    cell.layer.borderColor = UIColor.lightGray.cgColor
    cell.layer.borderWidth = 0.5

    DispatchQueue.global(qos: .userInitiated).async {
        //background thread
        let ImageString = self.vImages_api[indexPath.item]
        let imageUrl = URL(string: ImageString)
        let imageData = NSData(contentsOf: imageUrl!)

        // main thread to update the UI
        DispatchQueue.main.async {
            let key1 = self.vImages_api[indexPath.item] as AnyObject
            //if i saved allready my image in cache, then i will load my image from cache
            if let imageFromCache = self.objCache.object(forKey: key1){
                cell.imgPicture.image = imageFromCache as! UIImage
            }
            else{//if my image is not in cache  ......
                        if imageData != nil {
                            let myPicture = UIImage(data: imageData! as Data)
                            cell.imgPicture.image = myPicture                 

                            //save my image in cache
                            self.objCache.setObject(myPicture!, forKey: ImageString as AnyObject)
                        }
            }
        }
    }

    return cell
}

Edited code - version II:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = self.collectionViewPRODUSE.dequeueReusableCell(withReuseIdentifier: "MyCustomCell", for: indexPath) as! clsCustomCell

    cell.lblPret.text = vPrice[indexPath.item]
    cell.lblTitlu.text = vTitle[indexPath.item]

    cell.layer.borderColor = UIColor.lightGray.cgColor
    cell.layer.borderWidth = 0.5
    let key1 = self.vImages_api[indexPath.item] as AnyObject

    if let imageFromCache = self.objCache.object(forKey: key1){
        cell.imgPicture.image = imageFromCache as! UIImage
    }else{
        DispatchQueue.global(qos: .background).async {
            let ImageString = self.vImages_api[indexPath.item]
            let imageUrl = URL(string: ImageString)
            let imageData = NSData(contentsOf: imageUrl!)
            let  myPicture = UIImage(data: imageData! as Data)
            self.objCache.setObject(poza!, forKey: ImageString as AnyObject)
                DispatchQueue.main.async {
                    if imageData != nil {
                        cell.imgPicture.image = myPicture
                    }
                }
        }

    }

    return cell
}

Edited code - version III

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = self.collectionViewPRODUSE.dequeueReusableCell(withReuseIdentifier: "celula_custom", for: indexPath) as! clsCustomCell
    cell.lblPret.text = vPrice[indexPath.item]
    cell.lblTitlu.text = vTitle[indexPath.item]

  NKPlaceholderImage(image: UIImage(named: "loading"), imageView: cell.imgPicture, imgUrl:  self.vImages_api[indexPath.item]
    ) { (image11) in
        cell.imgPicture.image = image11
    }
    cell.layer.borderColor = UIColor.lightGray.cgColor
    cell.layer.borderWidth = 0.5

    return cell
}

Try this one it's Working code (Swift 4).

func NKPlaceholderImage(image:UIImage?, imageView:UIImageView?,imgUrl:String,compate:@escaping (UIImage?) -> Void){

    if image != nil && imageView != nil {
        imageView!.image = image!
    }

    var urlcatch = imgUrl.replacingOccurrences(of: "/", with: "#")
    let documentpath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
    urlcatch = documentpath + "/" + "\(urlcatch)"

    let image = UIImage(contentsOfFile:urlcatch)
    if image != nil && imageView != nil
    {
        imageView!.image = image!
        compate(image)

    }else{

        if let url = URL(string: imgUrl){

            DispatchQueue.global(qos: .background).async {
                () -> Void in
                let imgdata = NSData(contentsOf: url)
                DispatchQueue.main.async {
                    () -> Void in
                    imgdata?.write(toFile: urlcatch, atomically: true)
                    let image = UIImage(contentsOfFile:urlcatch)
                    compate(image)
                    if image != nil  {
                        if imageView != nil  {
                            imageView!.image = image!
                        }
                    }
                }
            }
        }
    }
}

Use Like this :

// Here imgPicture = your imageView and UIImage(named: "placeholder") is Display image brfore download actual image.  
imgPicture.image = nil 
NKPlaceholderImage(image: UIImage(named: "placeholder"), imageView: imgPicture, imgUrl: "Put Here your server image Url Sting") { (image) in }