Search code examples
iosswiftfirebasefirebase-storageprofile-picture

How to prevent constantly downloading an image from Firebase and show the image even if there is no internet connection?


Every time I show the profile picture, the UIImageView flashes to signify that the image was just downloaded from the Firebase Storage URL. This download speed differs based on the device type, some times it is unnoticeable while other times there is a significant delay.

I have attempted to cache the image with NSCache and the Kingfisher library but I still see the UIImageView flash rather than remain there every time I reopen the app.

My last attempt was to save the image to the document directory and then retrieve it from there but I still see the image flash. I would also like the profile picture to remain there even if the application is opened without any internet connection.

func saveImageDocumentDirectory(imgUrl: URL){
    let fileManager = FileManager.default
    let paths =     (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("proPic.png")
    let data = (try? Data(contentsOf: imgUrl))
    let image = UIImage(data: data!)
    print("\n\(paths)\n")
    let imageData = image!.pngData()
    fileManager.createFile(atPath: paths as String, contents: imageData, attributes: nil)
}


func getDirectoryPath() -> String {
    let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    let documentsDirectory = paths[0]
    return documentsDirectory
}

func getImage(){
    let fileManager = FileManager.default
    let imagePAth = (self.getDirectoryPath() as NSString).appendingPathComponent("proPic.png")
    if fileManager.fileExists(atPath: imagePAth){
        self.profilePic.image = UIImage(contentsOfFile: imagePAth)
    }else{
        print("\nNo Image\n")
    }
}

func createDirectory(){
    let fileManager = FileManager.default
    let paths = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent("customDirectory")
    if !fileManager.fileExists(atPath: paths){
        try! fileManager.createDirectory(atPath: paths, withIntermediateDirectories: true, attributes: nil)
    }else{
        print("\nAlready dictionary created.\n")
    }
}

And I would call the function by:

func getEmailPic(){


    guard let uid = Auth.auth().currentUser?.uid else {return}

    //receive the location of the profile pic
    let storageRef = Storage.storage().reference().child(uid).child("profilePic.png");

    //how to access the downloadURL
    _ = storageRef.downloadURL(completion: { (URLe, error) in
    if let error = error{

        //error handling
        print("\nCould not download user's profile image from url. 
     Error: \(error.localizedDescription)\n");
        return;
    }

            self.createDirectory()
            self.saveImageDocumentDirectory(imgUrl: URLe!)
            print("\nThis is the URL: \(URLe)\n")
            self.getImage()

    })

}

in viewDidLoad.


Solution

  • Using kingfisher for image caching, Try this and feel free to ask if facing any issue

        override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        // set profile image if you have url saved in userdefaults
        let imageUrl = getUrlImageFromUserDefaults()
        let placeholderImage = UIImage(named: "placeholder")
        profileImageView.kf.setImage(with: imageUrl, placeholder: placeholderImage)
        getEmailPic()
    }
    
    func getUrlImageFromUserDefaults() -> URL?{
        // save image URL to userdefault and fetch here
        let userdefaults = UserDefaults.standard
    
        return userdefaults.url(forKey: "profileURL")
    }
    func getEmailPic(){
    
    
        guard let uid = Auth.auth().currentUser?.uid else {return}
    
        //receive the location of the profile pic
        let storageRef = Storage.storage().reference().child(uid).child("profilePic.png");
    
        //how to access the downloadURL
        _ = storageRef.downloadURL(completion: { (URLe, error) in
            if let error = error{
    
                //error handling
                print("\nCould not download user's profile image from url.
                    Error: \(error.localizedDescription)\n");
                    return;
            }
    
            if URLe == getUrlImageFromUserDefaults() {
                // if url is same no need to set again
            }else{
                // set profile image
                let placeholderImage = UIImage(named: "placeholder")
                profileImageView.kf.setImage(with: URLe, placeholder: placeholderImage)
                // and again save this new URL to userdefaults
            }
    
        })
    
    }