@MainActor class AddCarViewModel: ObservableObject {
@Published var photoUrls : [String] = []
func uploadImages(images: [Image], customerId: String) async throws {
let subFolderId = UUID().uuidString
let pictureFolderRef = Storage.storage().reference().child("CarPhotos").child(customerId).child("\(subFolderId)")
images.enumerated().forEach { index, image in
guard let imageData = image.asUIImage().jpegData(compressionQuality: 0.5) else {
return
}
let pictureReference = pictureFolderRef.child("image_\(index).jpeg")
pictureReference.putData(imageData, metadata: nil) {
metadata, error in
if let error = error {
print("failed to put data")
}
pictureReference.downloadURL { url, error in
if let error = error {
print("error while downloading url ")
}
guard let urlString = url?.absoluteString else {
return
}
self.photoUrls.append(urlString)
}
}
}
}
func addCar(carInfo: Car) async throws {
try await Firestore.firestore().collection("cars").document().setData(from: carInfo)
}
}
VStack {
Button {
Task {
do {
try await viewModel.uploadImages(images: selectedImages, customerId: viewModel.user!.uid)
try await viewModel.addCar(carInfo: Car(photoUrls: viewModel.photoUrls))
} catch {
print(error)
}
}
dismiss()
} label: {
Text("Post")
}
}
Saving Multiple Photo Url to FireStore Database
as I see I can not edit post. my question is when user selected images it can be saved to Firebase Storage with no issue. I can print url string but when I append it to view model published property which is urlStrings array it doesn't and I when I send to Firestore database it shows empty array.
The problem you're facing happens because the image uploads to Firebase Storage and the download URL fetching happen asynchronously. This means the photoUrls array doesn’t get updated quickly enough before the addCar function runs, so an empty array ends up being sent to Firestore.
@MainActor
class AddCarViewModel: ObservableObject {
@Published var photoUrls: [String] = []
func uploadImages(images: [Image], customerId: String) async throws {
let subFolderId = UUID().uuidString
let pictureFolderRef = Storage.storage().reference().child("CarPhotos").child(customerId).child("\(subFolderId)")
let dispatchGroup = DispatchGroup()
var urls: [String] = []
for (index, image) in images.enumerated() {
guard let imageData = image.asUIImage().jpegData(compressionQuality: 0.5) else {
continue
}
let pictureReference = pictureFolderRef.child("image_\(index).jpeg")
dispatchGroup.enter()
pictureReference.putData(imageData, metadata: nil) { metadata, error in
if let error = error {
print("Failed to upload image: \(error)")
dispatchGroup.leave()
return
}
pictureReference.downloadURL { url, error in
if let error = error {
print("Failed to get download URL: \(error)")
} else if let urlString = url?.absoluteString {
urls.append(urlString)
}
dispatchGroup.leave()
}
}
}
// Wait for all uploads to finish
dispatchGroup.wait()
// Update the photoUrls array
await MainActor.run {
self.photoUrls = urls
}
}
func addCar(carInfo: Car) async throws {
try await Firestore.firestore().collection("cars").document().setData(from: carInfo)
}
}
---
Button {
Task {
do {
try await viewModel.uploadImages(images: selectedImages, customerId: viewModel.user!.uid)
try await viewModel.addCar(carInfo: Car(photoUrls: viewModel.photoUrls))
dismiss()
} catch {
print(error)
}
}
} label: {
Text("Post")
}
Can't you use Observable vs ObservableObject ?