Search code examples
swiftfirebasefirebase-storage

How to use async/await in for by Swift


I want to know how to use async/await in for by Swift.

var data = [String]()
    func uploadImage(images: [UIImage]) async -> Array<String> {
        
        for image in images{
            
                 ImageUploader.uploadImages(image: image, type: .post) { imageUrl async in
                    await self.data.append(imageUrl)
                }
            
        }
        return data
    }
static func uploadImages(image: UIImage,type: UploadType,completion: @escaping(String) -> Void) async{
        guard let imageData = image.jpegData(compressionQuality: 0.5) else{return}
        let ref = type.filePath
        
        ref.putData(imageData,metadata: nil){ _, error in
            if let error = error{
                print("DEBUG: Failed to upload image\(error.localizedDescription)")
                return
            }
            
            print("Successfully uploaded image")
            
            ref.downloadURL{ url, _ in
                guard let imageUrl = url?.absoluteString else {return}
                completion(imageUrl)
            }
        }
    }

When I call the func uploadImage, not waiting for self.data.append(imageUrl) The data return empty array.

I want for uploadImage to wait append,then the string array should return array including uploadImages'imageUrl.

How should I correct this. Thank you.


Solution

  • It looks like since the last time I looked at the API Firebase has provided its own async/await methods. For uploading images it is imageRef.putDataAsync(imageData).

    ///Uploads Data to the designated path in `FirebaseStorage`
    static func upload(imageData: Data, path: String) async throws -> URL{
        let storageRef = storage.reference()
        let imageRef = storageRef.child(path)
        
        let metadata = try await imageRef.putDataAsync(imageData)
        return try await imageRef.downloadURL()
    }
    

    Then you can use withThrowingTaskGroup to upload simultaneously or use a regular for loop to upload 1x1.

    import UIKit
    import FirebaseStorageSwift
    import FirebaseStorage
    struct ImageStorageService{
        static private let storage = Storage.storage()
        ///Uploads a multiple images as JPEGs and returns the URLs for the images
        ///Runs the uploads simultaneously
        static func uploadAsJPEG(images: [UIImage], path: String, compressionQuality: CGFloat = 1) async throws -> [URL]{
            return try await withThrowingTaskGroup(of: URL.self, body: { group in
                for image in images {
                    group.addTask {
                        return try await uploadAsJPEG(image: image, path: path, compressionQuality: compressionQuality)
                    }
                }
                var urls: [URL] = []
                for try await url in group{
                    urls.append(url)
                }
                
                return urls
            })
        }
        
        ///Uploads a multiple images as JPEGs and returns the URLs for the images
        ///Runs the uploads one by one
        static func uploadAsJPEG1x1(images: [UIImage], path: String, compressionQuality: CGFloat = 1) async throws -> [URL]{
            var urls: [URL] = []
            for image in images {
                let url = try await uploadAsJPEG(image: image, path: path, compressionQuality: compressionQuality)
                urls.append(url)
            }
            return urls
        }
        ///Uploads a single image as a JPG and returns the URL for the image
        ///Runs the uploads simultaneously
        static func uploadAsJPEG(image: UIImage, path: String, compressionQuality: CGFloat = 1) async throws -> URL{
            guard let data = image.jpegData(compressionQuality: compressionQuality) else{
                throw ServiceError.unableToGetData
            }
            
            return try await upload(imageData: data, path: path)
        }
        ///Uploads Data to the designated path in `FirebaseStorage`
        static func upload(imageData: Data, path: String) async throws -> URL{
            let storageRef = storage.reference()
            let imageRef = storageRef.child(path)
            
            let metadata = try await imageRef.putDataAsync(imageData)
            return try await imageRef.downloadURL()
        }
        
        enum ServiceError: LocalizedError{
            case unableToGetData
        }
    }