url coming back from firebase storage is nil Swift4 - ios

I'm trying to upload an image to firebase storage and get the download url to save it in database, but I get a nil value and the function returns upon checking it. I followed the documentation and solutions from other posts and I can't see where I'm mistaking.
The function is :
func uploadImage(completed: #escaping (Bool) -> (),_ image: UIImage ){
print(" ############## UPLOAD STARTED ###########")
// let stringURL: String?
// guard let uid = Auth.auth().currentUser?.uid else {return} // use the userUid to sign the alert
// Create a root reference
let storageRef = Storage.storage().reference()
// Create a reference to "mountains.jpg"
// let alertsRef = storageRef.child("userAlertImages.jpg")//("user/\(uid)") // change path for userAlertImages path
// Create a reference to 'images/mountains.jpg'
let alertsImagesRef = storageRef.child("Alert Images/userAlertImages.jpg")
// While the file names are the same, the references point to different files
// alertsRef.name == alertsImagesRef.name; // true
// alertsRef.fullPath == alertsImagesRef.fullPath; // false
let imageData = UIImageJPEGRepresentation(image, 0.5)
let metaData = StorageMetadata()
metaData.contentType = " jpeg " // data type
metaData.customMetadata = ["k1": "",
"k2" : " ",
"k3" : "",
"k4" : ""]
alertsImagesRef.putData(imageData! as Data , metadata: metaData) { metaData, error in
if(error != nil){
print(error as Any)
return
}
}
// Fetch the download URL
alertsImagesRef.downloadURL { (url,error) in
guard let downloadURL = url else {
print("########## downloaded url is: \(url) #############")
return
}
NewMapViewController.alertImageURL = (url?.absoluteString) ?? ""
// NewMapViewController.alertImageURL = (downloadURL)
print("######### url is:\(String(describing: url)) #########")
completed(true)
// self.postAlertNotification()
self.tapCounter = 0
self.performSegue(withIdentifier: "chooseIconSegue", sender: self)
}
}
can you see where's the error?
Many thanks.

Thinking about it I realised that downloadURL fetch was actually done before than the image upload was complete, that's because Firebase is asynchronous. So I added a completion block to the uploading part, and in the completion scope I put the downloadURL fetching part.
It's not blinking eyes fast and I will appreciate any suggestion on speeding up things, because this way there is a little lag before the seguegets performed.
It's not annoying at all an I could, and probably should, add a little spinning wheel to show users that the app has not frozen, but I rather just avoid the lag altogether if possible. I left the prints written in hope that this post will help others new to Firebase as I am, giving detailed almost step-by-step guidance, as this kind of answers really helped me before, and I haven't found any on the subject.
Rewritten function is:
func uploadImage(completed: #escaping (Bool) -> (),_ image: UIImage ){
print(" ############## UPLOAD STARTED ###########")
// Create a root reference
let storageRef = Storage.storage().reference()
// Create a reference to Images folder
let alertsImagesRef = storageRef.child("Alert Images")
// Create a reference for new images
let uuid = UUID()
let imageRef = alertsImagesRef.child("userAlertImage \(uuid).jpg")
let imageData = UIImageJPEGRepresentation(image, 0.5)
let metaData = StorageMetadata()
metaData.contentType = " jpeg " // data type
metaData.customMetadata = ["k1": "",
"k2" : " ",
"k3" : "",
"k4" : ""]
imageRef.putData(imageData! as Data , metadata: metaData, completion: { metaData, error in
if(error != nil){
print(error as Any)
return
}
print(" ####### image uploaded #######")
self.tapCounter = 0
self.performSegue(withIdentifier: "chooseIconSegue", sender: self)
// fetch url v2
imageRef.downloadURL(completion: { (url, err) in
if let err = err {
print("Error downloading image file, \(err.localizedDescription)")
return
}
guard let url = url else { return }
//Now you have the download URL for the image, and can do whatever you want with it.
NewMapViewController.alertImageURL = url.absoluteString
print(" ######### url is:\(String(describing: url)) #########")
completed(true)
// self.postAlertNotification()
// self.tapCounter = 0
// self.performSegue(withIdentifier: "chooseIconSegue", sender: self)
print(" ############## UPLOAD ENDED ###########")
})
})
}

Related

Display Image downloaded from downloadURL generated by Firebase

