GetStream get activities is getting them all instead of specific ID - ios

I'm working on a function that connects to GetStream in order to get the activities for a chosen feed group. The problem is that GetStream is returning all the activities inside the feed group as opposed to the ones for a specific slug. In MySQL terms it's ignoring the WHERE UUID = uuid :)
Here's the code I'm currently using which returns all activities, I've tried setting the elementFeed to nil before setting it but that doesn't work either.
func getStream(uuid: String, completion: #escaping([StreamActivity], Bool, APPError?) -> Void) {
let group = DispatchGroup()
group.enter()
var activities = [StreamActivity]()
elementFeed = nil
elementFeed = Client.shared.flatFeed(feedSlug: "element", userId: uuid)
elementFeed?.get(typeOf: StreamActivity.self, enrich: false, pagination: .limit(20), ranking: "", includeReactions: [], completion: { results in
if let foundActivities = results.value?.results {
activities.append(contentsOf: foundActivities)
group.leave()
} else {
completion([], false, .GetDiscussions)
group.leave()
}
})
group.notify(queue: .main) {
self.isUserFollowing(uuid: uuid) { (following) in
completion(activities, following, nil)
}
}
}
Here's the code used to create the activity:
func addActivity(type: ActivityType, body: String?, uuid: String, completion: #escaping(Bool, APPError?) -> Void) {
var feed: FeedId?
var originFeed: FeedId?
switch type {
case .comment:
feed = FeedId(feedSlug: "element", userId: uuid)
case .reply:
originFeed = FeedId(feedSlug: "comment", userId: uuid)
default:
break
}
let activity = StreamActivity(actor: StreamUser.current!, verb: type.rawValue, object: "element:\(uuid)", body: body!)
activity.feedIds = [feed!]
elementFeed = Client.shared.flatFeed(feedSlug: "element", userId: uuid)
elementFeed?.add(activity, completion: { (result) in
if result.error == nil {
completion(true, nil)
} else {
completion(false, .PostComment)
}
print("GETSTREAM: Activity added:\(result)")
})
}

Stream Feed API does not allow you to read activities outside a feed. Feeds are materialised so when you perform a call like this
Client.shared.flatFeed(feedSlug: "element", userId: uuid)
You are not performing a query (ie. SELECT * FROM activities WHERE slug = ? AND userId = ?) but you are reading the activities that were added (directly, via to targets or from followed feeds) to the element:$uuid feed.
If a feed contains wrong activities it is because something is going wrong with the logic used to add activities or to create follow relationships.
When you read a feed, Stream adds information about the origin of each activity; such information is stored in a field called "origin". If the origin field is missing / null it means the activity was added to the feed directly; if the activity was added via follow relationship, then the origin field will be set to the feed id from where the activity originated.
For example:
If element:x follows user:z; all activities user Z adds are propagated to element:x and will have origin = "user:z" on that feed.
You can use the origin field to get some more information about why the activity is in a feed; that should be very useful information to find out where the integration problem is.

Related

Adding a custom object to an array in Firestore

I am making an ordering app for customers to order their specific specs. When the user logs in they can go to a tab that contains a tableview with all their specs, once they click on a cell it will take them to a new view controller that will display more information on the spec. Once on this view controller they will have the ability to add x amount of pallets/rolls/etc of that item. I am able to add the spec to Firestore, but I cannot get it to an array in Firestore which I need. My goal is that on anther tab the user can view all the current specs they are trying to order until they hit submit. I am currently using the user.uid to get to that specific customers orders inside Firestore.
Code:
#IBAction func addPallet(_ sender: Any) {
// Get the current user
let user = Auth.auth().currentUser
if let user = user {
_ = user.uid
}
if spec != nil {
// Get the qty ordered for that spec
let totalQty: Int? = Int(palletsToAddTextField.text!)
let qty = spec!.palletCount * totalQty!
let specToAdd = Spec(specNumber: spec!.specNumber,
specDescription: spec!.specDescription,
palletCount: spec!.palletCount,
palletsOrdered: qty)
orderedArray.append(specToAdd)
let specAdded: [String: Any] = [
"SpecDesc": spec!.specDescription,
"SpecNum": spec!.specNumber,
"PalletCount": spec!.palletCount,
"PalletsOrder": qty
]
db.collection("orders").document(user?.uid ?? "error").setData(specAdded) { err in
if let err = err {
print("Error writing document: \(err)")
} else {
print("Document successfully written!")
}
}
}
}
code for spec:
struct Spec: Codable {
// Properties
var specNumber: String
var specDescription: String
var palletCount: Int
var palletsOrdered = 0
enum CodingKeys: String, CodingKey {
case specNumber
case specDescription
case palletCount
case palletsOrdered
}
}
I need something added like the below picture. The user will add x amount of pallets, then going to the next spec they want and add it to the array in Firestore as well.
Ok i guess i get what you want to do. Try:
db.collection("orders").document(userID).setData(["allMyData" : myArray])
"allMyData" will be the name of the field in which you want to save your array and myArray would be your array (specAdded). Thats what you are looking for?
If the document already exists you will want to use .updateData instead of .setData to keep all other fields that might already exist in that specific doc.
Kind regards

