Swift 3 - How to download Profile Image from FireBase Storage - swift

I need help on retrieving Image from firebase storage, i learned how to save it but cant download it to current user profile.
here is my code so far:
FIRAuth.auth()?.createUserWithEmail(email!, password: password!, completion: { (authData, error) in
if error == nil {
if (password == "" || name == "" || email == "") {
self.showAlert("error", message: " Please Fill In The Blank")
}
if (password != confirm_password) {
self.showAlert("ERROR", message: "Password Don't Match")
}
}
else {
self.showAlert("ERROR", message: "Please Try Again")
}
let filePath = "\(FIRAuth.auth()!.currentUser!.uid)/\("userPhoto")"
var data = NSData()
let metaData = FIRStorageMetadata()
//let imageName = NSUUID().UUIDString
let storageRef = FIRStorage.storage().referenceForURL("gs://storageURL")
storageRef.child(filePath).putData(data, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
}
if let ProfileImageUrl = metaData?.downloadURL()?.absoluteString {
let values : [String : AnyObject] = ["name": name!, "email": email!, "profileImageUrl": ProfileImageUrl]
self.userPost(values)
}
}
})
}
//save data in Database
func userPost(values: [String: AnyObject]) {
let ref = FIRDatabase.database().referenceFromURL("https://databaseURL.firebaseio.com/")
ref.child("users").childByAutoId().setValue(values)
}
so i got that so far but cant figure out how to download it to user profile.
here is my code for user ProfileVC:
#IBOutlet weak var ProfileImg: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.retriveData()
ProfileImg.image = UIImage(named: "ic_account_circle.png")
imagePicker.delegate = self
}
func DownloadProfilePhoto() {
let storageRef = FIRStorage.storage().referenceForURL("gs://StorageURL.com")
let filePath = "\(FIRAuth.auth()!.currentUser!.uid)/\("userPhoto")"
}
Please Help.....

You need to download the image data, and then create an image. You should try this:
let storage = FIRStorage.storage()
var reference: FIRStorageReference!
reference = self.storage.referenceForURL("gs://appname.appspot.com/filePath")
reference.downloadURLWithCompletion { (url, error) in
let data = NSData(contentsOfURL: url!)
let image = UIImage(data: data!)
ProfileImg.image = image
}
You should check the Firebase documentation https://firebase.google.com/docs/storage/ios/download-files

For people looking for swift 4
let storage = Storage.storage()
var reference: StorageReference!
reference = storage.reference(forURL: "gs://appname.appspot.com/filePath")
reference.downloadURL { (url, error) in
let data = NSData(contentsOf: url!)
let image = UIImage(data: data! as Data)
cell.imgOutlet.image = image
}

Related

How to load image from Firebase into users avatar

I have a problem with loading images from firebase. I have two functions. One of them collect info about user, second one load users avatar image. Unfortunately images load after function creates new user. I know it will be problem with asynchronous of Firebase but I don't know how to set up DispatchQueue to work properly. Can you help me with that?
// function that load user image in user manager class
func loadUserImage(contactUserID: String, completion: #escaping (UIImage) -> Void) {
let userID = Auth.auth().currentUser!.uid
var userImageRef = self.storage.child("\(userID)/userImage.jpg")
var image = UIImage()
if contactUserID != "" {
userImageRef = self.storage.child("\(contactUserID)/userImage.jpg")
}
userImageRef.getData(maxSize: 5 * 1024 * 1024) { (data, error) in
if let error = error {
print("Error with retrieving data: \(error.localizedDescription)")
} else {
if data?.count != 0 {
image = UIImage(data: data!)!
} else {
image = UIImage(systemName: "person.circle.fill")!
}
completion(image)
}
}
}
// function that load user in contact manager class
func loadContactList(completion: #escaping ([User]) -> Void) {
let currentUserID = Auth.auth().currentUser!.uid
db.collection("contacts")
.document(currentUserID)
.collection("userContacts")
.addSnapshotListener { (querySnapshot, error) in
var contactList = [User]()
if let error = error {
print("Error with retrieving data from DB: \(error.localizedDescription)")
} else {
if let snapshotDocuments = querySnapshot?.documents {
for document in snapshotDocuments {
let data = document.data()
let uid = data["uid"] as! String
let name = data["name"] as! String
let email = data["email"] as! String
var contact = User(email: email, name: name, userID: uid)
DispatchQueue.global().sync {
self.userService.loadUserImage(contactUserID: uid) { (image) in
contact.photoURL = image
}
}
contactList.append(contact)
contactList.sort {
$0.name < $1.name
}
completion(contactList)
}
}
}
}
}
// Function implementation in viewController
func loadContactList() {
self.contactService.loadContactList { (contactArray) in
self.contactList = contactArray
self.tableView.reloadData()
}
}
What you can do is to store the image url in the firebase database and after that create this extension:
import UIKit
let imageCache: NSCache = NSCache<AnyObject, AnyObject>()
extension UIImageView {
func loadImageUsingCacheWithUrlString(urlString: String) {
self.image = nil
if let cachedImage = imageCache.object(forKey: urlString as AnyObject) as? UIImage {
self.image = cachedImage
return
}
let url = URL(string: urlString)
if let data = try? Data(contentsOf: url!) {
DispatchQueue.main.async(execute: {
if let downloadedImage = UIImage(data: data) {
imageCache.setObject(downloadedImage, forKey: urlString as AnyObject)
self.image = downloadedImage
}
})
}
}
}
And call:
if let url = data["imgUrl"] as? String {
self.myImageView.loadImageUsingCacheWithUrlString(urlString: url)
}
For that what you need to do is to create and initialize an UIImage object. If you are working with cell classes you need to create this object in the cell.

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.

