0

I want to download images from server and display in UICollectionView. First time when user connect with internet than all images will download in background and display from local directory when user is offline. I am using alamofire to download the images. Firstly i am checking existence of image, if it is not already dowloaded than i download it. The problem is that the album is not showing when it is already downloaded. I do not know how. Here is my code:

   import UIKit
   import Alamofire

   var myurl : URL!
   var imageName : String!
   var bool = false

   let docsurl = try! FileManager.default.url(for:.documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)

    override func viewDidAppear(_ animated: Bool) {
            super.viewDidAppear(animated)
             if (background_imageurl.count > 0) {
                 if Reachability.isConnectedToNetwork() == true{
                    downloadAllImages(urlArray : background_imageurl)
                    }
                }
            }
     func downloadAllImages(urlArray:[String])->Void{
                 for i in 0 ..< urlArray.count  {
                       let fullName    =  urlArray[i]
                       let fullNameArr = (fullName as AnyObject).components(separatedBy: "//")
                       let imgname = fullNameArr[1]

                       let tempimgname    = imgname
                       let tempimgname2 = tempimgname.components(separatedBy: "/")

                        imageName = tempimgname2[4]

                        myurl  = docsurl.appendingPathComponent("\("guidedCellImages")/\(self.imageName!)")

                        print("\n myurl", myurl)

                        if FileManager.default.fileExists(atPath: myurl.path, isDirectory: &bool),bool.boolValue  {
                        print("\n fileExists", myurl.path)
                         }else{
                         downloadFile(url: urlArray[i] as! String)
                              }
                        }
                }

      func downloadFile(url: String)->Void{

             let destination: (URL, HTTPURLResponse) -> (URL, DownloadRequest.DownloadOptions) = {
        (temporaryURL, response) in 
         let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        let filePath = directoryURL?.appendingPathComponent("\("guidedCellImages")/\(self.imageName!)")
        return (filePath!, [.removePreviousFile, .createIntermediateDirectories])
    }

      let utilityQueue = DispatchQueue.global(qos: .utility)
      print("url", url)

        Alamofire.download(
        url,
        method: .get,
        encoding: JSONEncoding.default,
        to: destination)

        .downloadProgress(queue: utilityQueue) { progress in       
        }
        .response(completionHandler: { (DefaultDownloadResponse) in

        if (self.urlArray.count > 0){
            self.urlArray.removeFirst()
            print("self.urlArray", self.urlArray.count)
        }

        if DefaultDownloadResponse.response?.statusCode == 200 { 
                print(DefaultDownloadResponse.destinationURL!)
            }
        })
     }

  override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! CollectionViewCell

    myurl  = docsurl.appendingPathComponent("\("guidedCellImages")")

    if FileManager.default.fileExists(atPath: myurl.path, isDirectory: &bool),bool.boolValue  {

        let directoryContents = try! fileManager.contentsOfDirectory(at: myurl, includingPropertiesForKeys: nil)
        print("\ndirectoryContents", directoryContents)
        for imageURL in directoryContents where imageURL.pathExtension == "png" {
            if let image = UIImage(contentsOfFile: imageURL.path) {

                cell.tab1GuidedimageView.image = image
            } else {
                fatalError("Can't create image from file \(imageURL)")
            }
        }
    }else{
    if (background_imageurl.count > 0 ){
    cell.tab1imageView.sd_setImage(with: URL(string: background_imageurl[indexPath.row]), placeholderImage: UIImage(named: "background"),options: .refreshCached)
    }

}

    return cell
}
Chetan Lodhi
  • 308
  • 1
  • 3
  • 18

3 Answers3

1

Try this below procedure, this might helps you

struct Animal{
    var name: String
    var url: String
    var image: UIImage?
}

extension Animal{
    init(info: [String: String]) {
        self.name = info["name"]!
        self.url = info["url"]!
    }
}

class CollectionViewCell{
    @IBOutlet weak var imageView: UIImageView!
    @IBOutlet weak var label: UILabel!
}

class ViewController: UIViewController{
    var animals = [Animal]()

    override func viewDidLoad(){
        super.viewDidLoad()
    }

