Use of Unresolved Identifier firebase function - swift

I've defined the function sendDataToDatabase but for some reason it's not resolving photoUrl?
I've been trying to figure out what might be causing this for 6 hours now and can't seem to find a solution, if anyone could provide some help it would be appreciated.
#IBAction func shareButton_TouchUpInside(_ sender: Any) {
ProgressHUD.show("Waiting...", interaction: false)
if let profileImg = self.selectedImage, let imageData = profileImg.jpegData(compressionQuality: 0.1) {
let photoId = NSUUID().uuidString
let storageRef = Storage.storage().reference(forURL: "manifest-bit-233115.appspot.com").child("posts").child(photoId)
storageRef.putData(imageData, metadata: nil, completion: { (metadata, Error) in
if Error != nil {
return
}
storageRef.downloadURL(completion: { (URL, Error) -> Void in
if (Error != nil) {
//handle any errors
} else {
//get download url
let photoUrl = URL?.absoluteString
}
self.sendDataToDatabase(photoUrl: photoUrl!)
})
}
)}
func sendDataToDatabase(photoUrl: photoUrl!) {
let ref = Database.database().reference()
//let uid = Auth.auth().currentUser!.uid
let postsRef = ref.child("posts")
let newPostId = postsRef.childByAutoId().key
let newPostRef = postsRef.child(newPostId!)
newPostRef.setValue(["photoUrl": photoUrl])
}

There are many issues.
You have to call sendDataToDatabase only in the else branch and declare the parameters with starting lowercase letters.
The parameters are not types.
storageRef.downloadURL(completion: { (url, error) -> Void in
if let error = error {
//handle any errors
} else {
//get download url
let photoUrl = url!.absoluteString
self.sendDataToDatabase(photoUrl: photoUrl)
}
})
and you have to declare the type in the function
func sendDataToDatabase(photoUrl: String) { ...

This won't work:
storageRef.downloadURL(completion: { (URL, Error) -> Void in
if (Error != nil) {
//handle any errors
} else {
//get download url
let photoUrl = URL?.absoluteString
}
self.sendDataToDatabase(photoUrl: photoUrl!)
})
photoUrl will only be available within the else clause, since that's where it's defined, and you can not use it outside of that scope.
Also, this:
func sendDataToDatabase(photoUrl: photoUrl!)
should probably be:
func sendDataToDatabase(photoUrl: String)
It's also a good idea to not name variables URL and Error, since they are identical to the URL and Error classes. Name them url and error instead.

Related

Completion not working with Firebase Database

The user can upload a profile picture and some information about himself in my app.
I want to write the url of the uploaded picture in firebase realtime database but it takes the placeholder text "testentry" and not the real url. Why does my completion not work here?
var imagePicker: UIImagePickerController!
var urltoPicture = "testentry"
#IBAction func updateProfile(_ sender: UIButton) {
uploadPic(arg: true, completion: { (success) -> Void in
if success {
linkUbertragen()
} else {
}
})
func uploadPic(arg: Bool, completion: #escaping (Bool) -> ()) {
guard let imageSelected = self.image else {
//print("ok")
return
}
guard let imageData = imageSelected.jpegData(compressionQuality: 0.1) else {
return
}
let storageRef = Storage.storage().reference(forURL: "gs://h......com")
let storageProfileRef = storageRef.child("profilePictures").child(Auth.auth().currentUser!.uid)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
storageProfileRef.putData(imageData, metadata: metadata, completion: {
(storageMetadata, error) in
if error != nil {
//print(error?.localizedDescription)
return
}
storageProfileRef.downloadURL(completion: { (url, error) in
if let metaImageURL = url?.absoluteString {
print(metaImageURL)
self.urltoPicture = metaImageURL
}
})
})
completion(arg)
}
func linkUbertragen(){
ref = Database.database().reference()
let userID = Auth.auth().currentUser!.uid
ref.child("user/\(userID)").updateChildValues(["profileText": profileText.text!])
print(urltoPicture)
ref.child("user/\(userID)").updateChildValues(["picture": urltoPicture])
}
self.navigationController?.popViewController(animated: true)
}
This is a very common mistake. You have to call completion inside the (final) closure.
And it is good practice to call completion(false) always in case of an error – even better to return and handle all errors
func uploadPic(arg: Bool, completion: #escaping (Bool) -> ()) {
guard let imageSelected = self.image else {
//print("ok")
completion(false); return
}
guard let imageData = imageSelected.jpegData(compressionQuality: 0.1) else {
completion(false); return
}
let storageRef = Storage.storage().reference(forURL: "gs://h......com")
let storageProfileRef = storageRef.child("profilePictures").child(Auth.auth().currentUser!.uid)
let metadata = StorageMetadata()
metadata.contentType = "image/jpg"
storageProfileRef.putData(imageData, metadata: metadata, completion: {
(storageMetadata, error) in
if error != nil {
//print(error?.localizedDescription)
completion(false); return
}
storageProfileRef.downloadURL(completion: { (url, error) in
if let metaImageURL = url?.absoluteString {
print(metaImageURL)
self.urltoPicture = metaImageURL
completion(true)
} else {
completion(false)
}
})
})
}
The arg parameter is actually not needed.

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

