With the current implementation I can read the bookings belonging to var uid
by creating an instance of a structure FireBaseData
which is initialized with the values specified in the Firebase Database.
I need to get all bookings for all users under /Users
node and then assign them to an array so that I can display the values in a UITableVIew. I don't know how to read deeper into the snapshot.
Firebase Database Structure:
// create a reference to Firebase
var dbRef:FIRDatabaseReference!
var uid = "lwrUjaDCcoOcx4K2gioO76JWp2i2"
// this array will hold all bookings for the logged in user
var bookingInfo = [FireBaseData]()
override func viewDidLoad() {
super.viewDidLoad()
dbRef = FIRDatabase.database().reference().child("Users")
startObservingDB() // observe the database for value changes
}
func startObservingDB() {
dbRef.child("lwrUjaDCcoOcx4K2gioO76JWp2i2").
observe(.value, with: { (snapshot: FIRDataSnapshot) in
// an instance of FireBaseData holding all bookings belonging to currentUid
var newBookingInfo = [FireBaseData]()
for booking in snapshot.children {
// after each iteration create an instance of FireBaseData with
// 'booking' for the current iteration & assign it to bookingItem
let bookingItem = FireBaseData(snapshot: booking as! FIRDataSnapshot)
// append the bookingItem after each iteration to newBookingInfo array
newBookingInfo.append(bookingItem)
}
//assign newBookingInfo to global variable bookingInfo so it can be used globally within the class
self.bookingInfo = newBookingInfo
}, withCancel: { (Error:Any) in
})
}
// use this struct to retrieve data from the snapshot received
struct FireBaseData {
var BookingAmount:String!
var BookingNumber:String!
.
.
... and so on
}
init(snapshot:FIRDataSnapshot){
if let BookingAmountContent = (snapshot.value! as? NSDictionary)?["BookingAmount"] as? String {
BookingAmount = BookingAmountContent
}
if let BookingNumberContent = (snapshot.value! as? NSDictionary)?["BookingNumber"] as? String {
BookingNumber = BookingNumberContent
.
.
.... and so on
}
snapshot.children_IS <FTransformedEnumerator: 0x60800023a380>
snapshot.value_IS({
lwrUjaDCcoOcx4K2gioO76JWp2i2 = {
718565122 = {
BookingAmount = 12;
BookingNumber = 718565122;
DateAndTime = "Mon, 26 Sep 2016 18:30";
EmailAddress = "[email protected]";
FlatNumber = 10;
FrequecyAmount = 48;
FrequencyName = Once;
FullName = "Michael ";
PhoneNumber = 25558882522;
PostCode = SE13TYY;
SelectedBathRow = 4;
SelectedBedRow = 3;
StreetAddress = "High Street";
SuppliesAmount = 5;
SuppliesName = "Bring cleaning supplies";
insideCabinets = 0;
insideFridge = 1;
insideOven = 0;
interiorWindows = 1;
laundryWash = 1;
};
890149009 = {
BookingAmount = 73;
BookingNumber = 890149009;
DateAndTime = "Sat, 01 Oct 2016 13:30";
EmailAddress = "[email protected]";
FlatNumber = 10;
FrequecyAmount = 48;
FrequencyName = Once;
FullName = "Michael ";
PhoneNumber = 25558882522;
PostCode = SE13TYY;
SelectedBathRow = 4;
SelectedBedRow = 3;
StreetAddress = "High Street";
SuppliesAmount = 5;
SuppliesName = "Bring cleaning supplies";
insideCabinets = 0;
insideFridge = 1;
insideOven = 0;
interiorWindows = 1;
laundryWash = 1;
};
};
xd5rwZzUqoRbfMp2rq5pTxuRB3s1 = {
116928124 = {
BookingAmount = 22;
BookingNumber = 116928124;
DateAndTime = "Fri, 16 Dec 2016 16:30";
EmailAddress = "[email protected]";
FlatNumber = 10;
FrequecyAmount = 22;
FrequencyName = "Every week";
FullName = Mi;
PhoneNumber = 28488824;
PostCode = RTRFHGT;
SelectedBathRow = 3;
SelectedBedRow = 1;
StreetAddress = "12 High St";
SuppliesAmount = 0;
SuppliesName = "I have cleaning supplies";
TimeStampBookingSavedInDB = 1481886718;
TimeStampDateAndTime = 1481905800;
insideCabinets = 0;
insideFridge = 0;
insideOven = 0;
interiorWindows = 0;
laundryWash = 0;
};
328241274 = {
BookingAmount = 22;
BookingNumber = 328241274;
DateAndTime = "Sun, 18 Dec 2016 16:30";
EmailAddress = "[email protected]";
FlatNumber = 10;
FrequecyAmount = 22;
FrequencyName = "Every week";
FullName = Mi;
PhoneNumber = 28488824;
PostCode = RTRFHGT;
SelectedBathRow = 3;
SelectedBedRow = 1;
StreetAddress = "12 High St";
SuppliesAmount = 0;
SuppliesName = "I have cleaning supplies";
TimeStampBookingSavedInDB = 1481888650;
TimeStampDateAndTime = 1482078600;
insideCabinets = 0;
insideFridge = 0;
insideOven = 0;
interiorWindows = 0;
laundryWash = 0;
};
};
})
Updated Answer according to @Jay suggestion
*Problem : with this implementation `queryOrdered(byChild:)` has no effect.*
func startObservingDB() {
dbRef.queryOrdered(byChild: "TimeStampDateAndTime").observe(.value, with: { (snapshot: FIRDataSnapshot) in
// an instance of FireBaseData holding all bookings under currentUid
var newBookingInfo = [FireBaseData]()
//iterate over each user node child
for user_child in snapshot.children {
//user_snap is each user
let user_snap = user_child as! FIRDataSnapshot
//now iterate over each booking
for booking in user_snap.children {
// after each iteration through snapshot.children, create an instance of FireBaseData with 'booking' for the current iteration & assign it to bookingItem
let bookingItem = FireBaseData(snapshot: booking as! FIRDataSnapshot)
// append the bookingItem after each iteration to newBookingInfo array
newBookingInfo.append(bookingItem)
}
}
//assign newBookingInfo to global variable bookingInfo so it can be used globally within the class
self.bookingInfo = newBookingInfo
// reload the data every time FIRDataEventType is triggered by value changes in Database
self.tableView.reloadData()
}, withCancel: { (Error:Any) in
print("Huge \(Error)")
})
//Set the estimatedRowHeight of your table view
tableView.estimatedRowHeight = 44.0
// Set the rowHeight of your table view to UITableViewAutomaticDimension
tableView.rowHeight = UITableViewAutomaticDimension
} // end of startObservingDB()
@Jay also suggested that to sort the array of objects retrieved from Firebase DB in code rather than using queryOrdered(by:_)
method provided by Firebase.
Here is how I am sorting and filtering the array.
// sort the array in place so that the most recent date will appear first
self.bookingInfo.sort(by: {(DateAndTimeObject_1,DateAndTimeObject2) -> Bool in
DateAndTimeObject_1.TimeStampDateAndTime < DateAndTimeObject2.TimeStampDateAndTime
})
// filter each element in the array against the condition specified in the body of the closure
self.bookingInfo = self.bookingInfo.filter({(firstElementInArray) -> Bool in
firstElementInArray.TimeStampDateAndTime == 1483201800
})
So what you after here is each booking node within each users node
A template would look like:
users
uid_0
booking_0
booking_amount: "12"
booking_1
booking_amount: "14"
Read by .value which reads in everything in the users node.
Each user node has multiple booking children so we will then need to iterate over those to get their child values.
Inside each child is the key: value pairs we want to get, in this example, booking_amount and the value.
let usersRef = myRootRef.child(byAppendingPath: "users")
usersRef?.observe(.value, with: { snapshot in
if ( snapshot!.value is NSNull ) {
print("not found")
} else {
for user_child in (snapshot?.children)! { //iterate over each user node child
//assign the user_child enumerator to a snapshot
let user_snap = user_child as! FDataSnapshot
for booking in user_snap.children { //now iterate over each booking
//booking is a enumerator so make it a snapshot
let bookingSnap = booking as! FDataSnapshot
let dict = bookingSnap.value as! [String: String]
let amount = dict["booking_amount"]
print(amount!)
}
}
}
})