Search code examples
swiftfirebaseuiimagepickercontrollerfirebase-storage

Data uploads to Firebase Storage but Doesn't updateValue of child node


My issue is that Im Uploading an image to Firebase storage but the URL is not updated to a child node in my database so I can't reference the image.

I've checked to make sure that I'm entering my database values correctly and updating child nodes seems to work outside of that specific function. I tried to add a dispatch queue since it is an async function but I'm not having any success.

func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
        var selectedImageFromPicker: UIImage?
        if let originalImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
            selectedImageFromPicker = originalImage
            print("The photo is \(originalImage)")
        }
        if let selectedImage = selectedImageFromPicker, let cell = selectedCell {
            cell.imageContainer.image = selectedImage
            print("My image is now \(String(describing: cell.imageContainer.image))")
            guard let user = Auth.auth().currentUser else {return}
            let uid = user.uid
            let imageName = NSUUID().uuidString
            let storageRef = Storage.storage().reference().child("\(imageName).png")
            if let uploadData = cell.imageContainer.image!.pngData() {
            storageRef.putData(uploadData, metadata: nil, completion:
                { (metadata, error) in
                    if error != nil {
                        print(error as Any)
                        return
                    }
// Function doesn't seem to behave properly after this line
                    storageRef.downloadURL(completion: { (url, error) in
                        if let error = error {
                            print(error.localizedDescription)
                            return
                        } else {
                            let downloadUrl = url?.absoluteString
                            var valueKey = ""
                            if cell.activeCell == "0" { valueKey = "imageOne" } else if cell.activeCell == "1" {
                                valueKey = "imageTwo"} else if cell.activeCell == "2" { valueKey = "imageThree"} else if cell.activeCell == "3" { valueKey = "imageFour" } else if cell.activeCell == "4" { valueKey = "imageFive" } else if cell.activeCell == "5" { valueKey = "imageSix"} else { valueKey = "imageSeven" }

                            print("the node is titled \(valueKey)")
                            self.value = [valueKey: downloadUrl] as [String : AnyObject]
                            print("Here it is \(self.value) for \(uid)")
                        }
                    })

                    self.updateImageData(uid: uid, values: self.value)
                    print(metadata as Any)
                })
            }
            collectionView.reloadData()
        }
        dismissView()
    }

func updateImageData(uid: String, values: [String: AnyObject]) {
        Database.database().reference().child("profile_images").child(uid).updateChildValues(values as [AnyHashable : Any], withCompletionBlock: { (error, ref) in
            if let error  = error {
                print("Failed to update database values with error", error.localizedDescription)
                return
            }
        })
    }

the expected results are to see the URL link as the value to the corresponding key inside the database.


Solution

  • The storageRef.downloadURL method calls out to the server, and may take some time to complete. Instead of blocking the code and making the app unresponsive to your users, your main code continues, and immediately calls self.updateImageData(uid: uid, values: self.value) without the correct URL. Then when the server has returned the correct download URL, your completion handler runs and sets self.value.

    For this reason, all code that requires the download URL should be in the completion handler of storageRef.downloadURL.

    Something like:

    storageRef.downloadURL(completion: { (url, error) in
        if let error = error {
            print(error.localizedDescription)
            return
        } else {
            let downloadUrl = url?.absoluteString
            var valueKey = ""
            if cell.activeCell == "0" { valueKey = "imageOne" } else if cell.activeCell == "1" {
                valueKey = "imageTwo"} else if cell.activeCell == "2" { valueKey = "imageThree"} else if cell.activeCell == "3" { valueKey = "imageFour" } else if cell.activeCell == "4" { valueKey = "imageFive" } else if cell.activeCell == "5" { valueKey = "imageSix"} else { valueKey = "imageSeven" }
    
            self.value = [valueKey: downloadUrl] as [String : AnyObject]
    
            self.updateImageData(uid: uid, values: self.value)
        }
    })