storageRef.downloadURL isn't executing code inside - swift

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)")
let imageData = image.jpegData(compressionQuality: 0.75)
let metaData = StorageMetadata()
metaData.contentType = "image/jpg"
storageRef.putData(imageData!, metadata: metaData)
//Get url
let imageRef = storageRef
imageRef.downloadURL { url, error in
if let error = error {
print(error.localizedDescription)
}else{
print(url.absoluteString)
}
}
}
The code inside will not execute, it will skip everything and act like there is no code stored. I am unsure how completions work but I would imagine it would either print the error or print the url but it does neither. When I set a breakpoint here it doesn't even bother checking if error = error. Is there something I did wrong or anyone else having the same problem?
imageRef.downloadURL { url, error in
if let error = error {
print(error.localizedDescription)
}else{
print(url.absoluteString)
}
}

Your putData:metadata:completion: doesn't have completion block. You can access to download URL after upload so putData should be look like this.
storageRef.putData(imageData!, metadata: metaData) { (metadata, error) in
guard let metadata = metadata else {
// an error occurred!
return
}
// Metadata contains file metadata such as size, content-type.
let size = metadata.size
// You can only access to download URL after upload.
let imageRef = storageRef
imageRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// an error occurred!
return
}
}
}

Your API call is asynchronic so that why you need instead of print actually use completion block to return the result (error or value).

Related

Download firebase storage url not working

I have an image that is uploaded to the Firebase Storage but I can not get the downloadURL of this to save it in my database.
I already read a lot of posts on StackOverflow but there was nothing working out for me. The image is loaded into the storage but the error says that it is not existing.
let image = self.selectedImage
let imageData = UIImageJPEGRepresentation(image, 0.1)
let storageRef = Storage.storage().reference().child(uid)
storageRef.putData(imageData, metadata: nil) // i know that i can use a completion here but i left it for now
storageRef.downloadURL { url, error in
if let error = error {
print(error)
} else {
// do sth. else
}
}
These are the errors that I get:
FIRStorageErrorDomain Code=-13010 "Object W002MjRvi0d8JfVwImUJhH0ph2O2 does not exist."UserInfo={object=W002MjRvi0d8JfVwImUJhH0ph2O2,
ResponseBody={
"error": {
"code": 404,
"message": "Not Found. Could not get object"
}
}
ResponseErrorDomain=com.google.HTTPStatus, ResponseErrorCode=404}
Please check Storage Rules
service firebase.storage {
match /b/{bucket}/o {
match /{allPaths=**} {
allow read, write: if request.auth!=null;
}
}
}
In Your ViewController
import Firebase
import FirebaseAuth
import ImageIO
var imgData: NSData = NSData(data: UIImageJPEGRepresentation((self.img_Photo?.image)!, 0.5)!)
self.uploadProfileImageToFirebase(data: imgData)
func uploadProfileImageToFirebase(data:NSData){
guard let userID = Auth.auth().currentUser?.uid else {
return
}
let storageRef = Storage.storage().reference().child(“Images”).child(userID)
if data != nil {
storageRef.putData(data as Data, metadata: nil, completion: { (metadata, error) in
if(error != nil){
print(error)
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) ?? ""
}
}
})
}
}

error - metadata?.downloadURL()?.absoluteString [duplicate]

