Currently there is no direct API to uploading/downloading the files in batch. We can not use loop because all the tasks perform asynchronously
. What we can do is to use a recursive function.
Core Logic
let images = [image1, image2, image3, image4]
func uploadImage(forIndex index: Int) {
if index < images.count {
/// Perform uploading
/// After successfully uploading call this method again by increment the **index = index + 1**
return;
}
/// All images have been uploaded successfully
}
Full Code Example
1. I created a custom class for file uploading
import UIKit
import Firebase
class FirFile: NSObject {
/// Singleton instance
static let shared: FirFile = FirFile()
/// Path
let kFirFileStorageRef = Storage.storage().reference().child("Files")
/// Current uploading task
var currentUploadTask: StorageUploadTask?
func upload(data: Data,
withName fileName: String,
block: @escaping (_ url: String?) -> Void) {
// Create a reference to the file you want to upload
let fileRef = kFirFileStorageRef.child(fileName)
/// Start uploading
upload(data: data, withName: fileName, atPath: fileRef) { (url) in
block(url)
}
}
func upload(data: Data,
withName fileName: String,
atPath path:StorageReference,
block: @escaping (_ url: String?) -> Void) {
// Upload the file to the path
self.currentUploadTask = path.putData(data, metadata: nil) { (metaData, error) in
let url = metaData?.downloadURL()?.absoluteString
block(url)
}
}
func cancel() {
self.currentUploadTask?.cancel()
}
}
2. Here how can we use this
First of all create a completion block for main function which will let you know when all images will be uploaded successfully.
/// This is your images array
let images = [image1, image2, image3, image4]
/// Here is the completion block
typealias FileCompletionBlock = () -> Void
var block: FileCompletionBlock?
Below are two functions first one is the initial one which will start the uploading and the second one is a recursion which will call itself if there is next image available to upload.
func startUploading(completion: @escaping FileCompletionBlock) {
if images.count == 0 {
completion()
return;
}
block = completion
uploadImage(forIndex: 0)
}
func uploadImage(forIndex index:Int) {
if index < images.count {
/// Perform uploading
let data = UIImagePNGRepresentation(images[index])!
let fileName = String(format: "%@.png", "yourUniqueFileName")
FirFile.shared.upload(data: data, withName: fileName, block: { (url) in
/// After successfully uploading call this method again by increment the **index = index + 1**
print(url ?? "Couldn't not upload. You can either check the error or just skip this.")
self.uploadImage(forIndex: index + 1)
})
return;
}
if block != nil {
block!()
}
}
And finally here is the main function with completion block
startUploading {
/// All the images have been uploaded successfully.
}
EDIT "upload" function for new Firebase:
The only difference is the way of getting downloading url. Here is the new Firebase doc on same.
func upload(data: Data,
withName fileName: String,
atPath path:StorageReference,
block: @escaping (_ url: String?) -> Void) {
// Upload the file to the path
self.currentUploadTask = path.putData(data, metadata: nil) { (metaData, error) in
guard let metadata = metadata else {
// Uh-oh, an error occurred!
block(nil)
return
}
// Metadata contains file metadata such as size, content-type.
// let size = metadata.size
// You can also access to download URL after upload.
path.downloadURL { (url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
block(nil)
return
}
block(url)
}
}
}