Images not caching with Firebase url but caching with other - swift

I have little problem. I got caching to work with the following URL:
let URL = NSURL(string: "https://raw.githubusercontent.com/onevcat/Kingfisher/master/images/kingfisher-\(indexPath.row + 1).jpg")!
But can't get it to work like this with this URL:
FIRStorage.storage().reference().child("\(productImageref!).png").downloadURLWithCompletion({(url, error)in
if error != nil{
print(error)
return
}else{
cell.snusProductImageView.kf_setImageWithURL(url , placeholderImage: nil,
optionsInfo: [.Transition(ImageTransition.Fade(1))],
progressBlock: { receivedSize, totalSize in
print("\(indexPath.row + 1): \(receivedSize)/\(totalSize)")
},
completionHandler: { image, error, cacheType, imageURL in
print("\(indexPath.row + 1): Finished")
})
}
})
What I am doing wrong here? Can you point me to the right direction? For caching I use the 3rd party library "KingFisher"
Edit: Firebase guy Mike McDonald's quote
"The Github one has Cache-Control: max-age=300 while Firebase Storage
doesn't have cache control set by default (you can set it when you
upload the file, or change it by updating metadata), so I assume
that's why KingFisher isn't caching it."
KingFisher owner quotes.

Like Firebase guy Mike McDonald said I directly set the cache-control when uploading the image to the Firebase storage. Then it will be cached correctly when loading it later.
(Configuration: Swift3, Firebase Storage 3.11.0)
let storage = FIRStorage.storage()
let storageRef = storage.reference(forURL: yourFirebaseStorageReferenceURL)
let imageRef = storageRef.child("profileImages/\(imageUUID).jpg")
let metadata = FIRStorageMetadata()
metadata.contentType = "image/jpeg"
// THIS is the important part
metadata.cacheControl = "public,max-age=2592000"
let uploadTask = imageRef.put(imageData, metadata: metadata) { (metadata, error) in
if let error = error
{
completion(.failure(error as NSError))
}
guard let metadata = metadata, let downloadURL = metadata.downloadURL()?.absoluteString else {
// Handle upload error
return
}
// Handle successful upload
}
When I download the image, the cache-control is set

The way I got them caching was that I had to write all image url-s into JSON like this:
.setValue(url!.absoluteString)
And then read those as url-s

Related

Trouble Uploading Multiple Images to Firebase Storage

