When a user taps their profile photo they have an option to select a new photo. I want to store the photo into my Storage and Database and update the view as soon as the imagePickerController is dismissed the new image shows on the screen, however nothing changes in the database and after logging in and out the old profile image is still there.
var user: User!
var dataBaseRef: DatabaseReference!{
return Database.database().reference()
}
var storageRef: StorageReference!{
return Storage.storage().reference()
}
func updatePhoto() {
let user = Auth.auth().currentUser
let newPhoto = profileImage.image
let imgData = UIImageJPEGRepresentation(newPhoto!, 0.7)!
let imagePath = "profileImage\(user.uid)/userPic.jpg"
let imageRef = storageRef.child(imagePath)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
imageRef.putData(imgData, metadata: metadata) { (metadata, error) in
if error == nil {
let changeRequest = Auth.auth().currentUser?.createProfileChangeRequest()
if let photoURL = metadata!.downloadURL(){
changeRequest?.photoURL = photoURL
}
changeRequest?.commitChanges(completion: { (error) in
if error == nil{
let user = Auth.auth().currentUser
let userInfo = ["firstLastName": self.nameOld, "email": self.emailString, "password": self.passwordOld, "location": self.locationOld, "interests": self.interestsOld, "biography": self.bioOld, "uid": self.uid, "photoURL": String(describing: user?.photoURL!)]
let userRef = self.dataBaseRef.child("users").child((user?.uid)!)
userRef.setValue(userInfo)
let credential = EmailAuthProvider.credential(withEmail: self.emailString, password: self.passwordOld)
user?.reauthenticate(with: credential) { error in
if let error = error {
print(error)
// An error happened.
} else {
print("AUTHENTICATED")
// User re-authenticated.
}
}
print("user info set")
}
})
}}}
func loadUserInfo(){
let userRef = dataBaseRef.child("users/\(Auth.auth().currentUser!.uid)")
userRef.observe(.value, with: { (snapshot) in
let user = Users(snapshot: snapshot)
if let username = user.firstLastName{
self.name.text = username
self.nameOld = username
}
if let userLocation = user.location{
self.location.text = userLocation
self.locationOld = userLocation
}
if let bio = user.biography{
self.biog.text = bio
self.bioOld = bio
}
if let interests = user.interests{
self.interests.text = interests
self.interestsOld = interests
}
if let imageOld = user.photoURL{
// let imageURL = user.photoURL!
self.storageR.reference(forURL: imageOld).getData(maxSize: 10 * 1024 * 1024, completion: { (imgData, error) in
if error == nil {
DispatchQueue.main.async {
if let data = imgData {
self.profileImage.image = UIImage(data: data)
}
}
}else {
print(error!.localizedDescription)
}
}
)}
}) { (error) in
print(error.localizedDescription)
}
}
override func viewDidLoad() {
super.viewDidLoad()
setGestureRecognizersToDismissKeyboard()
loadUserInfo()
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage{
self.profileImage.image = image
updatePhoto()
}
else if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
self.profileImage.image = image
updatePhoto()
}
self.dismiss(animated: true, completion: nil)
}
From apple documentation, UIImagePickerControllerEditedImage: Specifies an image edited by the user and UIImagePickerControllerOriginalImage: Specifies the original, uncropped image selected by the user.
If the user does not edit the selected image, you should also call updatePhoto() function in order to upload the selected photo:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
if let image = info[UIImagePickerControllerOriginalImage] as? UIImage{
self.profileImage.image = image
updatePhoto()
}
else if let image = info[UIImagePickerControllerEditedImage] as? UIImage {
self.profileImage.image = image
updatePhoto()
}
self.dismiss(animated: true, completion: nil)
}
Related
I've got a problem regarding Firebase and the upload of pictures..
I've been tried to follow the Firebase doc but I'm not sur to do the right things ...
In my application I want to send in firebase the value of 2 textfields and 1 segmented control plus one picture which is coming from the iphone's gallery.
well my save button :
#IBAction func saveBtnWasPressed(_ sender: Any) {
//Informations from the segmented control
if isMe == false {// Si SE
acftType = "SE"
}else if isMe == true {//Si ME
acftType = "ME"
}
let ref = Database.database().reference()
let userID = Auth.auth().currentUser?.uid
let usersPlanes : NSDictionary = [ "Registration" : self.acftRegTxtField.text!,
"model": self.acftModelTxtField.text!,
"Type" : self.acftType]
if isMe == false {// Si SE
ref.child("Planes").child(userID!).child("SE").childByAutoId().setValue(usersPlanes)
}else if isMe == true {//Si ME
ref.child("Planes").child(userID!).child("ME").childByAutoId().setValue(usersPlanes)
}else{
print("Error: Impossible to find the type of aircraft...")
}
let Dpalert = UIAlertController(title: nil, message: "Your informations as been upload", preferredStyle: .alert)
Dpalert.addAction(UIAlertAction(title: "Roger", style: .cancel, handler: nil))
self.present(Dpalert, animated: true)
}
And my function to allow user to select an image from his gallery is :
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let userID = Auth.auth().currentUser?.uid
self.dismiss(animated: true, completion: nil)
if let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.planImageView.image = selectedImage
var data = Data()
data = selectedImage.jpegData(compressionQuality: 0.75)!
}else{
print("Error : Impossible to deal with this image...")
}
let imageRef = Storage.storage().reference().child("Images").child(userID!).child(randomString(20));
let uploadPict = imageRef.putData(data, metadata: nil){ (metadata, error) in
guard let metadata = metadata else {
return
}
let size = metadata.size
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
return
}
}
}
But nothing appears in firebase when the picture is load in the app and How can I add it in the same folder as my first 3 information send with the save button ?
I'm totally lost with all this information. How can I solve my problem ?
Thanks very much for your help !
Flyer-74
In this function
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let userID = Auth.auth().currentUser?.uid
self.dismiss(animated: true, completion: nil)
if let selectedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
self.planImageView.image = selectedImage
var data = Data()
data = selectedImage.jpegData(compressionQuality: 0.75)!
}else{
print("Error : Impossible to deal with this image...")
}
let imageRef = Storage.storage().reference().child("Images").child(userID!).child(randomString(20));
let uploadPict = imageRef.putData(data, metadata: nil){ (metadata, error) in
guard let metadata = metadata else {
return
}
let size = metadata.size
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
return
}
}
}
Try to put an output when your putData fail
let uploadPict = imageRef.putData(data, metadata: nil){ (metadata, error) in
guard let metadata = metadata else {
print("Error with upload \(String(describing: error?.localizedDescription))")
return
}
let size = metadata.size
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
print("Error with download URL: \(String(describing: error?.localizedDescription))")
return
}
}
Maybe it will help you to recognize the error; tell me what you got in the error
I am trying to write a function that will allow a user to set a new profile image, new image will be uploaded and the old image will be removed from firebase storage.
I have two functions that will do this and they work individually, however if I run the upload after the delete function the new image will not upload, even though I get a success message in the console nothing appears in the storage. Ideally I would like to remove first and the set the new image, and I have tried doing this multiple ways; completion handlers, adding delays but nothing has worked. I now even have two buttons one controlling each function to test this but this is still not working. What am I missing?? Any help would be great as ive spent hours racking my brains with this!
Here is my complete code for the VC:
//
// LandingVC.swift
// Login
//
// Created by George Woolley on 07/11/2017.
// Copyright © 2017 George Woolley. All rights reserved.
//
import UIKit
import FBSDKLoginKit
import SwiftKeychainWrapper
import Firebase
class MyAccountVC: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
#IBOutlet weak var profilePictureImg: UIImageView!
#IBOutlet weak var usernameField: UILabel!
#IBOutlet weak var saveButton: UIButton!
#IBOutlet weak var changeProfilePicButton: UIButton!
let picker = UIImagePickerController()
let myUID = KeychainWrapper.standard.string(forKey: "uid")
override func viewDidLoad() {
super.viewDidLoad()
picker.delegate = self
if myUID == nil {
print("You are not logged in")
} else {
let ref = DataService.ds.DBCurrentUser
ref.child("MyDetails").observe(.value, with: { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
if snap.key == "username" {
self.usernameField.text = snap.value as? String
}
if snap.key == "profileImageURL" {
if let url = snap.value as? String {
let ref = Storage.storage().reference(forURL: url)
ref.getData(maxSize: 2 * 1024 * 1024, completion: { (data, error) in
if error != nil {
print("An error has occured downloading image")
} else {
print("Image downloaded")
if let imageData = data {
if let img = UIImage(data: imageData) {
self.profilePictureImg.image = img
}
}
}
})
}
}
}
}
})
}
}
func removeImgFromFirebaseStorage() {
let ref = DataService.ds.DBCurrentUser.child("MyDetails")
ref.observe(.value) { (snapshot) in
if let snapshots = snapshot.children.allObjects as? [DataSnapshot] {
for snap in snapshots {
if snap.key == "profileImageURL" {
if let url = snap.value as? String {
let img = Storage.storage().reference(forURL: url)
img.delete(completion: { (error) in
if error != nil {
print("Error is \(String(describing: error))")
} else {
print("Success")
}
})
}
}
}
}
}
saveButton.isHidden = false
changeProfilePicButton.isHidden = true
}
func uploadImageToFirebase() {
if let imageToUpload = profilePictureImg.image {
if let imageData = UIImageJPEGRepresentation(imageToUpload, 0.2) {
let metaData = StorageMetadata()
metaData.contentType = "image/jpeg"
let imageUID = UUID().uuidString
DataService.ds.StorageProfile.child(imageUID).putData(imageData, metadata: metaData, completion: { (metadata, error) in
if error != nil {
print("Error occured uploading profile image")
} else {
print("Sucess")
if let downloadURL = metadata?.downloadURL()?.absoluteString {
DataService.ds.DBCurrentUser.child("MyDetails").child("profileImageURL").setValue(downloadURL)
}
}
})
}
}
saveButton.isHidden = true
changeProfilePicButton.isHidden = false
}
#IBAction func saveButonPressed(_ sender: Any) {
uploadImageToFirebase()
}
#IBAction func changeProfilePicturePressed(_ sender: Any) {
picker.allowsEditing = true
picker.sourceType = .photoLibrary
picker.mediaTypes = UIImagePickerController.availableMediaTypes(for: .photoLibrary)!
present(picker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
profilePictureImg.contentMode = .scaleAspectFill
profilePictureImg.image = chosenImage
dismiss(animated: true, completion: removeImgFromFirebaseStorage)
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
dismiss(animated: true, completion: nil)
}
#IBAction func logOffPressed(_ sender: Any) {
KeychainWrapper.standard.removeObject(forKey: "uid")
performSegue(withIdentifier: "loginVC", sender: nil)
let fbLogin = FBSDKLoginManager()
fbLogin.logOut()
try! Auth.auth().signOut()
}
}
errors encountered while discovering extensions:
Error Domain=PlugInKit Code=13 "query cancelled"
UserInfo={NSLocalizedDescription=query cancelled} 2017-10-03
11:08:38.585084+0530 NewChatDemo[858:20663] * Terminating app due to
uncaught exception 'NSInvalidArgumentException', reason: '*
-[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[1]
Here is my Swift 4 code:
#objc func handleRegister()
{
guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text else{
print("invalid form")
return
}
Auth.auth().createUser(withEmail: email, password: password, completion: { (user: User? , error) in
if error != nil
{
print(error)
return
}
guard let uid = user?.uid else{
return
}
//successfully authenticated user
let storageRef = Storage.storage().reference()
if let uploadData = UIImagePNGRepresentation(self.profileImageView.image!){
storageRef.putData(uploadData, metadata: nil, completion: {(metadata, error) in
if error != nil
{
print(error)
}
else
{
print(metadata)
}
})
}
let ref = Database.database().reference(fromURL: "https://newchatapp-470cb.firebaseio.com/")
let uerReference = ref.child("users").child(uid)
let values = ["name": name, "email": email]
uerReference.updateChildValues(values, withCompletionBlock: { (err, ref) in
if err != nil
{
print(err)
return
}
print("Successfully saved user in firebase db")
})
})
}
#objc func handleSelectProfileImageView()
{
let picker = UIImagePickerController()
picker.delegate = self
picker.allowsEditing = true
present(picker, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
print(info)
var selectedImageFromPicker: UIImage?
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage
{
selectedImageFromPicker = editedImage as! UIImage
}else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage{
selectedImageFromPicker = originalImage as! UIImage
}
dismiss(animated: true, completion: nil)
if var selectedImage = selectedImageFromPicker
{
profileImageView.image = selectedImage
}
}
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
print("Cancel picker")
}
I think this error is due to this line:
let storageRef = Storage.storage().reference()
You need to give the name of the file that you want to upload ,so you can do something like this:
let storageRef = Storage.storage().reference().child("yourImageName.png")
The new Firebase lets you have a NSURL profile image property for the user, but I'm having trouble displaying it. Here is how I'm saving it......
let user = FIRAuth.auth()?.currentUser
if let user = user {
let changeRequest = user.profileChangeRequest()
changeRequest.photoURL = searchURL
changeRequest.commitChangesWithCompletion { error in
if let _ = error {
print("Try Again")
} else {
print("Photo Updated")
self.profileImage.image = image
}
}
}
And this is how I'm trying to retrieve it....
if let user = FIRAuth.auth()?.currentUser
{
let name = user.displayName
let pic = user.photoURL
self.displayNameLBL.text = name
if pic != nil
{
print(pic!)
let urlString: String = pic!.path!
self.profileImage.image = UIImage(named: urlString)
//self.profileImage.image = UIImage(data: pic! as NSURL)
}else
{
self.profileImage.image = UIImage(named: "imagePlaceholder")
}
}
I'm getting user.displayName but not the image.
You are trying to display an URL not an image you need to download the image first you can use this extension:
extension UIImageView {
func downloadedFrom(link: String, contentMode mode: UIViewContentMode = .ScaleAspectFit) {
guard let url = NSURL(string: link) else { return }
contentMode = mode
NSURLSession.sharedSession().dataTaskWithURL(url) { (data, response, error) in
guard
let httpURLResponse = response as? NSHTTPURLResponse where httpURLResponse.statusCode == 200,
let mimeType = response?.MIMEType where mimeType.hasPrefix("image"),
let data = data where error == nil,
let image = UIImage(data: data)
else { return }
dispatch_sync(dispatch_get_main_queue()) {
self.image = image
}
}.resume()
}
}
usage :
imageView.downloadedFrom(stringURL, contentMode: .ScaleAspectFill)
I have a UIImagePicker set up within my app that works fine. I would like to upload a profile picture to Firebase when my UIImage picker has been chosen. Here is my function for when a picture has been selected.
//image picker did finish code
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
profilePic.contentMode = .ScaleAspectFill
profilePic.image = chosenImage
profilePic.hidden = false
buttonStack.hidden = true
changeButtonView.hidden = false
self.statusLabel.text = "Here is Your Profile Picture"
dismissViewControllerAnimated(true, completion: nil)
}
The new documentation states that we need to declare a NSURl in order to upload a file. Here is my attempt to find the NSURL of the given file, but it doesn't work. Here is the documentation and a link to it:https://firebase.google.com/docs/storage/ios/upload-files#upload_from_data_in_memory
// File located on disk
let localFile: NSURL = ...
// Create a reference to the file you want to upload
let riversRef = storageRef.child("images/rivers.jpg")
// Upload the file to the path "images/rivers.jpg"
let uploadTask = riversRef.putFile(localFile, metadata: nil) { metadata, error in
if (error != nil) {
// Uh-oh, an error occurred!
} else {
// Metadata contains file metadata such as size, content-type, and download URL.
let downloadURL = metadata!.downloadURL
}
}
Here is my attempt for retrieving the NSURL of the UIImagePicker:
//image picker did finish code
func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
let chosenImage = info[UIImagePickerControllerOriginalImage] as! UIImage
//getting the object's url
let imageUrl = info[UIImagePickerControllerReferenceURL] as! NSURL
let imageName = imageUrl.lastPathComponent
let documentDir = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true).first! as String;
let photoUrl = NSURL(fileURLWithPath: documentDir)
let localPath = photoUrl.URLByAppendingPathComponent(imageName!)
self.localFile = localPath
profilePic.contentMode = .ScaleAspectFill
profilePic.image = chosenImage
profilePic.hidden = false
buttonStack.hidden = true
changeButtonView.hidden = false
self.statusLabel.text = "Here is Your Profile Picture"
dismissViewControllerAnimated(true, completion: nil)
}
I believe that I am also running into difficulties if the image was taken from the camera instead of the gallery since it is not saved on the device yet. How do I find this image/snapshots's NSURL?
Here is my method to upload and download the user profile photo from firebase storage:
func imagePickerController(picker: UIImagePickerController, didFinishPickingImage image: UIImage, editingInfo: [String : AnyObject]?) {
userPhoto.image = image
dismissViewControllerAnimated(true, completion: nil)
var data = NSData()
data = UIImageJPEGRepresentation(userPhoto.image!, 0.8)!
// set upload path
let filePath = "\(FIRAuth.auth()!.currentUser!.uid)/\("userPhoto")"
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpg"
self.storageRef.child(filePath).putData(data, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
}else{
//store downloadURL
let downloadURL = metaData!.downloadURL()!.absoluteString
//store downloadURL at database
self.databaseRef.child("users").child(FIRAuth.auth()!.currentUser!.uid).updateChildValues(["userPhoto": downloadURL])
}
}
}
I also store the Image URL into firebase database and check if the user has a profile photo or you might get a crash:
//get photo back
databaseRef.child("users").child(userID!).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
// check if user has photo
if snapshot.hasChild("userPhoto"){
// set image locatin
let filePath = "\(userID!)/\("userPhoto")"
// Assuming a < 10MB file, though you can change that
self.storageRef.child(filePath).dataWithMaxSize(10*1024*1024, completion: { (data, error) in
let userPhoto = UIImage(data: data!)
self.userPhoto.image = userPhoto
})
}
})
Simple 2020 example
Don't forget to add pod 'Firebase/Storage' to your podfile, and pod install
Add two delegates to your class
class YourScreen: UIViewController,
UIImagePickerControllerDelegate, UINavigationControllerDelegate {
Storyboard, button, link to ...
#IBAction func tapCameraButton() {
let picker = UIImagePickerController()
picker.allowsEditing = true
picker.delegate = self
present(picker, animated: true)
}
and then
func imagePickerController(_ picker: UIImagePickerController,
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
guard let im: UIImage = info[.editedImage] as? UIImage else { return }
guard let d: Data = im.jpegData(compressionQuality: 0.5) else { return }
let md = StorageMetadata()
md.contentType = "image/png"
let ref = Storage.storage().reference().child("someFolder/12345678.jpg")
ref.putData(d, metadata: md) { (metadata, error) in
if error == nil {
ref.downloadURL(completion: { (url, error) in
print("Done, url is \(String(describing: url))")
})
}else{
print("error \(String(describing: error))")
}
}
dismiss(animated: true)
}
But where do you put the image?
At the code,
... .child("someFolder/12345678.jpg")
Almost inevitably,
• as the folder name use the user's id, the chat id, the feed id or a similar concept
• for the name, the only possibility is a uuid
Hence almost always
let f = chatId + "/" + UUID().uuidString + ".jpg"
let ref = Storage.storage().reference().child(f)
Working in Swift 4.2
Here i am doing click on image i have added tapGesture then it open gallery then selected image that upload in Firebase and also i add textField Value Too i hope it helps you Thank You
import UIKit
import Firebase
class ViewController: UIViewController {
#IBOutlet var myImageView: UIImageView!
#IBOutlet var txtText: UITextField!
var ref = DatabaseReference.init()
var imagePicker = UIImagePickerController()
override func viewDidLoad() {
super.viewDidLoad()
self.ref = Database.database().reference()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.openGalleryClick(tapGesture:)))
myImageView.isUserInteractionEnabled = true
myImageView.addGestureRecognizer(tapGestureRecognizer)
myImageView.backgroundColor = UIColor.red
}
#objc func openGalleryClick(tapGesture: UITapGestureRecognizer){
self.setupImagePicker()
}
#IBAction func btnSaveClick(_ sender: UIButton) {
self.saveFIRData()
}
func saveFIRData(){
self.uploadMedia(image: myImageView.image!){ url in
self.saveImage(userName: self.txtText.text!, profileImageURL: url!){ success in
if (success != nil){
self.dismiss(animated: true, completion: nil)
}
}
}
}
func uploadMedia(image :UIImage, completion: #escaping ((_ url: URL?) -> ())) {
let storageRef = Storage.storage().reference().child("myimage.png")
let imgData = self.myImageView.image?.pngData()
let metaData = StorageMetadata()
metaData.contentType = "image/png"
storageRef.putData(imgData!, metadata: metaData) { (metadata, error) in
if error == nil{
storageRef.downloadURL(completion: { (url, error) in
completion(url)
})
}else{
print("error in save image")
completion(nil)
}
}
}
func saveImage(userName:String, profileImageURL: URL , completion: #escaping ((_ url: URL?) -> ())){
let dict = ["name": "Yogesh", "text": txtText.text!, "profileImageURL": profileImageURL.absoluteString] as [String : Any]
self.ref.child("chat").childByAutoId().setValue(dict)
}
}
extension ViewController: UINavigationControllerDelegate, UIImagePickerControllerDelegate{
func setupImagePicker(){
if UIImagePickerController.isSourceTypeAvailable(.savedPhotosAlbum){
imagePicker.sourceType = .savedPhotosAlbum
imagePicker.delegate = self
imagePicker.allowsEditing = true
self.present(imagePicker, animated: true, completion: nil)
}
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
let image = info[UIImagePickerController.InfoKey.originalImage] as! UIImage
myImageView.image = image
picker.dismiss(animated: true, completion: nil)
}
}