In short, I am using image picker to upload images to firebase storage and saving the image id to a document in firestore. The images are uploaded successfully however, when trying to save the data to the document I am getting an error: 'FIRInvalidArgumentException', reason: 'Unsupported type: __SwiftValue'
Why is this happening?
Call to upload images, and then save to document:
ImagePickerView(currentEventID: self.liveEventID, configuration: self.pickerConfig) { imagesData in
print("PICKER DONE: \(imagesData)")
var eventImages: [EventImage] = []
for imageData in imagesData {
let imageUUID = UUID().uuidString
self.isvm.uploadImage(currentEventID: self.liveEventID, imageUUID: imageUUID, selectedImagesData: imageData) { imageURL in
eventImages.append(EventImage(id: imageUUID, url: imageURL, voteCount: 1))
if eventImages.count == imagesData.count {
print("LOOP DONE: \(eventImages)")
self.isvm.saveImagesToEvent(currentEventID: self.liveEventID, eventImages: eventImages) { isSavedToEvent in
if isSavedToEvent {
print("SAVED IMAGES SUCCESSFULLY")
self.isReloadingImages = true
self.isvm.getUpdatedEventPhotos(currentEventID: self.liveEventID) { liveEvent in
self.liveEvent = liveEvent
if !self.liveEvent.isEmpty {
self.liveEventID = self.liveEvent[0].id
self.isReloadingImages = false
}
}
}
}
}
}
}
}
Where the error occurs:
public func saveImagesToEvent(currentEventID: String, eventImages: [EventImage], completion: @escaping (_ isSavedToEvent: Bool) -> Void) {
self.eventsDataCollection.document(currentEventID).updateData([
"eventImages" : FieldValue.arrayUnion(eventImages)
]) { error in
if let error = error {
print("ERROR SAVING URLS TO EVENT: \(error)")
completion(false)
}
}
completion(true)
}
The data model:
struct EventModel: Identifiable, Codable {
var id: String
var isLive: Bool
var coors: [Coor]
var eventCenterCoorFormatted: [Double]
var hostTitle: String
var eventName: String
var eventDescription: String
var isEventPrivate: Bool
var eventGuestsJoined: [String]
var eventEndDate: String
var eventImages: [EventImage]
private enum CodingKeys: String, CodingKey {
case id
case isLive
case coors
case eventCenterCoorFormatted
case hostTitle
case eventName
case eventDescription
case isEventPrivate
case eventGuestsJoined
case eventEndDate
case eventImages
}
}
struct Coor: Identifiable, Codable {
var id = UUID()
var coorDoubles: [Double]
private enum CodingKeys: String, CodingKey {
case coorDoubles
}
}
struct EventImage: Identifiable, Codable {
var id = UUID().uuidString
var url: String
var voteCount: Int
private enum eventImage: String, CodingKey {
case id
case imageURL
case voteCount
}
}
Please note: The last print statement before the error is: "Loop done: " And it successfully gets all the eventImages of type EventImage.
CodePudding user response:
The error is caused by trying to save a custom Swift object to Firestore in your saveImagesToEvent
function:
"eventImages" : FieldValue.arrayUnion(eventImages)
You need to encode your EventImage
objects before trying to store them in Firestore. Your EventImage
struct already conforms to Codable
, but the set/update data methods for Firestore don't automatically encode your data.
To solve this, make sure you have included the Firestore swift extensions in your project:
pod 'FirebaseFirestoreSwift'
Now you can use Firestore.Encoder
to encode your data before sending it to Firestore:
val encodedEventImages = eventImages.map { Firestore.Encoder().encode($0) }
self.eventsDataCollection.document(currentEventID).updateData([
"eventImages" : FieldValue.arrayUnion(encodedEventImages)
]) { error in
...
If you want to read the data in Firestore document as a custom Swift object you will need to decode it as in this example from the Firestore documentation:
docRef.getDocument { (document, error) in
// Construct a Result type to encapsulate deserialization errors or
// successful deserialization. Note that if there is no error thrown
// the value may still be `nil`, indicating a successful deserialization
// of a value that does not exist.
//
// There are thus three cases to handle, which Swift lets us describe
// nicely with built-in Result types:
//
// Result
// /\
// Error Optional<City>
// /\
// Nil City
let result = Result {
try document?.data(as: City.self)
}
switch result {
case .success(let city):
if let city = city {
// A `City` value was successfully initialized from the DocumentSnapshot.
print("City: \(city)")
} else {
// A nil value was successfully initialized from the DocumentSnapshot,
// or the DocumentSnapshot was nil.
print("Document does not exist")
}
case .failure(let error):
// A `City` value could not be initialized from the DocumentSnapshot.
print("Error decoding city: \(error)")
}
}