I just updated Firebase Storage to 5.0.0 and it looks like metadata.downloadURL() is not recognized anymore. (Value of type 'StorageMetadata' has no member 'downloadURL')
Though after looking in the documentation it should still be available :
https://firebase.google.com/docs/reference/swift/firebasestorage/api/reference/Classes/StorageMetadata#/c:objc(cs)FIRStorageMetadata(im)downloadURL
The project was cleaned & rebuilt already.
Am I missing something ?
Can you try Google Firebase docs
// Create a reference to the file you want to download
let starsRef = storageRef.child("images/stars.jpg")
// Fetch the download URL
starsRef.downloadURL { url, error in
if let error = error {
// Handle any errors
} else {
// Get the download URL for 'images/stars.jpg'
}
}
This is my version for Swift 3 / Swift 4.
Explanation of what happens in the code.
This is essentially the same answer as Sh_Khan's. But in his example the User already knows the bucket path. In my example, we get the path from an upload task. This was what has lead me to this question as well as what I think op was looking for as he was looking for metadata.downloadURL() replacement.
class StorageManagager {
private let storageReference: StorageReference
init() {
// first we create a reference to our storage
// replace the URL with your firebase URL
self.storageReference = Storage.storage().reference(forURL: "gs://MYAPP.appspot.com")
}
// MARK: - UPLOAD DATA
open func uploadData(_ data: Data, named filename: String, completion: #escaping (URL? , Error?) -> Void) {
let reference = self.storageReference.child(filename)
let metadata = StorageMetadata()
metadata.contentType = "ourType" // in my example this was "PDF"
// we create an upload task using our reference and upload the
// data using the metadata object
let uploadTask = reference.putData(data, metadata: metadata) { metadata, error in
// first we check if the error is nil
if let error = error {
completion(nil, error)
return
}
// then we check if the metadata and path exists
// if the error was nil, we expect the metadata and path to exist
// therefore if not, we return an error
guard let metadata = metadata, let path = metadata.path else {
completion(nil, NSError(domain: "core", code: 0, userInfo: [NSLocalizedDescriptionKey: "Unexpected error. Path is nil."]))
return
}
// now we get the download url using the path
// and the basic reference object (without child paths)
self.getDownloadURL(from: path, completion: completion)
}
// further we are able to use the uploadTask for example to
// to get the progress
}
// MARK: - GET DOWNLOAD URL
private func getDownloadURL(from path: String, completion: #escaping (URL?, Error?) -> Void) {
self.storageReference.child(path).downloadURL(completion: completion)
}
}
Let's try this code in Swift 4.2:
let imgData = UIImage.jpegData(self.imageView.image!)
let imageName = UUID().uuidString
let ref = Storage.storage().reference().child("pictures/\(imageName).jpg")
let meta = StorageMetadata()
meta.contentType = "image/jpeg"
self.uploadToCloud(data: imgData(0.5)!, ref: ref, meta: meta)
UploadToCloud Method:
` Method UploadToCloud
func uploadToCloud(data:Data, ref:StorageReference, meta:StorageMetadata) {
ref.putData(data, metadata: meta) { (metaData, error) in
if let e = error {
print("==> error: \(e.localizedDescription)")
}
else
{
ref.downloadURL(completion: { (url, error) in
print("Image URL: \((url?.absoluteString)!)")
})
}
}
}
This question pops up for all language searches. Hence for Kotlin, the solution is something of the kind below:
val photoRef = FirebaseStorage.getInstance()
.reference.child("images/stars.jpg")
// Code ommited - Do some saving - putFile
photoRef.downloadUrl.addOnSuccessListener({ uri ->
product.imageUrl = uri.toString()
})
However, this is not a good solution. You are better off saving the path and then re-constructing the full Url on demand. For example:
photoRef.downloadUrl.addOnSuccessListener({ uri ->
val imagePath = uri.toString()
// Save to database
})
Now, you can use it later, and only on demand:
FirebaseStorage.getInstance().reference.child(product.imageUrl).downloadUrl
.addOnSuccessListener { uri ->
String imageUrl = uri.toString()
// Load in images
}
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
if error != nil {
print(error as Any)
return
}
guard let uid = user?.user.uid else {
return
}
self.dismiss(animated: true, completion: nil)
//Добавляем картинку в firebase. Надо добавить в Pods file pod 'Firebase/Storage' и запустить терминал
let imageName = NSUUID().uuidString
let storageRef = Storage.storage().reference()
// Create a reference to the file you want to download
let starsRef = storageRef.child("profile_images").child("\(imageName).png")
let uploadData = self.profileImageView.image?.pngData()
starsRef.putData(uploadData!, metadata: nil, completion: { (metadata, error) in
if error != nil {
print(error as Any)
}
if let profileImageUrl = metadata?.path {
let values = ["name": name, "email": email, "profileImage": profileImageUrl]
self.registerUserIntoDatabaseWithUID(uid: uid, values: values)
}
})
}
If you are stuck in converting URL to string... you can try this
url.absoluteString

Firebase Storage Download Response Error

I have been able to successfully upload images to firebase storage but have been unable to successfully download the image.
I have attempted to download images in all three of the suggested ways on Firebase Storage Guides:
1) Download to NSData in memory
2) Download to an NSURL representing a file on device
3) Generate an NSURL representing the file online
An example is below of two different attempts:
func loadProfileImage() {
guard let currentUser = Auth.auth().currentUser else { return }
let profilePhotoFile = "ProfileImages/" + currentUser.uid
let reference = Storage.storage().reference(withPath: profilePhotoFile)
#1st Attempt downloading to memory:
reference.getData(maxSize: 1 * 1024 * 1024) { (data, error) in
if let error = error {
print("an error occurred: \(error)")
print("see data response: \(data)")
} else {
self.profileView.image = UIImage(data: data!)
}
}
#2nd Attempt with download url:
reference.downloadURL { (url, error) in
if let error = error {
print(error)
} else {
self.profileView.sd_setImage(with: url, placeholderImage:
#imageLiteral(resourceName: "placeHolderProfileView")) {
(image, error, type, reference2) in
print("reference location of image in the google
bucket: \(reference2)")
print("error retrieving image: \(String(describing:
error))")
print("type: \(type)")
print("image details: \(String(describing: image))")
}
}
}
}
Also tried using alamofire instead of SDWebImage to see if error code was same and it is the same see below:
Error Domain=FIRStorageErrorDomain Code=-13000 "An unknown error occurred, please check the server response." UserInfo={object=ProfileImages/6I2RhzFI3edYNph9J4WsaXXXX, ResponseErrorCode=100, bucket=bXXXX-production.appspot.com, NSLocalizedDescription=An unknown error occurred, please check the server response., ResponseErrorDomain=NSPOSIXErrorDomain, _kCFStreamErrorDomainKey=1, NSErrorPeerAddressKey={length = 28, capacity = 28, bytes = 0x1c1e01bb000000002607f8b040090813 ... 0000200a00000000}, _kCFStreamErrorCodeKey=100}
I have checked and rechecked the google storage bucket location and believe I have the reference location correct (using the same as the upload file path which works correctly).
Any help would be much appreciated
There you go :
func downloadImage(url : String,
completionHandler: #escaping (Bool?, UIImage?, String?) -> Void) -> Void
{
var success : Bool = false
var img : UIImage? = nil
var errorLog : String? = nil
let u = URL(string: url)
let task = URLSession.shared.dataTask(with: u!, completionHandler: { (data, response, error) in
if error != nil
{
errorLog = error?.localizedDescription
completionHandler(success, img, errorLog)
}
else
{
success = true
img = UIImage(data: data!)
completionHandler(usuccess, img, errorLog)
}
})
task.resume()
}
Get URL using :
imgReference.downloadURL { (url, error) in
guard let url = url else { return }
urlString = url.absoluteString
//do something with the urlString (such as download image)
}
Realized the error was in headers that were included when uploading the image:
I had originally listed the following with the upload, by commenting them out I was able to successfully download with SDWebImage and the suggestion from vbuzze.
let uploadMetadata = StorageMetadata()
uploadMetadata.contentType = "image/jpeg"
uploadMetadata.customMetadata = ["Profile Name" : currentUser.displayName] as? [String : String]

Trying to return downloadUrl from Firebase storage using a function

I'm trying to create a function that uploads images to Firebase Storage and returns the download url for their path so I can use it other parts of the app.
This is what the function looks like:
func uploadImage(to reference:StorageReference, image:UIImage) -> URL? {
let imageData = UIImageJPEGRepresentation(image, 0.2)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
var downloadURL = metadata.downloadURL()
reference.putData(imageData!, metadata: metadata) { (metadata, error) in
if error != nil {
print("Couldnt upload due to \(String(describing: error))")
}
downloadURL = metadata?.downloadURL()
}
return downloadURL!
}
I can't seem to get the result that I want as downloadUrl always returns nil. What am I doing wrong?
The problem here is that your function is returning before the upload is complete. In other words your function needs to return a callback, rather than a plain URL. Something like -
func uploadImage(to reference:StorageReference, image:UIImage, completion: #escaping (URL?) -> Void) {
let imageData = UIImageJPEGRepresentation(image, 0.2)
let metadata = StorageMetadata()
metadata.contentType = "image/jpeg"
var downloadURL = metadata.downloadURL()
reference.putData(imageData!, metadata: metadata) { (metadata, error) in
if error != nil {
print("Couldnt upload due to \(String(describing: error))")
completion(nil)
} else {
if let downloadUrl = metadata?.downloadURL() {
completion(downloadUrl)
} else {
completion(nil)
}
}
}
}

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