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.
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)
}
})