I'm following Firebase's recommendation of flattening data, but I'm having trouble listing a series of items from my database.
Here's a sample of my database file:
"users" : {
"UID12349USER" : {
"firstName" : "Jon",
"lastName" : "Snow",
"email" : "[email protected]",
"albums" : {
"UID124ALBUM" : true,
"UID125ALBUM" : true
}
}
},
"albums" : {
"UID124ALBUM" : {
"name" : "My Artwork",
},
"UID125ALBUM" : {
"name" : "My Sketches",
}
}
I'm retrieving the list of albums for a given user:
let userAlbums = database.child(usersKey).child(user.uid).child(albumsKey)
userAlbums.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch [UID124ALBUM: 1, UID125ALBUM: 1]
})
Now I wish I could retrieve all the user's albums in one single query. I could do a batch of queries, and populate an asynchronous array, but that doesn't seem like a good approach to me...
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// fetch album.. append to array
})
}
Using that approach makes it tricky to detect when the queries have finished, due to the asynchronous nature of the requests. Add to that the fact that some of the requests might fail, due to a bad connection.
Also, if I want to filter one of the albums with a given name (e.g. "My Artwork") or return nil if it doesn't exist, I also end up with a tricky end condition.
var found = false
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
// if current.name == "My Artwork"
// completion(current)
})
}
// This block will be called before observeSingleEventOfType =.=
if !found {
completion(nil)
}
I have a good background on iOS and Swift, but I'm knew to Firebase and NoSQL databases. Can someone point me a good direction? Should I ditch Firebase and try something else? Am I missing some method that can query what I need? Is my json structure wrong and missing some extra keys?
Thanks
public String getKey ()The key name for the source location of this snapshot or null if this snapshot points to the database root.
FireSQL is a library built on top of the official Firebase SDK that allows you to query Cloud Firestore using SQL syntax. It's smart enough to issue the minimum amount of queries necessary to the Firestore servers in order to get the data that you request.
This chat application allows users to store the basic profile and contact list. The user profile would be located on a path such as Users/$uid. User is a node in it and will have a sort of primary key associated with an ID.
A DataSnapshot is an efficiently-generated immutable copy of the data at a Firebase Location. They cannot be modified and will never change.
I would suggest using a DispatchGroup
and mutual exclusion to handle asynchronous functions within a for loop. Here is the code you provided with a DispatchGroup
to ensure that all of the asynchronous functions in the loop have completed before it checks the if statement:
let myGroup = DispatchGroup()
var found = false
// iterate through your array
for key in albumKeys {
let album = database.child(self.albumsKey).child(key)
// lock the group
myGroup.enter()
album.observeSingleEventOfType(.Value, withBlock: { snapshot in
if current.name == "My Artwork" {
found = true
}
// after the async work has been completed, unlock the group
myGroup.leave()
})
}
// This block will be called after the final myGroup.leave() of the looped async functions complete
myGroup.notify(queue: .main) {
if !found {
completion(nil)
}
}
Anything contained in the myGroup.notify(queue: .main) {
codeblock will not execute until myGroup.enter()
and myGroup.leave()
have been called the same amount of times. Be sure to call myGroup.leave()
within the Firebase observe block (after the async work) and make sure that it is called even if an error is produced from the observe.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With