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.