0

I'd like to start using Firebase as my main BaaS now that Parse is closing. At the moment, Firebase's real-time capabilities are interesting and I see using them in the future, but for now I'd primarily like to use Firebase as a way to store a User and there associated data. With Parse, this was super easy, which required only uploading an object with the associated user attached to the object. I could then filter by the objects which were associated with that user.

However, in Firebase everything is Event driven as opposed to on-demand and you can't filter by security rules. In general, I like this approach, but I'm having trouble applying it to what I need today. I don't have much of background in networking/databases, so its possible some of this stuff is really basic.

Here is what I'm looking to do:

My Firebase data structure looks like this currently:

xnterval
   users
      100b920f-d2c5-4578-9f49-ecf38ef71302
      32a8dbf2-009a-44c5-a188-ca2c280ba135
      d53b0b37-d773-4cf5-af94-74635fc76f1f
         name: "Joe Schmo"
         provider: "password"
         username: "jschmo"
         workouts
            -K9wZ4_Rt-xBdVZt93d4
                workout_id: "-K9wZ4_Rt-xBdVZt93d3"
   workouts
      -K9wZ4_Rt-xBdVZt93d3
            interval_period: 180
            intervals: 2
            name: "Workout"
            owner: "d53b0b37-d773-4cf5-af94-74635fc76f1f"
            rest_period: 150
            work_period: 30

enter image description here

Inside the User/"uid" I have a workouts list which is order by childByAutoId and contains the ID of a single workout.

What I want to be able to do, is get a list of all the user's workouts and then retrieve that complete workout list from the workouts section.

I know the PATH directly to the users workouts root/"uid"/workouts, but how would I retrieve the entire list and get each "workout_id" and then subsequently query for that list of workouts in the /workouts section?

I've also already setup Security rules such that no other user can access another users data, and the only user that can read/write a workout is if the owner of that workout is the UID on the authenticated user. This I do have working correctly.

Maybe I'm going about this the wrong way, or there is an easier way to structure my data to accomplish what I want. I read a few articles on de-normalizing your data in order to optimize the amount of transfer required in a query, and building for the future I feel like this data structure is ideal for that. But if you have more experience, please let me know. Thanks!

Update

So I've create this method which will retrieve all the user's workout ID's and store them into an array userWorkoutIds:

private func observeAllUserWorkouts() {
    if userAuthData != nil {
        workoutObserver = userRef?.childByAppendingPath("workouts").observeEventType(FEventType.Value, withBlock: { snapshot in
            let enumerator = snapshot.children
            while let child = enumerator.nextObject() as? FDataSnapshot {
                NSLog("\(child.childSnapshotForPath("workout_id").value)")
                self.userWorkoutIds?.append(child.childSnapshotForPath("workout_id").value as! String)
            }
        })
    }
}

Now that I know exactly the paths of all the users workouts /workouts/"IDs", how can I poll for all of them at once as opposed to setting up single shot observers for all the different paths? OR is that the only option?

bryken
  • 157
  • 3
  • 14

1 Answers1

1

A super quick solution: add the uid of the user to the workout_id node.

Then you can query on the workouts node for uid equal to the user of whose workouts you want to retrieve.

workouts
   workout_id_0
     interval_period: 180
     internvals: 2
     uid: user_id
   workout_id_1
     interval_period: 180
     internvals: 2
     uid: user_id

A second option:

If you know the path to the user workouts

/users/uid_id/workouts

you could observe the node by value, which would read in all of the children in that node into a snapshot. Then iterate over the snapshot to get specific data. I personally would load each item in the snapshot into an array of dictionaries or maybe even workout object.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • Actually, the "owner" property of my workouts is indeed the user's ID. I have not tried doing what you said, only because I thought that because my security rules say that only a user who "owns" that workout can read/write that workout. Meaning, if I did a query on all the workouts the query would fail trying to read a workout who's owner is not the current authenticated user. I've only read this, so I will try it however to see myself. Thanks. – bryken Feb 08 '16 at 19:22
  • I added a bit more to the answer as an alternative. – Jay Feb 08 '16 at 19:24
  • Thanks for the tip. I've tested a few things out and got a bit further. I've updated my original question. – bryken Feb 09 '16 at 00:31
  • I think what you are asking is: now that all of the workout_id's are in the array, how can just those workouts be loaded from the workouts node. Maybe changing your structure would make more sense in this case. In your example structure, there isn't really any reason to have a separate /workouts node. That data could be easily stored within the /users/user_id/workouts node, then you would have everything you need with the code you added. *note that I am a big fan of flatter structures are better, but in this case it may not be necessary. Is there some reason the /workouts node is separete? – Jay Feb 09 '16 at 18:35
  • Yeah I thought about that as well. And while it makes sense for this case, in future I can see good reason to flatten this data for sharing purposes. – bryken Feb 09 '16 at 18:42
  • 1
    For sure! That can be addressed by duplicating the data you want to share, or storing private data in /users/user_id/workouts and shared data in a /workouts/shared_data node or a number of other solutions. Seems like you have an answer and a direction to work towards. Don't forget to accept the answer if it helped! – Jay Feb 09 '16 at 18:46
  • Thanks, makes sense! – bryken Feb 09 '16 at 18:47