Unable to fetch members of a group (XMPP)

When a specific room is created by me, I can fetch members in that group. But when the group is created by someone else, I can't fetch members in that group.
The didDiscoverRooms method is initially called and then within it the members are fetched like so...
func xmppMUC(_ sender: XMPPMUC, didDiscoverRooms rooms: [Any], forServiceNamed serviceName: String) {
print("XMPPRoom: didDiscoverRooms: \(rooms)")
if let elements = rooms as? [DDXMLElement] {
for element in elements {
print("Name: \(String(describing: element.attributeStringValue(forName: "name")))")
print("JID: \(String(describing: element.attributeStringValue(forName: "jid")))")
if let name = element.attributeStringValue(forName: "jid"), let roomJID = XMPPJID(string: name) {
let roomStorage = XMPPRoomMemoryStorage()
let room = XMPPRoom(roomStorage: roomStorage!, jid: roomJID, dispatchQueue: DispatchQueue.main)
room.addDelegate(self, delegateQueue: DispatchQueue.main)
room.activate(self.xmppStream)
room.fetchConfigurationForm()
room.fetchMembersList()
}
}
}
}
The room.fetchMembersList() in turn calls this delegate method..
func xmppRoom(_ sender: XMPPRoom, didFetchMembersList items: [Any]) {
print("XMPPRoom: \(sender.roomJID)")
print("XMPPRoom: didFetchMembersList: \(items)")
}
The first print statement gives the room name and the second print statement gives the room members. But this only works if the room is created by me. If the room is created by someone else then I can't get the members...
Fetching the member list (and the admin and owner list) usually requires owner permissions because it will leak the Jabber ID in anonymous rooms. Some modern servers (recent ejabberd versions for example) make an exception for non-anonymous and also allow regular participants to fetch that list.

Cannot convert value of type '[RuntimeIntent]' to expected argument type '[Intent]'

