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")
}
})
}
})
}
Related
enter image description here
#error >> Type of expression is ambiguous without more context.
Auth.auth().createUser(withEmail: email, password: password) { AuthDataResult, error in
// handle error
if let error = error {
print("Failed to create a user with error", error.localizedDescription)
return
}
// set profile image
guard let profileImage = self.plusPhotoButton.imageView?.image else { return }
//upload data
guard let uploadData = profileImage.jpegData(compressionQuality: 0.3) else { return }
// place image in database
let filename = NSUUID().uuidString
let storageRef = Storage.storage().reference().child("profile_image").child(filename)
storageRef.putData(uploadData, metadata: nil, completion: {(metadata, error) in
// handle error
if let error = error {
print("Faild to upload image to firebase storage with error", error.localizedDescription)
}
// profile image URL
guard let profileImageURL = metadata?.downloadURL()?.absoluteString else { return }
//user Id
guard let uid = AuthDataResult?.user.uid else { return }
//guard let fcmToken = messaging.messagin().fcmToken else { return }
let dictionaryValues = ["name": fullName,
"username": username,
"profileImageURL": profileImageURL]
let values = [uid: dictionaryValues]
//save data info to database
Database.database().reference().child("users").updateChildValues(values, withCompletionBlock: { (error, ref) in
print("Successfully created user and saved indformation to database")
})
})
}
i import Firebase but still not working.
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.
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
I have looked around for an answer to this. The closest I got is here, however, It does not exactly answer my question. That being, how to store a reference to images which are saved in firebase storage, in the database.
Below is the code I have tried. It is able to store one image when uploaded but I am unsure as to whether this is what they mean by storing the reference.
if let imageData = UIImageJPEGRepresentation(image, 0.8) {
let metadata = storageRef //.child("poop/")
let uploadTask = metadata.putData(imageData, metadata: nil) {
(metadata, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
return
}
// You can also access to download URL after upload.
storageRef.downloadURL {
(url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
return
}
//let imgURL = url
//database integration
let ref = Database.database().reference()
let usersRef = ref.child("usersPosts")
let uid = Auth.auth().currentUser?.uid
let newUserRef = usersRef.child(uid!)
//creates a child for email and password (i think we shud store password so we can tell sumone what it is inmediatly, maybe)
newUserRef.setValue(["Image": "\(downloadURL)"])
}
}
// let imgURL = storageRef.downloadURL
//
// //database integration
// let ref = Database.database().reference()
// let usersRef = ref.child("usersPosts")
//
// let uid = Auth.auth().currentUser?.uid
// let newUserRef = usersRef.child(uid!)
// //creates a child for email and password (i think we shud store password so we can tell sumone what it is inmediatly, maybe)
//// newUserRef.setValue(["Image": "\(imgURL)"])
// For progress
uploadTask.observe(.progress, handler: { (snapshot) in
guard let progress = snapshot.progress else {
return
}
let percentage = (Float(progress.completedUnitCount) / Float(progress.totalUnitCount))
progressBlock(Double(percentage))
})
} else {
completionBlock(nil, "Image could not be converted to Data.")
}
I appreciate the help!
Please modify your code as required
var imgData: NSData = NSData(data: UIImageJPEGRepresentation((self.img_Photo?.image)!, 0.8)!)
self.uploadProfileImageToFirebase(data: imgData)
func uploadProfileImageToFirebase(data:NSData){
let storageRef = Storage.storage().reference().child("usersPosts").child("\(uid).jpg")
if data != nil {
storageRef.putData(data as Data, metadata: nil, completion: { (metadata, error) in
if(error != nil){
print(error)
return
}
guard let userID = Auth.auth().currentUser?.uid else {
return
}
// Fetch the download URL
storageRef.downloadURL { url, error in
if let error = error {
// Handle any errors
if(error != nil){
print(error)
return
}
} else {
// Get the download URL for 'images/stars.jpg'
let urlStr:String = (url?.absoluteString) ?? ""
let values = ["downloadURL": urlStr]
self.addImageURLToDatabase(uid: userID, values: values as [String : AnyObject])
}
}
})
}
}
func addImageURLToDatabase(uid:String, values:[String:AnyObject]){
let ref = Database.database().reference(fromURL: "https://exampleapp.firebaseio.com/")
let usersReference = ref.child("usersPosts").child((Auth.auth().currentUser?.uid)!)
usersReference.updateChildValues(values) { (error, ref) in
if(error != nil){
print(error)
return
}
self.parentVC?.dismiss(animated: true, completion: nil)
}
}
(TS)
onFileChanged(param){
var aa;
const file: File = param.target.files[0];
const metaData = { 'contentType': file.type };
const StorageRef: firebase.storage.Reference = firebase.storage().ref('/photos/Category/'+this.CategoryName);
const Store = StorageRef.put(file, metaData);
setTimeout(() => {
const UP: firebase.storage.UploadTask = Store;
UP.snapshot.ref.getDownloadURL().then(function (downloadURL) {
console.log('File available at', downloadURL);
aa = downloadURL;
});
}, 1000);
setTimeout(() => {
this.ImageLink = aa;
debugger;
}, 2000);
IN HTML
type="file" accept="image/*" #file style="display: none">
<img (click)="file.click()" style="margin-left: 10%"src="http://icons.iconarchive.com/icons/icons8/windows-8/512/Photo-Video-Stack-Of-Photos-icon.png" width="50px" />
import * as firebase as '#ionic/firebase'
I'm trying to fetch the user's fb profile pic but wasn't able to do so far. I'm trying to do something simple: the user log in with fb account and the app goes to another view where appears his name, email and profile picture. User's name and email are okay, but I can't get the picture!
The app is crashing with my actual code because apparently I'm unwrapping a nil optional value, but I don't know why it's nil.
My code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields":"id,email,name,picture.width(480).height(480)"])
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
print("Error: \(error)")
}
else
{
print("fetched user: \(result)")
let userName : NSString = result.valueForKey("name") as! NSString
print("User Name is: \(userName)")
let userEmail : NSString = result.valueForKey("email") as! NSString
print("User Email is: \(userEmail)")
let id = result.valueForKey("id") as! String
self.nameLabel.text = userName as String
self.emailLabel.text = userEmail as String
self.profilePic.image = self.getProfPic(id)
}
})
}
func getProfPic(fid: String) -> UIImage? {
if (fid != "") {
let imgURLString = "http://graph.facebook.com/" + fid + "/picture?type=large" //type=normal
let imgURL = NSURL(string: imgURLString)
let imageData = NSData(contentsOfURL: imgURL!)
let image = UIImage(data: imageData!) // CODE CRASHES IN HERE
return image
}
return nil
}
From your comments I understand that it crashes at the image assigning, you should be doing it with the conditional binding methodology of Swift (if let) in order to avoid unwrapping a nil optional value:
if let data = result["picture"]?["data"]
{
if let url = data["url"] as? String
{
profilePictureURL = url
}
}
Also, as you can see I am not using the valueForKey method.