Home > database >  How to wait for firebase storage to upload image before using it?
How to wait for firebase storage to upload image before using it?

Time:09-05

I am trying to create a post but I have to wait for the image to upload to firebase storage before I can actually create the post. This just creates a post with an empty imageURL:

private func createFeedPost(feedPost: FeedPost) {
        Task {
            await persistImageToStorage()
        }
         FirebaseManager.shared.firestore.collection("feedposts").document(feedPost.id)
.setData(["commentsCount": feedPost.commentsCount, "description": feedPost.description, 
"feedImageUrl": feedPost.feedImageUrl, "id": feedPost.id, "likesCount": feedPost.likesCount, 
"locked": feedPost.locked, 
"price": feedPost.price, "timestamp": feedPost.timestamp])
    }
private func persistImageToStorage() async {
        let ref = FirebaseManager.shared.storage.reference(withPath: imageId)
        guard let imageData = self.image?.jpegData(compressionQuality: 0.5)
            else { return }
        ref.putData(imageData, metadata: nil) { metadata, err in
            if let err = err {
                print(err)
                return
            }
            ref.downloadURL { url, err in
                if let err = err {
                        print(err)
                        return
                }
                
                guard let url = url else { return }
                self.imageUrl = url.absoluteString // here is where I set the imageURL
            }
        }
    }

Is there a way to get something back from firebase storage once the image was uploaded?

EDIT:

I tried using a completion but this also didn't set the imageURL

 private func persistImageToStorage(complete:()->()) {
        let ref = FirebaseManager.shared.storage.reference(withPath: imageId)
        guard let imageData = self.image?.jpegData(compressionQuality: 0.5)
            else { return }
        ref.putData(imageData, metadata: nil) { metadata, err in
            if let err = err {
                print(err)
                return
            }
            ref.downloadURL { url, err in
                if let err = err {
                        print(err)
                        return
                }
                
                guard let url = url else { return }
                self.imageUrl = url.absoluteString
            }
        }
        complete()
    }

    private func createFeedPost(feedPost: FeedPost) {
        persistImageToStorage(complete: { () -> () in
            FirebaseManager.shared.firestore.collection("feedposts").document(feedPost.id).setData(["commentsCount": feedPost.commentsCount, "description": feedPost.description, "feedImageUrl": feedPost.feedImageUrl, "id": feedPost.id, "likesCount": feedPost.likesCount, "locked": feedPost.locked, "price": feedPost.price, "timestamp": feedPost.timestamp])
        })
    }

CodePudding user response:

For any time-consumption or long-waiting, I recommend using the async logic like what you were thinking about.

Here is your typealias to make your type definition more meaningful.

/// (File URL, Error Message/Code
typealias FireBaseUploadResponseHandler = (String?, String?) -> Void

So your code will be:

private func persistImageToStorage(image: UIImage?, done: @escaping FireBaseUploadResponse) {

    guard let imageData = image?.jpegData(compressionQuality: 0.5) 
    else { return done(nil, "IMG_TO_DATA_FAILED") }

    let ref = FirebaseManager.shared.storage.reference(withPath: imageId)
    ref.putData(imageData, metadata: nil) { metadata, err in
        if let err = err {
            print(err)
            return done(nil, err.localizedDescription)
        }

        ref.downloadURL { url, err in                
            guard let url = url else { return done(nil, err.localizedDescription) }
            done(url.absoluteString, nil) // Pass your downloaded URL here to closure `done`
        }
    }
}

Usage:

private func createFeedPost(feedPost: FeedPost, done: escaping (Bool) -> Void) {
    self.persistImageToStorage(image: self.image) { url, error in
        guard let _url = url else { return done(false) }
        // now _url is available to use, it is public url of your uploaded image
        FirebaseManager.shared.firestore.collection("feedposts")
            .document(feedPost.id)
            .setData(["commentsCount": feedPost.commentsCount, "description": feedPost.description, 
"feedImageUrl": feedPost.feedImageUrl, "id": feedPost.id, "likesCount": feedPost.likesCount, 
"locked": feedPost.locked, 
"price": feedPost.price, "timestamp": feedPost.timestamp])

        done(true) 
    }
}

Then


createFeedPost(feedPost: [your post here]) { [weak self] success in
    guard let _self = self else { return }
    if success {
        _self.[your function to notify user that post is created]()
    } else {
        _self.[your function to notify user that it failed to create the post]()
    }
}

For this bunch of codes:

["commentsCount": feedPost.commentsCount, "description": feedPost.description, 
"feedImageUrl": feedPost.feedImageUrl, "id": feedPost.id, "likesCount": feedPost.likesCount, 
"locked": feedPost.locked, 
"price": feedPost.price, "timestamp": feedPost.timestamp]

I suggest you use any JSON encoder/decoder/parser like ObjectMapper or Codable (built-in). To simplify your logic and easy to maintain it.

  • Related