I am trying to implement Watson Conversation API in my iOS app. While passing response.intents and response.entitities to to issueCommand function, I get an error "Cannot convert value of type '[RuntimeIntent]' to expected argument type '[Intent]'". I tried to typecast both the arguments of issueCommand arguments but it wasn't useful. It will be great if someone can guide me in the right direction? Thanks!
The code is as follows:
func conversationRequestResponse(_ text: String) {
let failure = { (error: Error) in print(error) }
let request = MessageRequest(input: InputData.init(text: text), context: self.context)
self.conversation?.message(workspaceID: Credentials.ConversationWorkspaceID,
request: request,
failure: failure) {
response in
print(response.output.text)
self.didReceiveConversationResponse(response.output.text)
self.context = response.context
var entities: ConversationV1.RuntimeEntity
/// An array of name-confidence pairs for the user input. Include the intents from the previous response when they do not need to change and to prevent Watson from trying to identify them.
// issue command based on intents and entities
print("appl_action: \(response.context.json["appl_action"])")
self.issueCommand(intents: response.intents, entities: response.entities)
}
}
func issueCommand(intents: [Intent], entities: [Entity]) {
for intent in intents {
print("intent: \(intent.intent), confidence: \(intent.confidence) ")
}
for entity in entities {
print("entity: \(entity.entity), value: \(entity.value)")
}
for intent in intents {
if intent.confidence > 0.9 {
switch intent.intent {
case "OnLight":
let command = Command(action: "On", object: "Light", intent: intent.intent)
sendToDevice(command, subtopic: "light")
case "OffLight":
let command = Command(action: "Off", object: "Light", intent: intent.intent)
sendToDevice(command, subtopic: "light")
case "TakePicture":
let command = Command(action: "Take", object: "Picture", intent: intent.intent)
sendToDevice(command, subtopic: "camera")
default:
print("No such command")
return
}
}
}
}
The errors can be seen in the image below:
enter image description here
Why not just change the signature of issueCommand to take RuntimeIntents and RuntimeEntities, like so:
func issueCommand(intents: [RuntimeIntent], entities: [RuntimeEntity]) {

Listing Firebase data and relationships

I have just started working with Firebase database and I am a bit confused how to structure the database. In the following example I have a users object and a groups object. Each user can be part of multiple groups and every group can have multiple users. The proposed database structure is the following, according to "Structure Your Database".
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
"groups": {
"techpioneers": true,
"womentechmakers": true
}
}
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"startDate": "24-04-1820",
"members": {
"alovelace": true,
"ghopper": true,
"eclarke": true
}
}
}
}
Let's say I want to display all groups in a list in my app, with the group name and start date. How would I make that database call? Since the user object only contains the id of the groups, would I then have to make a separate call to the database for every group just to find out the name and start date of the group? If there are many groups in the list, then that becomes a lot of calls. My group might contain a lot of other information as well so this doesn't seem good for performance. Can I get all the groups in the groups list of the user, in one call?
One though I had was to include the name and start date in the groups object under the user:
"users": {
"alovelace": {
"name": "Ada Lovelace",
"groups": {
"techpioneers":{
"name": "Historical Tech Pioneers",
"startDate": "24-04-1820"
},
"womentechmakers":{
"name": "Women in Technology",
"startDate": "13-10-1823"
}
}
}
}
}
but this solution seems to add a lot of duplicate data. Also if I want to update the name I would have to do that in multiple locations. And maybe I want to add a sponsor organization object, that also contains group, and then want to list them. Then there would be 3 places to update the information on. How would I solve this?
You would then have two possibilities, one would be to store the data you need (duplicating it) in the groups node of each user.
The other, which is the one that I would recommend the most, would be to add an observeSingleEvent(of: .value) inside your first observer (that could be an observe(.value), observe(.childAdded) or whatever).
Say you have an array of all your group members, and an object named AppUser that represents a user in your app :
var groupMembers = [AppUser]()
To detect whenever a new member is added to a group for example, you could use a .childAdded observer for example :
func groupWasAddedObserver(completion: #escaping () -> ()) {
// Add a .childAdded observer to the group's members node (groupId should be defined somewhere) :
groupsRef.child(groupId).child("members").observe(.childAdded, with: { [weak self] (snapshot) in
// When you get the snapshot, store its key, which represents the member id :
let memberId = snapshot.key
// fetch this member's profile information using its id :
self?.getUser(memberId, completion: { (groupMember) in
// Once you got the profile information of this member, add it to an array of all your members for example :
self?.groupMembers.append(groupMember)
// Call the completion handler so that you can update the UI or reload a table view somewhere maybe depending on your needs :
completion()
})
})
}
And the second method to fetch a user data knowing his or her id :
func getUser(_ userId: String, completion: #escaping (AppUser) -> ()) {
// Add the observerSingleEvent observer :
usersRef.child(userId).observeSingleEvent(of: .value, with: { (snapshot) in
// Get the data you need using the snapshot you get :
guard let email = snapshot.childSnapshot(forPath: "email").value as? String else { return }
guard let name = snapshot.childSnapshot(forPath: "name").value as? String else { return }
guard let picUrl = snapshot.childSnapshot(forPath: "picUrl").value as? String else { return }
// Call the completion handler to return your user/member :
completion(AppUser(id: snapshot.key, email: email, name: name, picUrl: picUrl))
})
}
As you can see you get the memberId of each user using the snapshot key, and you use this memberId to fetch this specific user data.

Finish all asynchronous requests before loading data?