    func getAnimals(){
        // hit server api to get the images
        // assuming that the following json is coming from server
        let jsonResponse = [["name":"Dog","url":"https://animals.com/images/image/dog.jpeg"],
                            ["name":"Lion","url":"https://animals.com/images/image/lion.jpeg"],
                            ["name":"Tiger","url":"https://animals.com/images/image/tiger.jpeg"],
                            ["name":"Horse","url":"https://animals.com/images/image/horse.jpeg"],
                            ["name":"Elephant","url":"https://animals.com/images/image/elephant.jpeg"]]
        for animal in jsonResponse {
            let lAnimal = Animal(info: animal)

            // get locally saved image initially from collectionview cell, if it is existed then add it to your response model
            let directoryURL = getDocumentsDirectory()
            let imageURL = URL(string: lAnimal.url)
            let imagePath = directoryURL.appendingPathComponent("animals/\(imageURL.lastPathComponent)")
            if fileManager.fileExistsAtPath(imagePAth){
                // pass locallay saved image path
                lAnimal.image = UIImage(contentsOfFile: imagePAth)
            }else{
                print("image needs to be downloaded")
            }
        }
    }

    func getDocumentsDirectory() -> URL {
        let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
        return directoryURL!
    }
}

extension ViewController: UICollectionViewDataSource{

    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return self.animals.count
    }

    override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell : CollectionViewCell = collectionView.dequeueReusableCell(withReuseIdentifier: "reuseIdentifier", for: indexPath) as! CollectionViewCell
        let animal = self.animals[indexPath.row]
        cell.label.text = animal.name
        if let animalImage = animal.image{
            //if animal image existis from local them simply display it
            cell.imageView.image = animalImage
        }else{
            //download image from server using simple url task or by using alamofire
            let imageURL = URL(string: animal.url)!
            let task = URLSession.shared.dataTask(with: imageURL, completionHandler: { (data, response, error) in
                if let lData = data {
                    let image = UIImage(data: lData)
                    cell.imageView.image = image
                    let filename = getDocumentsDirectory().appendingPathComponent("animals/\(imageURL.lastPathComponent)")
                    try? lData.write(to: filename)

                    //update local data model object
                    animal.image = image
                }
                if let lError = error{
                    /** Handle session error ..................... **/
                }
            })
        }
        return cell
    }
}
Jayachandra A
  • 968
  • 7
  • 18
0

The issue seems with self.imageName. When you are downloading image, the imageName Would have changed in the for loop. Make sure to generate the image name each time from url. while downloading and saving and also while checking.

In fact you can change the scope of imageName variable from global to local.

Recommended is write function to get the image name to avoid redundancy.

EDIT

The guidedCellImages folder must exists, just by adding guidedCEllImages will not create the folder automatically. make sure to add slash (/) before the guidedCellImages

Please check how to create the folder inside document directory here

Hope it helps..!!!

Van
  • 1,033
  • 9
  • 17
  • when the response comes to this statement print(DefaultDownloadResponse.destinationURL!), it is printing 36 times when I've 18 items in the array and it is not printing any statement in cellForItemAt IndexPath when the user is offline. – Chetan Lodhi Feb 01 '18 at 12:55
  • can you check once whether guidedCellImages named folder is created in document directory? – Van Feb 01 '18 at 13:01
  • else you will be need to create the one before you save image – Van Feb 01 '18 at 13:01
  • Yes folder is created with this root path : file:///var/mobile/Containers/Data/Application/C1EE516C-2397-4080-BC0F-4B5146005C1B/Documents/guidedCellImages – Chetan Lodhi Feb 01 '18 at 13:05
  • ok, but is this ur printed path or did you check the directory in finder? check with simulator, so that you will be able to see the actual directory created – Van Feb 01 '18 at 13:25
  • check my edit to create the folder inside document directory. you are need to create directory first with createDirectoryAtPath.. but add path till guidedCellImages only – Van Feb 01 '18 at 13:27
0

Try this code

func downloadFile(url: String)-> Void {

    let directoryURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
    let filePath = directoryURL?.appendingPathComponent("\("guidedCellImages")/\(self.imageName!)")


    let data    =   NSData(contentsOf: URL(string: url)!)
    data?.write(toFile: filePath, atomically: true)

}
Vinod Rathod
  • 552
  • 2
  • 6
  • 22
  • I have an array of string, will this method help me to download them asynchronously? – Chetan Lodhi Feb 01 '18 at 14:01
  • you are downloading this images async but inside the `downloadFile` method you have missed writing this downloaded image data to the file inside the disk. Just because of that you are not able to see the images inside the disk getting saved. – Vinod Rathod Feb 01 '18 at 14:04