Got nil when download image link from firebase - swift

I try to upload multiple images from library to Firebase storage. The upload process works perfectly, all the images is on Firebase storage but when i try to download the image url link to save in database, it shows nil. Please help me how to fix this? Thank a lot
for imageData in ArrayMedia {
print(imageData)
let filePath = "\(currentUserId)/\(key)"
let data = UIImageJPEGRepresentation(imageData, 0.3)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
storageReference.child(filePath).putData(data!, metadata: metadata, completion: { (metaData, error) in
print(metadata)
if error != nil {
print(error?.localizedDescription as Any)
} else {
let PhotoUrl = metadata.downloadURL()?.absoluteString // GOT NIL IN THIS LINE
print(PhotoUrl)
let value = ["Username" : currentUserName, "UserId" : currentUserId, "Text" : self.tvPost.text] as [String : Any]
databaseReference.child("PendingPost").child(currentUserId).child(key).updateChildValues(value)
databaseReference.child("PendingPost").child(currentUserId).child(key).child("Images").child(key).setValue(["PhotoUrl": PhotoUrl])
}
self.showAlert()
})
}

Try to use this instead :
storageRef.child(path).putData(data, metadata: metaData){ (metaData,error) in
if let error = error {
print(error.localizedDescription)
return
} else {
let downloadURL = metaData!.downloadURL()!.absoluteString
}
}
It works for me.

Related

Swift: upload multiple images to Firestore and assign URLs to object

I'm making an app whereby users post 2 images. I'm using Firebase for storage and as my database.
In my method to upload the images what I had wanted to do was to essentially use this method to return the URLs separately as well. I had written the following:
private func uploadImage(image: UIImage) -> URL? {
let randomName = UUID()
let storageRef = storage.reference().child("\(randomName)/png")
guard let uploadData = image.pngData() else { return nil}
var imageUrl: URL?
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
if error != nil {
print(error?.localizedDescription)
} else {
imageUrl = url
}
}
}
return imageUrl
}
And then I wrote the following 'post' method which is run when the submit button is tapped:
#objc func post() {
if let question = questionText.text,
let hashtagText = hashtagTextField.text,
let userHandle = Auth.auth().currentUser?.email,
let firstImage = left.image,
let secondImage = right.image,
let firstImageURL = uploadImage(image: firstImage)?.absoluteString,
let secondImageURL = uploadImage(image: secondImage)?.absoluteString
{
db.collection("posts").addDocument(data: [
"firstImage" : firstImageURL,
"secondImage" : secondImageURL,
"question" : question,
"hashtagText" : hashtagText,
"userHandle" : userHandle
]) { (error) in
if let e = error {
print("There was an issue saving data to Firestore, \(e)")
} else {
print("Successfully saved data")
self.dismiss(animated: true, completion: nil)
}
}
}
}
However, obviously the first method is not going to work as the closure is run after imageUrl is returned, therefore returning nil.
I've been trying to figure out how to manage this scenario - I had considered using a loop to populate an array of images but this got messy and I'm sure it is not the standard way to handle this. Any help would be greatly appreciated.
The return imageUrl is in the wrong place. It will return before Firebase has had time to store the image and return the url.
Additionally, the name of the file is not going to work. You currently have
storage.reference().child("\(randomName)/png") // xxxxx/png?
when it should be
storage.reference().child("\(randomName).png") // xxxxx.png
You can't 'return' data from a Firebase closure because firebase is asynchronous - a completion handler may possibly be a solution, but we don't know what the total use case is.
Let's assume you want want to store a users vacation picture in storage and then store that url in Firestore
private func uploadImage(image: UIImage) {
guard let uid = Auth.auth().currentUser?.uid else { return } //this users uid
let storageRef = storage.reference().child(uid).child("vacation.png")
//the path will be storage/users uid/vacation.png
guard let uploadData = image.pngData() else { return nil}
storageRef.putData(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
}
storageRef.downloadURL { (url, error) in
if error != nil {
print(error?.localizedDescription)
} else {
if url != nil {
//it's here where we store the imageUrl in Firestore
let dict = ["theUrl": url?.absoluteURL)]
let userRef = self.db.collection("users").document(uid)
//self.db points to *my* Firestore
userRef.collection("my_pics").addDocument(data: dict)
//will store in firstore/users/uid/docId/theUrl: the url
}
}
}
}
}

How can I upload an Image to Firebase with different strings?

In this code Im able to successfully post an image to Firebase. But I would like to add some strings along with it.
#objc func Post(){
print("Post Clicked")
let imageName = "\(NSUUID().uuidString).jpeg"
let imagesFolder = Storage.storage().reference().child("Post")
if let image = imageView.image{
if let imageData : Data = image.jpegData(compressionQuality: 1){
imagesFolder.child(imageName).putData(imageData, metadata: nil) { (metaData, error) in
if let error = error{
print(error)
}else{
imagesFolder.child(imageName).downloadURL { (url, error) in
if let imageURL = url?.absoluteString{
self.imageURL = imageURL
print("Success")
}
}
}
}
}
}
}
How can I upload these custom strings from UILabels along with the same image? I have tried placing this code in different areas of the Post function but it does not work. What is the proper way to do this?
//([
//"Title" : titleLabel.text,
//"Article" : articleLabel.text,
//"Author" : authorLabel.text,
//"Date" : DateLabel.text,
//])