I have an array of image data(in bytes), and I want to upload them to storage and get the download URL for all of them. But after uploading lots of pictures, there is only one file appears in the storage under the reference. I do get the download URLs, but only the last one works, the rest all say access denied.
I suspect it is because I am putting all the pictures under the same storage reference and the picture substitutes every time. But what can I do to upload multiple images?
My upload code:
var picturesURL: [String] = [] // I want to put the download URL in this array
if self.imageButton.isSelected{
picturesURL = []
for photoData in self.pictures{ // pictures is the array that stores all the photo data
self.storage.child("images/file.png").putData(photoData, metadata: nil) { _, error in
guard error == nil else{
print("Failed to upload")
return
}
self.storage.child("images/file.png").downloadURL (completion: { url, error in
guard let url = url, error == nil else{
print("problems getting url")
return
}
let urlString = url.absoluteString
picturesURL.append(urlString)
print(urlString)
//UserDefaults.standard.set(urlString, forKey: "url")
})
}
}

How to upload a PDF File to firebase that is already in memory

I have seen posts and read the docs on how to upload a file with a url reference to local disk, however, I cannot find anything about how to do this with a file in memory? The file is an array of pdfs. I take it I have to have some kind of URL reference to that file. Where would I URL reference a pdf in memory? Anybody know?
Edit:
I used this code from the Docs, however, I'm getting the following error for putData: Type of expression is ambiguous without more context
Which I think means the constructor is not expecting a type of PDFDoc?
#IBAction func confirmUploadButtonTapped(_ sender: Any) {
let uuid = UUID().uuidString
// Create a root reference
let storage = Storage.storage()
// Create a storage reference from our storage service
let storageRef = storage.reference()
// Data in memory
let data = Data()
// Create a reference to the file you want to upload
let pdfRef = storageRef.child("docRequest/" + uuid + "/" + ".pdf")
// Upload the file to the path "images/rivers.jpg"
let uploadTask = pdfRef.putData(pdfDocument, metadata: nil) { (metadata, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
print(error)
return
}
// Metadata contains file metadata such as size, content-type.
let size = metadata.size
// You can also access to download URL after upload.
pdfRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
print(error)
return
}
}
}
If you have the data for the PDF in memory already, you can upload that data to Firebase Storage by calling putData. See for an example the documentation on Upload from data in memory, or this code in the Firebase quickstart project.
This line is the issue
let pdfRef = storageRef.child("docRequest/" + uuid + "/" + ".pdf")
because it will create a path of
docRequest/xxxxx/.pdf
So you should use this
let pdfRef = storageRef.child("docRequest/" + uuid + ".pdf")
when will be a path of
docRequest/xxxxx.pdf
Also, please ensure that when that file is written to storage, you also save the url in Firebase so you can get to it later.
As far as a PDF in memory, the PDFKit (which I think you're using) has functions to work with that, check into pdfData(actions:) renderer to produce a Data object

FirebaseStorageUI + sdwebimage

I've updated firebase pods today and I've encountered some issues.
First of all I was getting this error:
No such module 'FirebaseStorageUI'
Now I've changed it into:
import FirebaseStorage
import FirebaseUI
This seems to work as all functions are still available like storage and sd_webimage.
Now I'm getting this error:
Cannot convert value of type 'StorageReference' to expected argument type 'URL?'
Below code worked before the update
let storageRef = Storage.storage().reference().child("user/thomas_1")
self.profileImageView.sd_setImage(with: storageRef, placeholderImage: UIImage(named: "placeholder"), completed: { (image, error, cacheType, storageRef) in
if image != nil && error != nil {
UIView.animate(withDuration: 0.3) {
self.profileImageView.alpha = 1
}
}
})
Now it didn't and it gave me that error, I changed it into a url but it still doesn't work or is giving me errors. Following the examples in the documentation and sample apps above method should work. I also found out that the url is a direct gs:// path and that is not reachable.
If anyone can point out (maybe the obvious) mistake that would be very helpful and thanks in advance.
You will need to get the download URL of the image, which can be obtained through the downloadURL method, like this:
let storageRef = Storage.storage().reference().child("user/thomas_1")
storageRef.downloadURL { url, error in
guard let url = url else { return }
self.profileImageView.sd_setImage(with: storageRef, placeholderImage: UIImage(named: "placeholder"), completed: { (image, error, cacheType, storageRef) in
if image != nil && error != nil {
UIView.animate(withDuration: 0.3) {
self.profileImageView.alpha = 1
}
}
})
}
This will work, but instead of generating the download URL every time like the above example, I'd recommend generating it right after initially uploading the image, and then storing this download URL in whatever database you use.

Swift Firebase Storage How to retrieve image with unknow name(NSUUID)

I am making a function to retrieve the url as the user Image. However, my upload image name function created by NSUUID. Therefore, I would not know what is the name of each user profile picture. How could I improve my code to get the user imgae for every user instead of hard coding the img name?
func getUserProfilePic(){
let uid = FIRAuth.auth()?.currentUser?.uid
let profilePath = refStorage.child("\(uid)").child("profilePic/xxxxx.jpg") // xxxxx = NSUUID
profilePath.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
print("got no pic")
} else {
let profilePic: UIImage! = UIImage(data: data!)
self.imageV.image = profilePic
print("got pic")
}
}
}
The path is uid/profilePic/--<-file name->--
upload function
func uploadingPhoto(){
let uid = FIRAuth.auth()?.currentUser?.uid
let imgName = NSUUID().UUIDString + ".jpg"
let filePath = refStorage.child("\(uid!)/profilePic/\(imgName)")
var imageData = NSData()
imageData = UIImageJPEGRepresentation(profilePic.image!, 0.8)!
let metaData = FIRStorageMetadata()
metaData.contentType = "image/jpg"
let uploadTask = filePath.putData(imageData, metadata: metaData){(metaData,error) in
if let error = error {
print(error.localizedDescription)
return
}else{
let downloadURL = metaData!.downloadURL()!.absoluteString
let uid = FIRAuth.auth()?.currentUser?.uid
let userRef = self.dataRef.child("user").child(uid!)
userRef.updateChildValues(["photoUrl": downloadURL])
print("alter the new profile pic and upload success")
}
}
I highly recommend using Firebase Storage and the Firebase Realtime Database together to store that UUID -> URL mapping, and to "list" files. The Realtime Database will handle offline use cases like Core Data as well, so there's really no reason to futz with Core Data or NSUserDefaults. Some code to show how these pieces interact is below:
Shared:
// Firebase services
var database: FIRDatabase!
var storage: FIRStorage!
...
// Initialize Database, Auth, Storage
database = FIRDatabase.database()
storage = FIRStorage.storage()
Upload:
let fileData = NSData() // get data...
let storageRef = storage.reference().child("myFiles/myFile")
storageRef.putData(fileData).observeStatus(.Success) { (snapshot) in
// When the image has successfully uploaded, we get it's download URL
let downloadURL = snapshot.metadata?.downloadURL()?.absoluteString
// Write the download URL to the Realtime Database
let dbRef = database.reference().child("myFiles/myFile")
dbRef.setValue(downloadURL)
}
Download:
let dbRef = database.reference().child("myFiles")
dbRef.observeEventType(.ChildAdded, withBlock: { (snapshot) in
// Get download URL from snapshot
let downloadURL = snapshot.value() as! String
// Create a storage reference from the URL
let storageRef = storage.referenceFromURL(downloadURL)
// Download the data, assuming a max size of 1MB (you can change this as necessary)
storageRef.dataWithMaxSize(1 * 1024 * 1024) { (data, error) -> Void in
// Do something with downloaded data...
})
})
For more information, see Zero to App: Develop with Firebase, and it's associated source code, for a practical example of how to do this.
There can be two ways to go about this :-
1.) Store the Firebase Storage path of users profile_picture in your Firebase database and retrieve every time before you start downloading your profile picture.
2.) Store the file path in CoreData every time your user uploads a profile picture and hit that path every time to get file_Path to where you stored that user's profile_pic .
Storing the path :-
func uploadSuccess(metadata : FIRStorageMetadata , storagePath : String)
{
print("upload succeded!")
print(storagePath)
NSUserDefaults.standardUserDefaults().setObject(storagePath, forKey: "storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)")
//Setting storagePath : (file path of your profile pic in firebase) to a unique key for every user "storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)"
NSUserDefaults.standardUserDefaults().synchronize()
}
Retrieving your path, Every time you start downloading your image :-
let storagePathForProfilePic = NSUserDefaults.standardUserDefaults().objectForKey("storagePath.\((FIRAuth.auth()?.currentUser?.uid)!)") as? String
Note :- i am using currentUser ID, you can use USER SPECIFIC id ,if you want to download multiple users profile pics, all you need to do is put their uid in place.

Downloading metadata from Firebase Storage

unable to get access to my metadata on the new Firebase. Able to get download of article and have them display.
storageRef.child(article).metadataWithCompletion { (metadata, error) in
if error != nil{
print("error getting metadata")
}else{
let metadata1 = FIRStorageMetadata()
let nameMeta = metadata1.downloadURLs
print("nameMeta is \(nameMeta)")
}
output display:
nameMeta is nil
You shouldn't create a new metatada instance, you should use the one already provided in the closure.
else {
let downloadUrl = metadata.downloadUrl()
print(downloadUrl)
}