I need to display the images that have been stored on the storage on Firebase. Right now, I only tracked the images using the link generated by function downloadURL:
func UploadImage(imageData: Data, path: String, completion: #escaping (String) -> ()){
let storage = Storage.storage().reference()
let uid = Auth.auth().currentUser?.uid
storage.child(path).child(uid ?? "").putData(imageData, metadata: nil) { (_, err) in
if err != nil{
completion("")
return
}
// Downloading Url And Sending Back...
storage.child(path).child(uid ?? "").downloadURL { (url, err) in
if err != nil{
completion("")
return
}
completion("\(url!)")
}
}
}
So all I can get is a hyperlink that is like: https://firebasestorage.googleapis.com/v0/b/getting-started-20f2f.appspot.com/o/profile_Photos%2FGQ1KR9H1mLZl2NAw9KQcRe7d72N2?alt=media&token=473ce86c-52ba-42ec-be71-32cc7dc895d7.
I refer to the official documentation, it seems that only when I have the name of the image file can I download it to an ImageView or UIImageView object. However, the link does not make any sense to me, so what can I do?
EDIT
I actually tried a solution provided by the official documentation:
func imageDownloader(_ imageURL: String) {
let store = Database.database().reference()
let uid = Auth.auth().currentUser?.uid
let imageRef = store.child(imageURL)
var myImageView = UIImageView()
imageRef.getData(completion: { (error, data) in
if let error = error {
// Uh-oh, an error occurred!
} else {
// Data for "images/island.jpg" is returned
let image = UIImage(data: data!)
}
})
}
But it suggests that I need to change something because Cannot convert value of type 'DataSnapshot' to expected argument type 'Data'.
If you're storing the image paths in Firestore, actually the exact file name does not matter if there is only one file available under the fork. So you just need to specify the path.
To then download the image from Storage, construct the path and download:
let uid = Auth.auth().currentUser?.uid
Storage.storage().reference().child("the\path\to\your\uid\collection").child(uid).getData(maxSize: 1048576, completion: { (data, error) in
if let data = data,
let img = UIImage(data: data) {
// do something with your image
} else {
if let error = error {
print(error)
}
// handle errors
}
})
You are uploading to Storage.storage(), but then in your imageDownloader, you're attempting to use Database.database(), which has a similar-looking API, but is, in fact, different.
Make sure to use Storage.storage() and that the closure parameters are in the order data, error in.
Finally, right now in your imageDownloader, it doesn't look like you're doing anything yet with var myImageView = UIImageView(), but keep in mind that you won't have access to the UIImage until the async getData completes.
Store your images at Firebase Storage & then retrieve using this code.
Storage.storage().reference.child("ProfilePhotos").child("ImageName").downloadURL {(url, _) in
DispatchQueue.main.async {
guard let url = url else { return }
imageView.setImage(with: url, placeholder: UIImage(named: "dummyImage"))
}
}

ImageView to Firebase Storage in Swift