Having trouble understanding why my app crashes

I'm trying to upload an image to the Firebase storage. This is what I have so far:
fileprivate func uploadImageToFirebase(){
let storageRef = Storage.storage().reference()
guard let uploadData = imageToUpload?.pngData() else { return }
storageRef.putData(uploadData, metadata: nil) { (metadata, err) in
if err != nil {
print(err?.localizedDescription)
return
}
print(metadata)
}
}
uploadData is not nil, but my app crashes. this is what I get:
reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]'
I tried to look up, but couldn't understand why the app crash and how to fix it.
I got it that I'm trying to insert a nil, but I don't have anything nil.
I got it to work, this is what I have now:
fileprivate func uploadImageToFirebase(completion: #escaping((_ url: String?)->())){
let imageName = UUID().uuidString
let storageRef = Storage.storage().reference()
let imagesRef = storageRef.child(imageName)
guard let uploadData = imageToUpload?.jpegData(compressionQuality: 0.8) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpeg"
imagesRef.putData(uploadData, metadata: metaData) { (metadata, err) in
if err == nil && metadata != nil {
imagesRef.downloadURL(completion: { (url, err) in
guard let downloadUrl = url else { return }
let urlString = downloadUrl.absoluteString
completion(urlString)
})
}
completion(nil)
}
}
EDIT:
The problem with the code in the question was there was no name assigned to the file to upload. In other words
let storageRef = Storage.storage().reference()
is a reference to the storage itself, but when uploading to storage there needs to be a filename like this
let storageRef = Storage.storage().reference()
let myFile = storageRef.child("my_cool_pic.jpg")
and the use is
myFile.putData(uploadData, metadata: nil)...

Trying to return downloadUrl from Firebase storage using a function

I'm trying to create a function that uploads images to Firebase Storage and returns the download url for their path so I can use it other parts of the app.
This is what the function looks like:
func uploadImage(to reference:StorageReference, image:UIImage) -> URL? {
let imageData = UIImageJPEGRepresentation(image, 0.2)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
var downloadURL = metadata.downloadURL()
reference.putData(imageData!, metadata: metadata) { (metadata, error) in
if error != nil {
print("Couldnt upload due to \(String(describing: error))")
}
downloadURL = metadata?.downloadURL()
}
return downloadURL!
}
I can't seem to get the result that I want as downloadUrl always returns nil. What am I doing wrong?
The problem here is that your function is returning before the upload is complete. In other words your function needs to return a callback, rather than a plain URL. Something like -
func uploadImage(to reference:StorageReference, image:UIImage, completion: #escaping (URL?) -> Void) {
let imageData = UIImageJPEGRepresentation(image, 0.2)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
var downloadURL = metadata.downloadURL()
reference.putData(imageData!, metadata: metadata) { (metadata, error) in
if error != nil {
print("Couldnt upload due to \(String(describing: error))")
completion(nil)
} else {
if let downloadUrl = metadata?.downloadURL() {
completion(downloadUrl)
} else {
completion(nil)
}
}
}
}

Firebase upload image

I'm trying to upload an image to Firebase storage using following code:
let storageRef = FIRStorage.storage().reference()
let imageRef = storageRef.child("AAA").child("BBB").child("CCC.jpeg")
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpeg"
if let uploadData = UIImageJPEGRepresentation(sampleImage, 0.5){
imageRef.put(uploadData, metadata: metaData, completion: { (metaData, error) in
print("finished")
if error != nil{
print(error!.localizedDescription)
}else{
print("success")
}
})
}else{
print("Cannot convert image to JPEG format")
}
The problem is that The above code doesn't do anything, in other words, nothing prints out. I have tried uploading strings to Firebase database, and it succeed. I've also checked the rules of Firebase storage:
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read: if true;
allow write: if request.auth != null;
}
}
}
So, what did i do wrong? Is there anything that I need to set up? Please help!!
Disclaimer: Let's just say my middle name isn't SwiftLint.
Try this, and make sure your database rules are similar as well. You should get a link in your database that will point you to the image in storage.
if let theData = UIImageJPEGRepresentation(sampleImage, 0.5) {
self.helper(photoData:theData)
}
// ^^ this can happen elsewhere ^^
func upload(photoData:Data) {
let imgName = Int(round(Date().timeIntervalSince1970)
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
let imageRef = FIRStorage.storage().reference().child("AAA/BBB/\(imgName).jpg")
imageRef.put(photoData, metadata: metadata) { (metadata, error) in
if let error = error {
print("Error uploading: \(error)")
return
}
let data:[String:Any] = [
"timeStamp":"\(Date())",
"image":metadata!.downloadURL()!.absoluteString
]
let dbRef = FIRDatabase.database().reference()
dbRef.child("imageUploads").childByAutoId().setValue(data)
}
}
Well, both of my original solution and #MGTLA solution are correct, the problem turns out to be the while loop after uploading image...which is unrelated to this topic.