How to store image from uiimageView in firebase

func uploadImage(){
let data = Data()
let storage = Storage.storage()
let storageRef = storage.reference()
let imagesRef = storageRef.child(imageView.image) //not sure how it's done
let uploadTask = imagesRef.putData(data, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
return
}
let size = metadata.size
imagesRef.downloadURL { (url, error) in
guard let downloadURL = url else {
return
}
}
}
}
Hi,I'm new to xcode. I would love to know how to upload image displayed on uiimageview to firebase when the above function is called.
you can do something like this:
func uploadImage(img1 :UIImage){
var data = NSData()
data = UIImageJPEGRepresentation(img1!, 0.8)! as NSData
// setting the upload path
// then choose the path where you want to store the image in the storage
let filePath = "\(userid)"
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{
//Storing the downloadURL..
let downloadURL = metaData!.downloadURL()!.absoluteString
}
}
}

How to work with async functions swift? Completion handlers [duplicate]

This question already has answers here:
Run code only after asynchronous function finishes executing
(2 answers)
Closed 5 years ago.
Im trying to wait for the function to process in order to show my image. I have try many things but none of this worked. I know this is an async function and basically i have to wait in order to get the right values but I dont know how to fix this function right here. I hope you can help me out. Thank you!
func createListProductsGood(Finished() -> void) {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
self.productPhoto.image = UIImage(data: data as Data)
}}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
self.productPhoto.image = UIImage(data: data as Data)
}}
}
}
})
finished()
}
Edited:
This is how my viewDidLoad looks like:
override func viewDidLoad() {
super.viewDidLoad()
setAcceptedOrRejected()
createListProductsGood{_ in
}
}
func createListProductsGood(finished: #escaping (_ imageData: Data) -> Void) {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
DispatchQueue.main.async {
self.productPhoto.image = UIImage(data: data as Data)
}
}}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
DispatchQueue.main.async {
self.productPhoto.image = UIImage(data: data as Data)
}
}}
}
}
})
}
This is my second method:
func setAcceptedOrRejected() {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
if self.ProductId == snapshot.key{
self.texto = prod["NotInterested"] as! String
self.refProducts.child("Products").child(self.ProductId).updateChildValues(["NotInterested": self.texto + ", " + self.userUID])
} })
}
You should change:
func createListProductsGood(Finished() -> void) {
to:
func createListProductsGood(finished: #escaping (_ something: SomeType) -> Void) {
or to be more specific:
func createListProductsGood(finished: #escaping (_ imageData: Data) -> Void) {
then wherever in your function you get the image, you call
finished(imageData)
so you can pass the imageData through a closure to where its needed.
then you call this function like this:
createListProductsGood{ imageData in
...
let image = UIImage(data: imageData)
// update UI from main Thread:
DispatchQueue.main.async {
self.productPhoto.image = image
}
}
Also:
it's not convention to use Finished(), you should use finished()
using void is wrong. You must use Void or ()
If you're having problems with closures and completionHandlers, I recommend you first try getting your hands dirty with a simple UIAlertController. See here. Try creating an action with a closure, e.g. see here
EDIT :
Thanks to Leo's comments:
func createListProductsGood(finished: #escaping(_ imageData: Data?, MyError?) -> Void) {
let value: Data?
let error = MyError.someError("The error message")
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active // REMOVE
self.productPhoto.image = UIImage(data: data as Data) // REMOVE
finished(data, nil) //ADD
}else{
finished(nil,error) //ADD
}
}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active // REMOVE
self.productPhoto.image = UIImage(data: data as Data) // REMOVE
finished(data,nil) //ADD
}else{
finished(nil,error) //ADD
}
}
}
}
})
}
And then you call it like:
createListProductsGood { imageData, error in guard let value = imageData, error == nil else { // present an alert and pass the error message return }
...
let image = UIImage(data: imageData)
// update UI from main Thread:
DispatchQueue.main.async {
self.ProductId = active
self.productPhoto.image = image } }
Basically this way the createListProductsGood takes in 2 closures, one for if the image is present, another for if an error was returned.

Use Facebook profile picture as you profile picture Swift

I am getting facebook's profile picture and displaying it as the profile picture in my app. Here is the code.
if let user = FIRAuth.auth()?.currentUser{
let photoUrl = user.photoURL
let name = user.displayName
self.FacebookUser.text = name
let storage = FIRStorage.storage()
//refer your particular storage service
let storageRef = storage.reference(forURL: "gs://gsignme-14416.appspot.com")
let profilePicRef = storageRef.child(user.uid+"/profile_pic.jpg")
profilePicRef.data(withMaxSize: 1 * 1024 * 1024, completion: { (data, error) -> Void in
if (error == nil){
self.FacebookPic.image = UIImage(data: data!)
}else{
print("Error downloading image:" )
}
})
if(self.FacebookPic.image == nil)
{
var profilePic = FBSDKGraphRequest(graphPath: "me/picture", parameters: ["height": 300, "width": 300, "redirect": false], httpMethod: "GET")
profilePic?.start(completionHandler: {(_ connection, result, error) -> Void in
// Handle the result
if error == nil {
if let dictionary = result as? [String: Any],
let data = dictionary["data"] as? [String:Any],
let urlPic = data["url"] as? String{
if let imageData = NSData(contentsOf: NSURL(string: urlPic)!as URL){
let uploadTask = profilePicRef.put(imageData as Data, metadata: nil) {
metadata, error in
if (error == nil)
{
let downloadurl = metadata!.downloadURL
}
else
{
print("Error in downloading image")
}
}
self.FacebookPic.image = UIImage(data: imageData as Data)
}}}})}
}else{
}
//The END of the Facebook user and picture code
I was able to get it working for a couple days and now it doesn't work anymore, I have gone through it line by line and I honestly can't figure out why it is not working.
I used this code:
func pictureFromFirebase(loginMethod: Int)
{
if loginMethod == 0 //FB
{
var profilePic = FBSDKGraphRequest(graphPath: "me/picture", parameters: ["height":300, "width":300, "redirect":false], httpMethod: "GET")
let profilePicRef = storageRef.child((user?.uid)!+"/profile_pic.jpg")
profilePicRef.data(withMaxSize: 1 * 1024 * 1024) { data, error in
if let error = error {
// Uh-oh, an error occurred!
// but we don't need to do anything yet. Try to download the profile pic
}
if (data != nil)
{
print("no need to download image from facebook")
self.profileImage.image = UIImage (data: data!)
}
else
{
// THIS IS THE BLOCK THAT HAS BEEN MOVED
// WHICH WILL NOW BE EXECUTED IN TWO CONDITIONS -
// 1. AN ERROR IN THE DOWNLOAD
// 2. NO PROFILE PIC AVAILABLE
print("downloading image from facebook")
profilePic?.start(completionHandler: {(_ connection, _ result, _ error) -> Void in
if (error == nil)
{
if let dictionary = result as? [String:Any], let data = dictionary["data"] as? [String:Any],
let urlPic = data["url"] as? String {
if let imageData = NSData(contentsOf: NSURL(string: urlPic)! as URL)
{
let uploadTask = profilePicRef.put(imageData as Data, metadata: nil){
metadata, error in
if (error == nil)
{
let downloadUrl = metadata!.downloadURL
}
else
{
print("error in downloading image")
}
}
self.profileImage.image = UIImage(data: imageData as Data)
}
}
}
})
}
}
}
}
from this post Second If statement gets called before first statement finished in one function and it worked
you just get your facebook profile pic. using this url and put the url in your UIImageview
let profilepicURl = "https://graph.facebook.com/\(user_id_fb)/picture?type=large" //user_id_fb like 1251246454544 your facebook ID