I get the error listed below when I try to save a nested model in Firebase. It is because the data model is not properly formatted, but I have searched for hours and I am stuck now.
Cannot store object of type _SwiftValue at . Can only store objects of type NSNumber, NSString, NSDictionary, and NSArray.
The model I want to write looks like this:
payPeriodDateStartDate
payPeriodEndDate
-bookings
booking1
hours:
originalPriceOfBooking:
cancelledBy // <- optional, it may or may not exist
-timeStamp // <- a timeStamp node
cleanerUID:
costToCancelByCleaner:
booking2
I think that I am not properly converting cancelledBy
in this code snippet.
for booking in self.bookingsFromQuery {
let bookingForPayPeriod = DisbursePaymentData(
numberOfHours: booking.NumberOfHours
cancelledBy: booking.objectsUnderCancelledBy) // <- this argument is not properly converted to AnyObject and causes exception 'InvalidFirebaseData
)
Below you can find the full code.
//FireBaseData is used to read from Firebase
struct FireBaseData {
var BookingNumber:String!
var PostCode:String!
var objectsUnderCancelledBy:[String: CancelledObject]! //the object that is causing the crash of the app, it is optional, cleaner may/may not cancel booking
init(snapshot:FIRDataSnapshot){
//I am initializing just the property we are concerned about, in my project all properties are initialized
//CancelledBy is nested
if ((snapshot.value! as? NSDictionary)?["BookingNumber"] as? String) != nil {
var cancelledItems = [String: CancelledObject]()
//each key:value under the bookingNumber
for item in snapshot.children{
let elementUnderBookingNumber = item as! FIRDataSnapshot
if elementUnderBookingNumber.key == "CancelledBy" {
//iterate through the elements under each timeStamp
for objectUnderTimeStamp in elementUnderBookingNumber.children {
let item = CancelledObject(snapshot: objectUnderTimeStamp as! FIRDataSnapshot)
//assign the timeStamp as a key for retrieved item
cancelledItems["\(item.key!)"] = item
}
}
}
self.objectsUnderCancelledBy = cancelledItems
}//end of init
}//end of struct FirebaseData
//holds data under CancelledBy key when data is read from Firebase
class CancelledObject {
var cleanerUID: String!
var costToCancelByCleaner: String!
init() {
if let cleanerUIDContent = (snapshot.value! as? NSDictionary)?["CleanerUID"] as? String {
self.cleanerUID = cleanerUIDContent
}
}
}//end of CancelledObject
//DisbursePaymentData is used to update the database
struct DisbursePaymentData {
var payPeriodDateStartDate:String!
var payPeriodEndDate:String!
//- optional, cancelledBy might not exist in Database
var cancelledBy: [String: CancelledObject]!
var numberOfHours:String!
init(
payPeriodDateStartDate:String,
payPeriodEndDate:String) {
self.payPeriodDateStartDate = payPeriodDateStartDate
self.payPeriodEndDate = payPeriodEndDate
}
init(
numberOfHours:String,
cancelledBy: [String:CancelledObject]!
) {
self.numberOfHours = numberOfHours
self.cancelledBy = cancelledBy
}
//enables to convert the values to AnyObject
func convertDisbursePaymentDataToAnyObject() -> AnyObject {
var someDict = [String : AnyObject]()
someDict["numberOfHours"] = self.numberOfHours as AnyObject?
someDict["cancelledBy"] = self.cancelledBy as AnyObject? //<- is this the way to convert cancelledBy to AnyObject?
return someDict as AnyObject
}
}//end of struct DisbursePaymentData
class UpdateFirebase {
var bookingsFromQuery = [FireBaseData]()
override func viewDidLoad() {
super.viewDidLoad()
//data was successfully read from Firebase
var finalBookingArray = [String: AnyObject]()
for booking in self.bookingsFromQuery {
let bookingForPayPeriod = DisbursePaymentData(
numberOfHours: booking.NumberOfHours
cancelledBy: booking.objectsUnderCancelledBy) // <- this argument is not properly converted to AnyObject and causes exception 'InvalidFirebaseData
)
//convert each booking to AnyObject
let bookingCompleted = bookingForPayPeriod.convertDisbursePaymentDataToAnyObject()
//assign to BookingNumber of each loop the converted object
finalBookingArray[booking.BookingNumber] = bookingCompleted
}//end of for loop
let finalObject = DisbursePaymentData(
payPeriodDateStartDate: self.fromDateString.text!,
payPeriodEndDate: self.toDateString.text!)
let childUpdates = ["Payments/\(uid)/\(timeStamp)/\(paymentRef)" : finalObject,
"Payments/\(uid)/\(timeStamp)/\(paymentRef)/\("bookings")" : finalBookingArray] as [String : Any]
self.dbRef.updateChildValues(childUpdates)
}//end of viewDidLoad
}