How to upload multiple image on firebase using swift?
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)
}
}
}
How to upload multiple image on firebase using Swift's PHPickerController
This worked for me.
Note: Make sure that the images you are selecting from the photoLibrary are not the default ones that come with xCode. Some of the default images do not work because they don't have a file location.
SwiftUI Solution
Here is how you call the PHPickerViewController:
struct PHPicker: UIViewControllerRepresentable {
func makeUIViewController(context: Context) -> PHPickerViewController {
var config = PHPickerConfiguration()
config.selectionLimit = 5
config.filter = PHPickerFilter.images
let pickerViewController = PHPickerViewController(configuration: config)
pickerViewController.delegate = context.coordinator
return pickerViewController
}
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
}
class something: NSObject, PHPickerViewControllerDelegate {
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
var fileName: Int = 5
for result in results {
// Get all the images that you selected from the PHPickerViewController
result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in
// Check for errors
if let error = error {
print("Sick error dawg \(error.localizedDescription)")
} else {
// Convert the image into Data so we can upload to firebase
if let image = object as? UIImage {
let imageData = image.jpegData(compressionQuality: 1.0)
// You NEED to make sure you somehow change the name of each picture that you upload which is why I am using the variable "count".
// If you do not change the filename for each picture you upload, it will try to upload the file to the same file and it will give you an error.
Storage.storage().reference().child("fileName").child("\(fileName)").putData(imageData!)
fileName += 1
print("Uploaded to firebase")
} else {
print("There was an error.")
}
}
}
}
}
}
func makeCoordinator() -> something {
return something()
}
}
Here is how I present the sheet:
struct PresentMyPicker: View {
@State var presentSheet: Bool = false
var body: some View {
VStack {
Button {
presentSheet.toggle()
} label: {
Text("Click me")
}
}
.sheet(isPresented: $presentSheet) {
PHPicker()
}
}
}
UIKit solution
This is how I present the PHPickerViewController when they tap the button:
func setupView() {
var config = PHPickerConfiguration()
config.selectionLimit = 5
config.filter = PHPickerFilter.images
let pickerViewController = PHPickerViewController(configuration: config)
pickerViewController.delegate = self
view.addSubview(button)
button.addAction(UIAction() { _ in
self.present(pickerViewController, animated: true)
}, for: .touchUpInside)
}
Here is my delegate function that runs after you click "Add" with the selected images you want to upload.
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
picker.dismiss(animated: true)
var fileName: Int = 1
for result in results {
// Get all the images that you selected from the PHPickerViewController
result.itemProvider.loadObject(ofClass: UIImage.self) { object, error in
// Check for errors
if let error = error {
print("Sick error dawg \(error.localizedDescription)")
} else {
// Convert the image into Data so we can upload to firebase
if let image = object as? UIImage {
let imageData = image.jpegData(compressionQuality: 1.0)
// You NEED to make sure you somehow change the name of each picture that you upload which is why I am using the variable "fileName".
// If you do not change the filename for each picture you upload, it will try to upload all the selected images to the same file location and give you an error.
Storage.storage().reference().child("CollectionName").child("\(fileName)").putData(imageData!)
fileName += 1
} else {
print("There was an error.")
}
}
}
}
}
Also if you are wanting to upload videos to firebase and having trouble take a look at this example it took me forever to figure this out. Uploading Videos to firebase correctly.
Uploading Multiple Images simultaneously with Google Firebase
Answered by @FrankVanPuffellen in comments...
"You'd indeed just loop and upload them individually. Your iOS user might also appreciate if you upload them one at a time, so that they can abort the upload midways through without losing progress on all images." – Frank van Puffelen
SwiftUI: upload multiple images to Firebase
Each call to putData
stores a single image, in the location that you call putData
on.
So if you want to store three separate images, you'll have to call putData
on three difference StorageReference
objects. To then get the three download URLs, you call downloadURL
on each of the three StorageReference
objects too.
storagePostRef1.putData(image1, metadata: metadata) { (storageMetadata, error) in
storagePostRef1.downloadURL { (url1, error) in
let image1 = url?.absoluteString storagePostRef2.putData(image2, metadata: metadata) { (storageMetadata, error) in
storagePostRef2.downloadURL { (url2, error) in
storagePostRef3.putData(image3, metadata: metadata) { (storageMetadata, error) in
storagePostRef3.downloadURL { (url3, error) in
You can probably clean this up a bit, by creating your own helper function that handles the calls to putData
and downloadUrl
with a single closure/callback.
Multiple image upload in firebase
I wrote a function to upload an image in Firebase storage, please use this one, you can also call it multiple times to upload multiple pictures in parallel. You can also have a look at the article Utility Class to Upload Images, Videos & Files to Firebase Storage in IOS Swift or it's android version Utility Class to Upload Images, Videos & Files to Firebase Storage in Android.
public func uploadData(data: Data, serverFileName: String) {
let storage = Storage.storage()
let storageRef = storage.reference()
// Create a reference to the file you want to upload
var directory = "images/"
let fileRef = storageRef.child(directory + serverFileName);
// Upload the file to the path "images/rivers.jpg"
let uploadTask = fileRef.putData(data, metadata: nil) { metadata, error in
/* guard let metadata = metadata else {
// Uh-oh, an error occurred!
print("Uh-oh, an error occurred! in metadata retreiving")
return
} */
// Metadata contains file metadata such as size, content-type.
// let size = metadata.size
// You can also access to download URL after upload.
fileRef.downloadURL { (url, error) in
guard let downloadURL = url else {
// Uh-oh, an error occurred!
return
}
// File Uploaded Successfully
// file url is here downloadURL.absoluteString
}
}
}
Related Topics
iOS Library to Convert Powerpoint Presentations into Images
Uialertcontroller Set Attributed Messages
iOS - Custom Table Cell Not Full Width of Uitableview
Avassetresourceloaderdelegate Methods Not Working on Device
Allow "Auto Lock" While Video Is Being Played
Swift 3.0 Multiple Selection with Select All Cell
Didreceiveremotenotification Function Doesn't Called with Fcm Notification Server
Game Center Login Dialog Not Shown Again After Cancelling It for the First Time (Ios7)
How to Capture Taps Ignored by Wkwebview
Bounce Animation on Google Map Marker in iOS? [Objective-C]
How to Add Navigation Interface After Create a Tab Bar Controller Programmatically (Swift)
How to Get All Nsrange of a Particular Character in a Nsstring
How to Record Actual Sound on the Simulator Using Mic
Cannot Get My Location to Show Up in Google Maps in iOS 8
How Can One Enable Keyboard Like in Imessages/Fb Messenger in Landscape Mode at iOS8