How to Upload Multiple Image on Firebase Using Swift

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



Leave a reply



Submit