Upload Profile Pic. Cannot assign photo picked from library to profilepic in users node

func uploadProfileImage(_ image:UIImage, completion: #escaping ((_ url:URL?)->())) {
let storageRef = Storage.storage().reference().child("profileImages").child("\(NSUUID().uuidString).jpg")
guard let imageData = image.jpegData(compressionQuality: 0.75) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData, metadata:metaData) { (metaData, error) in
if error != nil, metaData != nil {
storageRef.downloadURL (completion: {(url, error) in
if error != nil {
if let downloadurl = url?.absoluteString {
if (self.profilePicLink == "") {
self.profilePicLink = downloadurl
Database.database().reference().child("users").child(self.uid).updateChildValues(["profilePicLink":downloadurl])
}
}
} else {
completion(nil)
}
}
)
}
}
}
I am trying to assign upload photo from my library and then assign it to profilePicLink. When I choose pic from my library, it appears in the UIImage ImageView frame, however. When I use these func and action to update avatar it went error
#IBAction func updateAvatar(_ sender: Any) {
uploadPhoto()
}
func uploadPhoto(){
selectedUser?.uploadProfileImage(imageView.image!){
url in print (URL.self)
}
}
After I check in Firestore, there is no pic stored and my profilePicLink has no value.
Please be advised on this
You are checking for error != nil, metadata != nil together. It's contradicting each other hence that condition is never executed. Change it to:
if error == nil, metadata != nil {
...
}

Why do I have Problem with upload pic in swift. Problem with nil unwrap. URL

