I want to learn how to properly retrieve the image from Firebase for the current user.I am trying to get the user ImageUrl from the User table and use that url to display the image down below but it does not do it and crashes the app. I want to know if I am doing it properly or doing it wrong.
Thank you in advance
func retrieveTheImage() {
let userID = Auth.auth().currentUser?.uid
let retrieveTheUrl = Database.database().reference().child("User").child(userID!)
var capatureUrl :String = ""
retrieveTheUrl.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? Dictionary<String,String>{
let url = snapShotValue["ImageUrl"]! /
capatureUrl = url
print(capatureUrl)
}
}
let storage = Storage.storage()
var reference: StorageReference!
reference = storage.reference(forURL: capatureUrl)
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data )
self.imageUser.image = image
}
}
Your code actually works...almost.
The problem with the code is that Firebase is asynchronous so data only becomes valid within the closure following a firebase call.
So here's what's happening (condensed code)
func retrieveTheImage2() {
let userID = Auth.auth().currentUser?.uid
let retrieveTheUrl = Database.database().reference().child("User").child(userID!)
//code in closure//
}
//code after closure//
--> reference = storage.reference(forURL: capatureUrl) //not valid
reference.downloadURL { (url, error) in
}
}
The code after the closure will execute before the //code in closure//.
That means capatureUrl will be nil because it has not been populated yet. Code is faster than the internet.
To fix that, just move the code that accesses data from Firebase within the closure.
func retrieveTheImage2() {
let userID = Auth.auth().currentUser?.uid
let retrieveTheUrl = Database.database().reference().child("User").child(userID!)
var capatureUrl :String = ""
retrieveTheUrl.observeSingleEvent(of: .value) { (snapShot) in
if let snapShotValue = snapShot.value as? Dictionary<String,String>{
let url = snapShotValue["ImageUrl"]!
capatureUrl = url
print(capatureUrl)
let storage = Storage.storage()
var reference: StorageReference!
reference = storage.reference(forURL: capatureUrl) //will be valid here.
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data )
self.imageUser.image = image
}
}
}
}
In general, it takes time for data to return from the internet and that's the purpose of Firebase closures - that code executes when the data is valid. So if you want to work with Firebase data, only attempt to access it initially within those closures.
Related
This is my firebase realtime database
Image link
This is the snippet i am using its always returning nil
let rootRef = Database.database().reference()
rootRef.child("POSTS").queryLimited(toLast: 1).observeSingleEvent(of: .value) { (myDataSnap) in
let value = myDataSnap.value as? NSDictionary
print(value as? Any)
}
If you are trying to access Id and Id's can be anything other than 0.
Than you can try the snippet below
let Key = rootRef.child("POSTS").childByAutoId().key
rootRef.child("POSTS").child(Key!).setValue(postData.toDictionary()){
(error:Error?, ref:DatabaseReference) in
if let error = error {
print("Data could not be saved: \(error).")
} else {
//do stuff
}
}
Use .childAdded or childChanged instead of .value, this might solve it
let rootRef = Database.database().reference()
rootRef.child("POSTS").queryLimited(toLast: 1).observeSingleEvent(of: .childChanged) { (myDataSnap) in
let value = myDataSnap.value as? NSDictionary
print(value as? Any)
}
I have trouble retrieving a UIImage from Firebase Storage, the child path seems to be correct, though the image does not get "downloaded" to be displayed. The part about the Firebase Database is working fine, hence retrieving data, whereas the Storage one is now. Code and Firebase path below
I cannot understand whether the problem is in the fact that I nested the function into the .observeSingleEvent of the Database retrieving function or not.
gs://xxxyyy-xxxyyy.appspot.com/images/QhRmIcbF7AOWjZ3nrjFd7TOekrA3/FirstImage.jpg
var cells : [Cella] = []
var imageReference: StorageReference {
return Storage.storage().reference().child("images")
}
var databaseReference: DatabaseReference {
return Database.database().reference()
}
func getDataFromFirebase() -> [Cella]{
let queryRef = databaseReference.queryLimited(toLast: 1)
var appCells : [Cella] = []
queryRef.observeSingleEvent(of: .value, with: { (snapshot) in
for snap in snapshot.children {
var userPhoto : UIImage?
let userSnap = snap as! DataSnapshot
let customerUid = userSnap.key
let userDict = userSnap.value as! [String:AnyObject]
let description = userDict["description"] as! String
let title = userDict["title"] as! String
print(title)
print(String(customerUid))
print(description)
self.descriptionsArray[String(customerUid)] = description
self.titlesArray[String(customerUid)] = title
//error is here BECAUSE it can't retrive the image to be dispalyed. Title and description are fine
self.imageReference.child(String(customerUid)).child("FirstImage.jpg").getData(maxSize: 10*1024*1024, completion: { (data, error) in
if error != nil {
print("\(String(describing: error?.localizedDescription))")
}
else {userPhoto = UIImage(data: data!)}
})
let newCella = Cella(image: userPhoto!, title: title, bodyMessage: description)
appCells.append(newCella)
}
})
return appCells
}
------ UPDATE ------
As suggested I changed to using firebase Firestore and saving there the download URL as well as the other information. Still though, I cannot seem to get the image downloading. New code below.
This is the data retrieved by document.data() :
xxx.yyy#gmail.com => ["userID": QhRmIcbF7AOWjZ3nrjFd7TOekrA3, "userDescription": Route66, "userImageUrl": https://firebasestorage.googleapis.com/v0/b/shardana-61183.appspot.com/o/images%2FQhRmIcbF7AOWjZ3nrjFd7TOekrA3%2FFirstImage.jpg?alt=media&token=dea541bf-d598-414e-b4ed-a917541598d5, "userTitle": Sample]
firestoreUsersDatabase.getDocuments { (querySnapshot, error) in
if let error = error {
print("Error getting the documents: \(error)")
} else {
for document in querySnapshot!.documents {
print("\(document.documentID) => \(document.data())")
let data = document.data()
let imageUrl = data["userImageUrl"] as! String
let title = data["userTitle"] as! String
let description = data["userDescription"] as! String
let urlDownloadReference = self.imageReference.reference(forURL: imageUrl)
urlDownloadReference.getData(maxSize: 10*2014*2014, completion: { (data, error) in
if error != nil {
print("An error occurred: \(String(describing: error?.localizedDescription))")
} else {
guard let imageDownloaded = UIImage(data: data!) else {print("Image url returned nil value ERROR"); return}
let newCell = Cella(image: imageDownloaded, title: title , bodyMessage: description )
print("NEW CELL: Image \(newCell.image)")
appCells.append(newCell)
}
})
}
}
}
yes, I think you're logic needs review. You need to store on your Firestore all the users data, including all the references to needed images. On the other hand, Firebase Storage, which is a different service within Firebase will save the images an will give you download links, but it uses a different logic than Firestore.
See the following example for clarification on what I mean:
https://firebase.google.com/docs/storage/web/download-files
My app allows for Facebook authentication. When the user logs in, I will like to set up a node called users that holds some of the user's information.Specifically, I will like to get the user's UID, name and profile picture from Facebook.
Here is my code so far:
let credential = FIRFacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
// using the credentials above, sign in to firebase to create a user session
FIRAuth.auth()?.signIn(with: credential) { (user, error) in
print("User logged in the firebase")
// adding a reference to our firebase database
let ref = FIRDatabase.database().reference(fromURL: "https://gsignme-14416.firebaseio.com/")
// guard for user id
guard let uid = user?.uid else {
return
}
// create a child reference - uid will let us wrap each users data in a unique user id for later reference
let usersReference = ref.child("users").child(uid)
// performing the Facebook graph request to get the user data that just logged in so we can assign this stuff to our Firebase database:
let graphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil).start{
(connection, result, err) in
if let user = FIRAuth.auth()?.currentUser{
let name = user.displayName! as String
let newImage = UIGraphicsGetImageFromCurrentImageContext()
let data: Data = UIImageJPEGRepresentation(newImage!, 0.5)!
let storage = FIRStorage.storage()
let storageRef = storage.reference(forURL: "gs://gsignme-14416.appspot.com")
let profilePicRef = storageRef.child(user.uid+"/profile_pic.jpg")
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpg"
// Upload the file
let uploadTask = profilePicRef.put(data, metadata: metadata) { metadata, error in
if (error == nil) {
self.downloadurl = metadata!.downloadURL()!.absoluteString
} else {
print("there was an error uploading the profile pic!")
}
let postObject: Dictionary<String, Any> = [
"uid": uid,
"username" : name,
"userpic" : self.downloadurl
]
if ((error) != nil) {
// Process error
print("Error: \(String(describing: error))")
} else {
print("fetched user: \(String(describing: result))")
let values: [String:AnyObject] = result as! [String : AnyObject]
// update our database by using the child database reference above called usersReference
usersReference.updateChildValues(postObject, withCompletionBlock: { (err, ref) in
// if there's an error in saving to our firebase database
if err != nil {
print(err!)
return
}
// no error, so it means we've saved the user into our firebase database successfully
print("Save the user successfully into Firebase database")
})
}
}}}
}
Without implementing retrieving the user's photo URL from firebase storage, it works perfectly by outputting the name and UID. However, when I try to retrieve the image URL from firebase storage it crashes and nothing outputs. I am not really sure what I am doing wrong.
I want the database to look like this:
Below is the procedure how to upload image in storage and update link of image in DB and then how to retrieve image from link.
Update data in DB.
//// **NOTE:** store user ID globally in user auth so that you use userID here
func updateUserData(){
let dbstrPath : String! = "\(userid)"// where you wanted to store Db.
// Create dict as your DB.
let aDictUpdateValues = ["userName" : "John",
"userEmail" : "john#gmail.com",
"userPic" : "",
"uid" : userid]
// The Db is created as per your dict and file path. So create this two things specifically according to you..
dbRef!.child(dbstrPath).updateChildValues(aDictUpdateValues, withCompletionBlock: { (error, ref) in
if error == nil{
print("updated successfully")
self.uploadProfilePic()
dbRef.child(dbstrPath).setValue(aDictUpdateValues)
let aData = NSKeyedArchiver.archivedData(withRootObject: auserDetail)
print(ref)
}
else{
print("error in updation")
print(error!)
}
})
}
// Upload image in storage
func uploadProfilePic(){
var data = NSData()
data = UIImageJPEGRepresentation(ivProfile.image!, 0.8)! as NSData
// set upload path
let filePath = "\(userid)" // path where you wanted to store img in storage
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpg"
self.storageRef = FIRStorage.storage().reference()
self.storageRef.child(filePath).put(data as Data, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
}else{// store imageURL in DB.
let dbfilePath = "\(userid)/userpic" // update db userpic link.
//store downloadURL
let downloadURL = metaData!.downloadURL()!.absoluteString
//store downloadURL at database
dbRef.child(dbfilePath).updateChildValues(["userPic" : downloadURL])
}
}
}
Get image form firebase storage.
func getProfileImage(){
let dbstrPath : String! = "your_db_path" // path where link is stored in DB.
dbRef.child(dbstrPath).observeSingleEvent(of: .value, with: { (snapshot) in
// get dict of value from db.
if let aDictValue = snapshot.value! as? [String : Any] {
// call storage ref from link
self.storageRef = FIRStorage.storage().reference(forURL: aDictValue[Constant.FireBaseDBKey.kuserProfilePic]! as! String)
// Assuming your image size < 10MB.
self.storageRef.data(withMaxSize: 10*1024*1024, completion: { (data, error) in
if data != nil{ // if image found
let userPhoto = UIImage(data: data!)
self.ivProfile.image = userPhoto
}
else{ // if img not found set default image.
self.ivProfile.image = UIImage(named: "profile")
}
})
}
})
}
I have an issue when I'm downloading an image from firebase here is my code:
func getuser(){
FIRDatabase.database().reference().child("users").child(userID!).child("credentials").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
let name = value?["name"] as! String
let email = value?["email"] as! String
let profilePicLink = value?["profilePicLink"] as? String ?? ""
Variables.userName = name as String
Variables.userEmail = email as String
self.username.text = Variables.userName
print(profilePicLink)
// Create a storage reference from the URL
let storageRef = self.storage.reference(forURL: profilePicLink)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.data(withMaxSize: 1 * 1000 * 1000 ) { (data, error) -> Void in
let pic = UIImage(data: data!)
self.img.image = pic
}
})
}
I'm getting this error:
==> this the link as printed https://firebasestorage.googleapis.com/v0/b/eswitch-72b56.appspot.com/o/usersProfilePics%2FKYe6fIQReNM8Oog4ELOdRLsC99J3?alt=media&token=6f2392bc-d35b-4ebc-b2c1-2dc34bc4b95a
fatal error: unexpectedly found nil while unwrapping an Optional value
The error that I'm getting is in this line
let storageRef = self.storage.reference(forURL: profilePicLink)
below is my snapshot readings:
snap (credentials) {
email = "bilal#me.com";
mobile = 50955514;
name = Bilal;
profilePicLink = "https://firebasestorage.googleapis.com/v0/b/eswitch-72b56.appspot.com/o/usersProfilePics%2FKYe6fIQReNM8Oog4ELOdRLsC99J3?alt=media&token=6f2392bc-d35b-4ebc-b2c1-2dc34bc4b95a";
role = user;
}
Thanks
Here is the solution:
Added this line to func viewDidLoad()
storage = FIRStorage.storage()
You need to make sure that self.storage is initialized.
storage = FIRStorage.storage()
Need help trying to read an array of this form:
As far as I tried, I got this
let defaults = UserDefaults.standard
let userUuid = defaults.string(forKey: defaultsKeys.keyOne)
let ref = FIRDatabase.database().reference().child("images").child("\(userUuid!)")
let filterQuery = ref.queryOrdered(byChild: "uuid").queryEqual(toValue: "\(uuid)") // where uuid is a value from another view
filterQuery.observe(.value, with: { (snapshot) in
for images in snapshot.children {
print(images)
}
})
But I receive nothing. I want to read the images' links to show them in the view controller.
Make sure that the uuid var in the line below is not an optional value (or if it is, unwrap it) because otherwise you'll be querying to compare to "Optional(myUuidValue)" instead of "myUuidValue"
let filterQuery = ref.queryOrdered(byChild: "uuid").queryEqual(toValue: "\(uuid)")
The snapshot in the line below contains more than just the images, it has all the other children under that uuid
filterQuery.observe(.value, with: { (snapshot) in })
So extract the images like this:
filterQuery.observe(.value, with: { (snapshot) in
let retrievedDict = snapshot.value as! NSDictionary
let innerDict = retrievedDict["KeyHere"] as! NSDictionary // the key is the second inner child from images (3172FDE4-...)
let imagesOuterArray = userDict["images"] as! NSArray
for i in 0 ..< imagesOuterArray.count {
let innerArray = imagesOuterArray[i] as! NSArray
for image in innerArray {
print(image as! String)
}
}
})
Clarification: cast all the children of the uuid as an NSDictionary, then extract the nested arrays using those two for-loops
Update
Thanks to Jay for pointing out the error! Also, as Jay suggested, consider restructuring your database and replacing those arrays with dictionaries that perhaps contain the URL, path (for deleting purposes if you need that), and timestamp of each image.
After strugling for the answer, got this code works
let ref = FIRDatabase.database().reference().child("images").child("\(userUuid!)")
let filterQuery = ref.queryOrdered(byChild: "identifier").queryEqual(toValue: "\(identifier)")
filterQuery.observe(.value, with: { (snapshot) in
for child in snapshot.children {
if (child as AnyObject).hasChild("images") {
let images = (images as AnyObject).childSnapshot(forPath: "images").value! as! NSArray
for i in images {
for j in i as! [AnyObject] {
let url = NSURL(string: j as! String)
//Then downloaded the images to show on view
URLSession.shared.dataTask(with: url! as URL, completionHandler: { (data, response, error) in
if error != nil {
print(error)
return
}
//Code to show images..
}).resume()
}
}
}
}
})
Can i receive feedback about this?