I am trying to upload an image from ImageView to Firebase storage but it won't work.
I have listed my code below: My image view is called ProfileImage
let storageRef = Storage.storage().reference().child("myImage.png")
if let uploadData = self.ProfileImage.image!.pngData() {
storageRef.putFile(from: uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("error")
completion(nil)
} else {
// your uploaded photo url.
}
}
}
It comes up with the error "Cannot convert value of type 'Data' to expected argument type 'URL'
You are trying to upload Data, not a file. Replace
putFile
With
putData
And it should work fine
Try this code :-
let storageRef = Storage.storage().reference().child("myImage.png")
if let uploadData = UIImagePNGRepresentation(self.profileImage.image!) {
storageRef.put(uploadData, metadata: nil) { (metadata, error) in
if error != nil {
print("\(error.localizeDescription)")
} else {
// your uploaded photo url.
}
}
let refDatabase = Database.database().reference()
var refstorage = Storage.storage().reference()
let data = image.jpegData(compressionQuality: 1.0) //
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
let postkey = refDatabase.child("Post").childByAutoId().key
print(postkey)
let imagename = "PostImage/\(postkey).png"
refstorage = refstorage.child(imagename)
let timestamp = Date().timeIntervalSince1970 // you can use this to track time when it was uploaded.
self.refstorage?.putData(data!, metadata: metadata, completion: { (meta, error) in
if error == nil{
if let imageData = meta?.downloadURL()?.absoluteString{
// DO SOMETHING
} else {
print("Couldn't get the URL for image")
}
}
})
I know that this question has been answered, but there is an easier way to do this. Along with import Firebase and Firebase Storage, you will also have to add FirebaseUI to your podfile and import it.
After you have done that, you could get your image to your app much simpler.
let storage = Storage.storage()
let storageRef = storage.reference()
let placeholderImage = UIImage(named: "placeholder.jpeg")
let reference = storageRef.child("myImage.png")
ProfileImage.sd_setImage(with: reference, placeholderImage: placholderImage)
(The placeholder Image would just be a transparent image that you put in your assets folder in XCode that you could reuse multiple times in your application, for whenever you needed to get a Firebase Image on your app.)

How to save a group (array) of images into firebase cloud storage?

I am attempting to save an array of images the user took into cloud storage and then save a URL to that image under that persons profile. How can I do this? Currently I wrote the code bellow which save one images at a time taken by the user but I was told this was a bad way of doing it. Additionally images are not being added but replacing the one before it.
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let userPickedImage = info[UIImagePickerControllerOriginalImage] as? UIImage {
// let imageToUse = PhotoArray()
// let data = UIImagePNGRepresentation(userPickedImage) //here convert to data
PhotoArray.sharedInstance.photosArray.append(userPickedImage) //append converted data in array
// do {
// try realm.write {
// realm.add(imageToUse)
// }
// } catch {
// print(“error adding image to array\(error)“)
// }
imageView.image = userPickedImage
//-----------------------------//
//Create a reference to the image
let imageRef = Storage.storage().reference().child("image.jpg")
// Get image data
if let uploadData = UIImagePNGRepresentation(userPickedImage) {
// Upload image to Firebase Cloud Storage
imageRef.putData(uploadData, metadata: nil) { (metadata, error) in
guard error == nil else {
// Handle error
return
}
// Get full image url
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// Handle error
return
}
// Save url to database
Firestore.firestore().collection("images").document("myImage").setData(["imageUrl" : downloadURL.absoluteString])
}
}
}
//-----------------------------//
}
// print(PhotoArray().photosArray.count)
imagePicker.dismiss(animated: true, completion: nil)
}
I found this in the firebase docs:
// Add a new document with a generated id.
var ref: DocumentReference? = nil
ref = db.collection("cities").addDocument(data: [
"name": "Tokyo",
"country": "Japan"
]) { err in
if let err = err {
print("Error adding document: \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
Hope this will help you.
You can read about here: Click me

Firebase image object does not exist?

I am currently learning about how to upload/download images to/from Firebase. However, I am encountering two problems:
When I upload the image to my Firebase console it says it's type is "application/octet-stream" which is not exactly what I want (I uploaded a jpeg file)
Here's the code:
#IBAction func completeSignUpButton(_ sender: UIButton) {
let userProfileImageRef = storage.child("userProfileImage")
//UPloading the photo into firebase
let data = Data()
//uploading user profile picture
if self.userInitialPhoto.image != nil {
if let uploadData = UIImagePNGRepresentation(self.userInitialPhoto.image!) {
userProfileImageRef.putData(uploadData, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error?.localizedDescription)
return
} else {
print("Image upload success!")
//self.dismiss(animated: true, completion: nil)
self.performSegue(withIdentifier: "completeRegistrationSegue", sender: self)
}
})
}
} else {
print("You need to pick a picture!")
}
}
So this might be the problem that's causing my second problem:
When I try to load the image from Firebase to my UIImage in my app, it says Object Optional(\"userID\")/userProfileImage does not exist."
Here's my code for that:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
docRef = Firestore.firestore().collection("user Status").document("Current Status")
self.userStatusTableView.reloadData()
let referencePath = Storage.storage().reference(withPath: "\(userID)/userProfileImage")
let userProfileImageRef = Storage.storage().reference().child("\(userID)/userProfileImage")
userProfileImageRef.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if error != nil {
print("There's an eror downloading the image\(error?.localizedDescription)")
} else {
self.userProfileImage.image = UIImage(data: data!)
}
}
}
I am wondering what I should do to fix these issues.
Thanks for your help in advance and happy new year to you all.
while uploading the image to firebase you passed metadata as nil. you need to pass image metadata there
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpg"
and in upload complitionBlock, you can get URL for that image using
if error == nil , let meta = meta {
if let url = meta.downloadURL()?.absoluteString {
//use this url to access your image
}
}

Changing values in storage metadata in Firebase

In my iOS app, users can upload profile pictures which are upload to the storage in Firebase. I have saved the URL of the profile pictures on to the database so I can know which URLs correspond to which users using the following code:
let storageRef = FIRStorage.storage().reference()
let fileRef = storageRef.child("pages/").child(UUID().uuidString + ".jpg")
_ = fileRef.put(UIImageJPEGRepresentation(image, 0.75)!, metadata: nil) { (metadata, error) in
if let error = error {
SCLAlertView().showError("Error", subTitle: error.localizedDescription)
return
} else {
let downloadURL = metadata!.downloadURL()
let dictionary: [String: Any] = ["UserID": uid, "PageName": self.pageTitle.text!, "PFPURL": downloadURL!, "Tags": self.tags, "Likes": [uid]]
let reference = FIRDatabase.database().reference().child("Pages").childByAutoId()
reference.setValue(dictionary, withCompletionBlock: { (error, ref) in
if let error = error {
SCLAlertView().showError("Error", subTitle: error.localizedDescription)
return
}
self.performSegue(withIdentifier: "setupComplete", sender: self)
})
}
}
However, I would like these pictures to be editable. Is it possible I can replace the image in the storageRef yet still keep the same URL? If so, how would I accomplish that? Thanks!
You can't keep the same download URL. When the photo is edited, you'll need to update the value of "PFPURL" in the database to be the new URL.

Resources