** Update**
My point is im trying to match my profilePicLink with image I upload from my library.
Also selectedUsers is Users Type as following
var username : String = ""
var email : String = ""
var uid : String = ""
var profilePicLink : String = ""
init(username : String, email: String, uid : String, profilePicLink: String ) {
self.username = username
self.email = email
self.uid = uid
self.profilePicLink = profilePicLink
}
I am having problem when I am trying to upload photo. The action are
I pick the photo from my library
#IBAction func getPhotoButton(_ sender: Any) {
let image = UIImagePickerController()
image.delegate = self
image.sourceType = UIImagePickerController.SourceType.photoLibrary
self.present(image, animated: true, completion: nil)
}
It leads me to my photo library. After I pick my photo. I click on button "Update" with the action as following code
#IBAction func updatePhoto(_ sender: Any) {
uploadPhoto()
}
func uploadPhoto(){
selectedUser?.uploadProfileImage(imageView.image!){
url in print (URL.self)
}
}
I got the error as ** Fatal error: Unexpectedly found nil while unwrapping an Optional value: ** in the func uploadPhoto as the picture
Fatal Error
And here is the code of func in my other class (Users) for upload and get Profile Image
func getProfileImage() -> UIImage {
if let url = NSURL(string: profilePicLink){
if let data = NSData(contentsOf: url as URL) {
return UIImage(data: data as Data)!
}
}
return UIImage()
}
func uploadProfileImage(_ image:UIImage, completion: #escaping ((_ url:URL?)->())) {
guard let uid = Auth.auth().currentUser?.uid else { return }
let storageRef = Storage.storage().reference().child("user/\(uid)")
guard let imageData = image.jpegData(compressionQuality: 0.75) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData, metadata: metaData) { metaData, error in
if error == nil, metaData != nil {
storageRef.downloadURL { url, error in
completion(url)
// success!
}
} else {
// failed
completion(nil)
}
}
}
Updated : I modifed my function uploadProfileImage as following. My point is I wanna assign profilePicLink variables to the downloadurl. And then I update value of profilePicLink
func uploadProfileImage(_ image:UIImage, completion: #escaping ((_ url:URL?)->())) {
let storageRef = Storage.storage().reference().child("profileImages").child("\(NSUUID().uuidString).jpg")
guard let imageData = image.jpegData(compressionQuality: 0.75) else { return }
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData, metadata:metaData) { (metaData, error) in
if error != nil, metaData != nil {
storageRef.downloadURL (completion: {(url, error) in
if error != nil {
if let downloadurl = url?.absoluteString {
if (self.profilePicLink == "") {
self.profilePicLink = downloadurl
Database.database().reference().child("users").child(self.uid).updateChildValues(["profilePicLink":downloadurl])
}
}
} else {
completion(nil)
}
}
)
}
}
}
Please be advised on this.

metadata?.downloadURL()?.absoluteString

I have a some problem with old syntax like:
metadata?.downloadURL()?.absoluteString"
How to use new way in my code?
Error in this part code:
let downloadURL = metadata?.storageReference?.downloadURLWithCompletion()
Full code:
Storage.storage().reference().child(imgUid).putData(imgData, metadata: metadata) { (metadata, error) in
if error != nil {
print("Did'n upload image")
} else {
print("uploaded")
let downloadURL = metadata?.storageReference?.downloadURLWithCompletion()
if let url = downloadURL {
self.setUser(img: url)
}
}
}
This is a easyway to do this use this Func to save the data to the FirebaseStorage then take the url as a String then you can save it on
func uploadImageToFirebaseStorage(data: Data, onSuccess: #escaping (_ imageUrl: String) -> Void) {
let photoIdString = NSUUID().uuidString
let storageRef = Storage.storage().reference(forURL: Config.STORAGE_REF_URL).child(POST_REF).child(photoIdString)
storageRef.putData(data, metadata: nil) { (metadata, error) in
if let error = error {
debugPrint(error.localizedDescription)
return
}
metadata?.storageReference?.downloadURL(completion: { (url, error) in
if let error = error {
print(error.localizedDescription)
return
}
onSuccess("\(url!)")
})
}
}
func save() {
let newPostRef = Database.database().reference().child("users").childByAutoId()
let newPostKey = newPostRef.key
// 1. save image
if let imageData = self.profilPic!.jpegData(compressionQuality:0.5) {
let storage = Storage.storage().reference().child("profileImages")
DispatchQueue.main.sync {
storage.putData(imageData).observe(.success, handler: { (snapshot) in
storage.downloadURL(completion: { (url, error) in
if error != nil {
print(error!.localizedDescription)
return
}
if let profileImageUrl = url?.absoluteString {
guard let uid = (Auth.auth().currentUser?.uid) else {return}
let values = ["name": self.name, "email": self.email, "profilePictureUrl": profileImageUrl]
newPostRef.setValue(values)
self.ref.child(uid).childByAutoId().setValue(values)
}
})
})
}
}
}