I wanted to know what is the best way to fetch images which are uploaded by the user on my app. Currently, the images are all being saved in storage. I assume this is not the correct way as it has nothing to do with userId? Perhaps, I need to save user media in real time database in order to fetch their images? If someone could help me, that will be much appreciated.
Thank you.
guard let image = imageView.image, let data = image.jpegData(compressionQuality: 1.0)
else {
print("something went wrong")
return
}
let imageName = UUID().uuidString
let imageReference = Storage.storage().reference().child(MyKeys.imagesFolder).child(imageName)
imageReference.putData(data, metadata: nil) { (metadata, err) in
if let err = err {
print ("Error")
return
}
imageReference.downloadURL(completion: { (url, err) in
if let err = err {
print ("Error")
return
}
guard let url = url else {
print ("Error")
return
}
let dataReference = Firestore.firestore().collection(MyKeys.imagesCollection).document()
let documentUid = dataReference.documentID
let urlString = url.absoluteString
let data = [
MyKeys.uid:documentUid,
MyKeys.imageUrl: urlString,
MyKeys.imageTitle: self.imageTitle.text!,
] as [String : Any]
dataReference.setData(data) { (err) in
if let err = err {
print ("Error")
return
}
}
})
}
}
A pretty common pattern is to store the user ID in the image's path in Firebase Storage. For example,
let imageName = //...
guard let uidString = Auth.auth().currentUser?.uid else {
return
}
let imageRefPath = "\(uidString)/\(imageName)"
Related
I can only upload a single photo to FirebaseStorage.
All the tutorials I have seen on youtube use FirebaseStorage to save user's profile pictures. However, I am trying to make a restaurant app where I should be able to keep several pictures and not only one. My code right now only allows me to keep one. As soon as I try to upload a second image, it erases the first one.
Here's my code:
#IBAction func agregarComidaFav(_ sender: Any) {
guard let imageData = imagen?.jpegData(compressionQuality: 0.4) else {
return
}
let storageRef = Storage.storage().reference(forURL: "gs://comidas-68043.appspot.com")
let storageProfileRef = storageRef.child("ComidasFavoritas")
let metadata = StorageMetadata()
metadata.contentType = "image/png"
storageRef.child("comidasFavoritas/png").putData(imageData, metadata: metadata) { (storageMetaData, error) in
if error != nil {
print(error?.localizedDescription ?? "")
return
}
}
storageProfileRef.downloadURL(completion: { (url, error) in
if let metaImageUrl = url?.absoluteString {
print(metaImageUrl)
self.imagenURL = metaImageUrl
}
})
var ref: DocumentReference? = nil
ref = db.collection("comidasFavoritas").addDocument(data: [
"imagenURL" : imagenURL
]) { err in
if let err = err {
print("Error agregando comida: \(err)")
} else {
let id = ref!.documentID
print("Comida agregado con ID: \(id)")
}
}
}
This line:
storageRef.child("comidasFavoritas/png").putData(...)
...guarantees that each time, the data is going to be written to "comidasFavoritas/png"
I don't know anything about how you're keeping track of your data/images in your app, but you'll probably need some way to keep track of an ID for that image. Then, when you store it, you could store it like:
storageRef.child("comidasFavoritas/png\(imageId)").putData(...)
You'd probably also want to store that ID in your database when you store the image URL.
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
}
}
}
}
}
I'm trying to save the PKPass with Firebase so that I'm not generating the pass every time the user wants to add the pass to the waller or send the pass. I'm storing the pass.passURL in my firebase database and then fetching it again when the user wants to do something with the pass. Right now all that is happening is the Wallet is opening but the pass isn't showing up. Is this the best way to store the PKPass
Save Function:
func saveTicketIdToDB(pass: PKPass) {
guard let uid = Auth.auth().currentUser?.uid else { return }
guard let postId = post?.postId else { return }
guard let paymentId = paymentId else { return }
guard let passUrl = pass.passURL as? URL else { return }
let passUrlString: String = passUrl.absoluteString
let values = ["passUrl": passUrlString]
let db = Firestore.firestore()
db.collection("payments").document(postId).collection("purchases").document(paymentId).updateData(values) { (error) in
if let error = error {
print("There was an error", error.localizedDescription)
return
}
print("Successfully saved pkpass into database.")
}
}
Opening in wallet:
guard let passUrlData = URL(string: passUrl) else { return }
if #available(iOS 10.0, *) {
UIApplication.shared.open(passUrlData, options: [:], completionHandler: nil)
} else {
UIApplication.shared.openURL(passUrlData)
}
You need to store the entire PKPass bundle as a data blob. Then re-create it using PKPass.init(data: ...) and use the PassKit APIs to see if it's in the Wallet already.
Here is the code that I'm using
#objc func handleSignUp() {
guard let email = emailTextField.text, email.characters.count > 0 else { return }
guard let username = usernameTextField.text, username.characters.count > 0 else { return }
guard let password = passwordTextField.text, password.characters.count > 0 else { return }
Auth.auth().createUser(withEmail: email, password: password, completion: { (user, error) in
if let err = error {
print("Failed to create user:", err)
return
}
print("Successfully created user:", user?.user.uid)
// guard let image = self.plusPhotoButton.imageView?.image else { return }
// guard let uploadData = UIImageJPEGRepresentation(image, 0.3) else { return }
guard let uploadData = self.plusPhotoButton.imageView?.image?.jpegData(compressionQuality: 0.3) else{return}
let filename = NSUUID().uuidString
Storage.storage().reference().child("profile_images").child(filename).putData(uploadData, metadata: nil, completion: { (metadata, err) in
if let err = err {
print("Failed to upload profile image:", err)
return
}
//this code isn't correct for swift4.2
//guard let profileImageUrl = metadata?.downloadURL()?.absoluteString else { return }
func getDownloadURL(from path: String, completion: #escaping (URL?, Error?) -> Void) {
let storageReference = Storage.storage().reference(forURL: "gs://instagramfirebase-60be5.appspot.com")
let storageRef = storageReference.child(path).downloadURL(completion: completion)
}
print("Successfully uploaded profile image")//,profileImageUrl
guard let uid = user?.user.uid else { return }
let dictionaryValues = ["username": username] as [String : Any]//, "profileImageUrl": profileImageUrl
let values = [uid: dictionaryValues]
Database.database().reference().child("users").updateChildValues(values, withCompletionBlock: { (err, ref) in
if let err = err {
print("Failed to save user info into db:", err)
return
}
print("Successfully saved user info to db")
})
})
})
}
Snapshots are as followed :
I am confused about how to get the imageUrl from firebase storage and store it in firebase database in Xcode 10 swift 4.2
Please help, Thank you for your time.
You can get the download url easily by using the following code inside the closure of your image-upload:
storageRef.downloadURL(completion: { (url, error) in
if let err = error{
// error happened - implement code to handle it
print(err)
} else {
// no error happened; so just continue with your code
print(url?.absoluteString) // this is the actual download url - the absolute string
}
Please note: Don't store the downloadURL on its own as Firebase can change tokens of the downloadURL, I'd suggest to always grab a hold of the full storage path.
I hope I was able to help.
I am having a bit of an issue checking Firebase Storage when a user logs into my app. When a user logs in an image is created and stored successfully in Firebase database and storage, but every time a user logs out and logs back into the app the profileImage file (not the child value url string) is duplicated in Firebase Storage as a new and separate image file.
I would like the app when logging in to check the Firebase storage for the profileImage file, and if it exists then do not re-create that same image in storage. I understand the logic of checking Firebase for image and if does not exist, than create new image. I am just having some trouble with the syntax. Thanks for help in advance!
// login user with Facebook
func loginWithFacebook() {
let accessToken = FBSDKAccessToken.current()
guard let accessTokenString = accessToken?.tokenString else { return }
let credentials = FIRFacebookAuthProvider.credential(withAccessToken: accessTokenString)
FIRAuth.auth()?.signIn(with: credentials, completion: { (user, error) in
if error != nil {
print("Something went wrong with our FB user: ", error ?? "")
return
}
guard let uid = user?.uid else {
return
}
let imageName = NSUUID().uuidString
let storageRef = FIRStorage.storage().reference().child("profile_images").child("\(imageName).png")
let photoUrl = user?.photoURL
// check to see if current user already has stored image with URL
if FIRStorage.storage().reference().child("profile_images").child("\(imageName).png") == nil {
if let imageData = NSData(contentsOf: photoUrl!) {
storageRef.put(imageData as Data, metadata:nil) {
(metadata, error) in
if error != nil {
print(error!)
return
} else {
if let profileImageUrl = metadata?.downloadURL()?.absoluteString {
let values = ["name": user!.displayName!, "email": user!.email!, "profileImageUrl": profileImageUrl]
self.registerUserWithUID(uid: uid, values: values as [String : AnyObject])
}
}
}
}
} else {
print("image already exists")
}
print("Successfully logged in with our user: ", user ?? "")
self.delegate?.finishLoggingIn()
})
private func registerUserWithUID(uid: String, values: [String: AnyObject]) {
// create items in database upon creating user ---------------
let ref = FIRDatabase.database().reference()
let usersReference = ref.child("users").child(uid)
usersReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil {
print(err!)
return
}
print("user has been saved to Firebase database")
})
}
I think the issue is with the "if" condition. You are referencing the location, but not checking with the actual data in that location.
You can implement that using .observerSingleEventOf().
private func checkUser (imageName:String, _ completionHandler: #escaping(Bool) -> Void)
{
let userExists = FIRStorage.storage().reference().child("profile_images")
userExists.observeSingleEvent(of: .value, with:{(snapshot) in
if snapshot.hasChild(imageName){
print ("Image already exists")
completionHandler(false)
}
else
{
print ("Image does not exist")
print ("store the image")
}
})
}
`