Search code examples
iosfirebaseswift3grand-central-dispatchdispatch-async

Swift 3 DispatchQueue.global (download images from Firebase and UI frozen)


I have a problema with this method:

func DownloadImages(uid: String, indice: Int) {    
    DispatchQueue.global(qos: .background).async {
        let refBBDD = FIRDatabase.database().reference().child("users").child(uid)
        refBBDD.observeSingleEvent(of: .value, with: { snapshot in
            let snapshotValue = snapshot.value as? NSDictionary
            let profileImageUrl = snapshotValue?.value(forKey: "profileImageUrl") as! String

            let storage = FIRStorage.storage()
            var reference: FIRStorageReference!

            if(profileImageUrl == "") {
                return
            }
            print("before")

            reference = storage.reference(forURL: profileImageUrl)
            reference.downloadURL { (url, error) in
                let data = NSData(contentsOf: url!)
                let image = UIImage(data: data! as Data)

                print("image yet dowload ")
                self.citas[indice].image = image

                DispatchQueue.main.async(execute: { () -> Void in
                    self.tableView.reloadRows(at: [IndexPath(row: indice, section: 0)], with: .none)
                    //self.tableView.reloadData()
                    print("image loaded")
                })
            }
            print("after")
        })
    }
}

I want to download images in background mode. I want follow using app, but the UI has frozen until methods not entry in reloadRows.

Is it possible run in true background mode and can i follow using the app??

Trace program:

before
after
before
after
...
before
after
before
after
image yet dowload  --> here start UI frozen
image loaded
image yet dowload 
image yet dowload 
...
image yet dowload 
image yet dowload 
image loaded  
image loaded
...
image loaded
image loaded
image yet dowload ------> here UI is not frozen
image loaded

Solution

  • The problem is caused by this line: let data = NSData(contentsOf: url!). That constructor of NSData should only be used to read the contents of a local URL (a file path on the iOS device), since it is a synchronous method and hence if you call it to download a file from a URL, it will be blocking the UI for a long time.

    I have never used Firebase, but looking at the documentation, it seems to me that you are using the wrong method to download that file. You should be using func getData(maxSize size: Int64, completion: @escaping (Data?, Error?) -> Void) -> StorageDownloadTask instead of func downloadURL(completion: @escaping (URL?, Error?) -> Void), since as the documentation states, the latter only "retrieves a long lived download URL with a revokable token", but doesn't download the contents of the URL itself.

    Moreover, you shouldn't be force unwrapping values in the completion handler of a network request. Network requests can often fail for reasons other than a programming error, but if you don't handle those errors gracefully, your program will crash.