I have run into an issue where I have multiple asynchronous requests occuring which grab images and information from the Facebook API and my Firebase database. I want to perform all my asynchronous requests, then store all that data that I grabbed from the Facebook API/Firebase database into one entire object which I can quickly load. I have set up completion handlers for every asynchronous request which I thought forces the program to "wait" until the request is complete and then have the program continue, but that doesn't seem to work for me. Below is my attempt:
func setupEvents(completion: (result: Bool, Event: Event) -> Void){
// Get a reference to Events
eventsReference = Firebase(url:"<DB Name>")
eventAttendeesRef = Firebase(url:"<DB Name>")
//Read the data at our posts reference
println("Event References: \(eventsReference)")
eventsReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in
let eventName = snapshot.value["eventName"] as? String
let eventLocation = snapshot.value["eventLocation"] as? String
let eventCreator = snapshot.value["eventCreator"] as? String
var attendees: NSMutableDictionary = [:]
var attendeesImages = [UIImage]()
let attendee: NSMutableDictionary = [:]
let group = dispatch_group_create()
//Get attendees first
dispatch_group_enter(group)
self.getAttendees(snapshot.key as String, completion:{ (result, name, objectID) -> Void in
if(result == true){
println("Finished grabbing \(name!) \(objectID!)")
attendees.addEntriesFromDictionary(attendee as [NSObject : AnyObject])
}
else {
println("False")
}
dispatch_group_leave(group)
})
//Get attendees photos
dispatch_group_enter(group)
self.getAttendeesPictures(attendee, completion: { (result, image) -> Void in
if result == true {
println("Finished getting attendee photos. Now to store into Event object.")
attendeesImages.append(image!)
}
else{
println("false")
}
dispatch_group_leave(group)
})
dispatch_group_notify(group, dispatch_get_main_queue()) {
println("both requests done")
//Maintain array snapshot keys
self.eventIDs.append(snapshot.key)
if snapshot != nil {
let event = Event(eventName: eventName, eventLocation:eventLocation, eventPhoto:eventPhoto, fromDate:fromDate, fromTime:fromTime, toDate:toDate, toTime:toTime, attendees: attendees, attendeesImages:attendeesImages, attendeesImagesTest: attendeesImagesTest, privacy:privacy, eventCreator: eventCreator, eventCreatorID: eventCreatorID)
println("Event: \(event)")
completion(result: true, Event: event)
}
}
}) { (error) -> Void in
println(error.description)
}
}
I know I have my completion handlers set correctly as I have tested in my program. However, what I want is that only after both the getAttendees and getAttendeesPictures function completes, I then want to store all the information I grabbed the snapshot, getAttendees, and getAttendeesPictures function and store them into an event object. Any ideas on how to accomplish this? I've tried to look into dispatch_groups to help me handle this via this link: Checking for multiple asynchronous responses from Alamofire and Swift but my program seems to only execute the getAttendees function but not the getAttendeesPictures function. Below are also the getAttendees and getAttendeesPictures functions:
func getAttendees(child: String, completion: (result: Bool, name: String?, objectID: String?) -> Void){
//Get event attendees of particular event
var attendeesReference = self.eventAttendeesRef.childByAppendingPath(child)
println("Loading event attendees")
//Get all event attendees
attendeesReference.observeEventType(FEventType.ChildAdded, withBlock: { (snapshot) -> Void in
let name = snapshot.value.objectForKey("name") as? String
let objectID = snapshot.value.objectForKey("objectID") as? String
println("Name: \(name) Object ID: \(objectID)")
completion(result: true, name: name, objectID: objectID)
}) { (error) -> Void in
println(error.description)
}
func getAttendeesPictures(attendees: NSMutableDictionary, completion: (result: Bool, image: UIImage?)-> Void){
println("Attendees Count: \(attendees.count)")
for (key, value) in attendees{
let url = NSURL(string: "https://graph.facebook.com/\(key)/picture?type=large")
println("URL: \(url)")
let urlRequest = NSURLRequest(URL: url!)
//Asynchronous request to display image
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
if error != nil{
println("Error: \(error)")
}
// Display the image
let image = UIImage(data: data)
if(image != nil){
completion(result: true, image: image)
}
}
}
}
For users seeking answer to question in title then use of dispatch_group and GCD outlined here: i.e embedding one group inside the notification method of another dispatch_group is valid. Another way to go at a higher level would be NSOperations and dependencies which would also give further control such as canceling operations.
Outline:
func doStuffonObjectsProcessAndComplete(arrayOfObjectsToProcess: Array) -> Void){
let firstGroup = dispatch_group_create()
for object in arrayOfObjectsToProcess {
dispatch_group_enter(firstGroup)
doStuffToObject(object, completion:{ (success) in
if(success){
// doing stuff success
}
else {
// doing stuff fail
}
// regardless, we leave the group letting GCD know we finished this bit of work
dispatch_group_leave(firstGroup)
})
}
// called once all code blocks entered into group have left
dispatch_group_notify(firstGroup, dispatch_get_main_queue()) {
let processGroup = dispatch_group_create()
for object in arrayOfObjectsToProcess {
dispatch_group_enter(processGroup)
processObject(object, completion:{ (success) in
if(success){
// processing stuff success
}
else {
// processing stuff fail
}
// regardless, we leave the group letting GCD know we finished this bit of work
dispatch_group_leave(processGroup)
})
}
dispatch_group_notify(processGroup, dispatch_get_main_queue()) {
print("All Done and Processed, so load data now")
}
}
}
The remainder of this answer is specific to this codebase.
There seem to be a few problems here:
The getAttendees function takes an event child and returns an objectID and Name which are both Strings? Shouldn't this method return an array of attendees? If not, then what is the objectID that is returned?
Once an array of attendees is returned, then you can process them in a group to get the pictures.
The getAttendeesPictures eventually returns UIImages from Facebook. It's probably best to cache these out to the disk and pass path ref - keeping all these fetched images around is bad for memory, and depending on size and number, may quickly lead to problems.
Some examples:
func getAttendees(child: String, completion: (result: Bool, attendees: Array?) -> Void){
let newArrayOfAttendees = []()
// Get event attendees of particular event
// process attendees and package into an Array (or Dictionary)
// completion
completion(true, attendees: newArrayOfAttendees)
}
func getAttendeesPictures(attendees: Array, completion: (result: Bool, attendees: Array)-> Void){
println("Attendees Count: \(attendees.count)")
let picturesGroup = dispatch_group_create()
for attendee in attendees{
// for each attendee enter group
dispatch_group_enter(picturesGroup)
let key = attendee.objectID
let url = NSURL(string: "https://graph.facebook.com/\(key)/picture?type=large")
let urlRequest = NSURLRequest(URL: url!)
//Asynchronous request to display image
NSURLConnection.sendAsynchronousRequest(urlRequest, queue: NSOperationQueue.mainQueue()) { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
if error != nil{
println("Error: \(error)")
}
// Display the image
let image = UIImage(data: data)
if(image != nil){
attendee.image = image
}
dispatch_group_leave(picturesGroup)
}
}
dispatch_group_notify(picturesGroup, dispatch_get_main_queue()) {
completion(true, attendees: attendees)
}
}
func setupEvents(completion: (result: Bool, Event: Event) -> Void){
// get event info and then for each event...
getAttendees(child:snapshot.key, completion: { (result, attendeesReturned) in
if result {
self.getAttendeesPictures(attendees: attendeesReturned, completion: { (result, attendees) in
// do something with completed array and attendees
}
}
else {
}
})
}
The above code is just an outline, but hopefully points you in the right direction.
The two requests are executing at the same time, so there is no attendees to get pictures from when the second request executes, if the getAttendees completion closure is going to be called multiple times then you can do something like this:
let group = dispatch_group_create()
for key in keys {
dispatch_group_enter(group)
self.getAttendee(key as String, completion:{ (result, attendee) in
if(result == true){
attendees.addEntriesFromDictionary(attendee)
self.getAttendeesPictures(attendee, completion: { (result, image) in
if result == true {
attendeesImages.append(image!)
}
dispatch_group_leave(group)
})
} else {
dispatch_group_leave(group)
}
})
}
dispatch_group_notify(group, dispatch_get_main_queue()) {}
If the result of the first request is the complete set of attendees you don't even need to use GCD, just call getAttendeesPictures inside the completion closure.
This code doesn't exactly uses the same variables and methods of the original code, it only gives the idea.
Hope it helps!
While there is definitely solution with using GCD and stuff around it, synchronization in general is pain and the more your code gets complicated, the more problems it will start showing - but I think there is one-for-all solution to that: Bolts framework from Facebook (both for android na iOS)
Bolts Framework usage
So what is so magical about it? Well, it lets you create "Tasks", and then chain them. The method in particular that you are interested in is taskForCompletionOfAllTasks: , which is made for parallel processing, just what you need. I wrote a little example for you which you can adjust to your needs:
func fetchAllInformation() -> BFTask {
// First, create all tasks (if you need more, than just create more, it is as easy as that
var task1 = BFTaskCompletionSource()
var task2 = BFTaskCompletionSource()
var tasks = [task1, task2]
// What you do, is you set result / error to tasks and the propagate in the chain upwards (it is either result, or error)
// You run task 1 in background
API.instance.fetchFirstDetailsInBackgroundWithBlock {
(object: AnyObject!, error: NSError!) -> Void in
// On error or on success, you assign result to task (whatever you want)
if error == nil {
task1.setResult(object)
} else {
task1.setError(error)
}
}
// You run task 2 in background
API.instance.fetchSecondDetailsInBackgroundWithBlock {
(object: AnyObject!, error: NSError!) -> Void in
// On error or on success, you assign result to task (whatever you want)
if error == nil {
task2.setResult(object)
} else {
task2.setError(error)
}
}
// Now you return new task, which will continue ONLY if all the tasks ended
return BFTask(forCompletionOfAllTasks: tasks)
}
Once you have main method done, you can use bolts chaining magic:
func processFullObject() {
// Once you have main method done, you can use bolts chaining magic
self.fetchAllInformation().continueWithBlock { (task : BFTask!) -> AnyObject! in
// All the information fetched, do something with result and probably with information along the way
self.updateObject()
}
}
The Bolts framework documentation / README covers basically everything there is to know about it and it is quite extensive, so I would suggest you to go through it - it is very easy to use once you get the basics. I personally use it for exactly this, and it is a blast. This answer will hopefully provide you with different solution and approach, possibly a cleaner one.
There is something wrong with this conceptually. It sounds like you want to wait until both of these functions complete before doing something else, but what you haven't explained is that getAttendeesPictures depends on the outcome of getAttendees. That means what you really want to do it execute one asynchronous block, then execute a second asynchronous block with the output of the first, and then execute your final completion block when both are finished.
GCD is not particularly suited for this; you're better of using NSOperationQueue with NSBlockOperations. There are two distinct advantages to this over GCD:
NSOperation uses familiar object-oriented syntax compared to GCD's c-type functions, so it's pretty easy to write and understand.
Operations in the queue can have explicit dependencies on one another, so you can make it clear that e.g. operation B will only be executed after operation A is complete.
There is a great writeup of this by NSHipster which I'd recommend you go read. It's talked about mostly in the abstract, but what you want to do is use NSBlockOperation to create two block operations, one for executing getAttendees and one for executing getAttendeesPictures, and then make it explicit that the second block depends on the first before adding them both to a queue. They will then both execute and you can use a completion block on the second operation to do something once both have completed.
Dave Roberts is right in his response though: an immediate problem with the code is that you don't use the output of the getAttendees function to actually create any attendees. Perhaps this part of the code is missing, but from what I can see the name and objectID are just printed out. If you want to pass something useful into the getAttendeesPictures function you will need to fix this part first.
This is off the top of my head. The idea is to read and handle new asyc data only when all of the nested blocks complete.
We leverage a while loop to handle waiting for a signal to read the next set of data.
The outside while loop continues as long as done equals false. And nothing is really going on, other than consuming cpu cycles while it waits. The if inside the loop will only be trigged (set to true) when all of the attendees have been read.
Meanwhile inside the loop we work through nested blocks, reading in the attendee and then when that completes, read their picture, and when that completes read the firebase data. Finally once we have all data from the prior blocks we stuff the data into an object which is then added to the dictionary. At that time it is determined if we are finished reading attendees and if so, bail completely. If not, we read the next attendee.
(this is conceptual)
done = false
readyToReadNextAttendee = true
while ( done == false )
{
if (readyToReadNextAttendee == true ) {
readyToReadNextAttendee = false
readAttendee
readPicture
readFirebase {
putDataIntoObject
addObjectToDictionary
if finishedReadingAttendees {
done = true
} else {
readyToReadNextAttendee = true
}
}
}
}
If you have the option of reading in all of the attendees first, you could iterate over and array as well, not reading the next index until readyToReadNextAttendee = true
One Idea i have used is to place an if statement check inside the query statement call back and place the query statement call back in a for loop (so you can loop through all of your queries), so the if statement should check if this the last call back expected, then you should execute a return statement or a deferred.resolve statement, the following is a concept code.
var list=fooKeys //list of keys (requests) i want to fetch form firebase
var array=[] // This is the array that will hold the result of all requests
for(i=xyz;loop breaking condition; i++){
Ref = new Firebase("https://yourlink.firebaseio.com/foo/" + fooKeys[i]);
Ref.once("value", function (data) {
array.push(data.val());
if(loop breaking condition == true){
//This mean that we looped over all items
return array; //or deferred.resolve(array);
}
})
}
Putting this code in a function and call it asynchronously will give you the ability to wait for the whole results before proceed in doing other stuff.
Hope you (and the others) find this beneficial.

Resources