0

Consider the following database structure -

tasks
    task1
        title: "My first task"
        uid: "user1"
user-tasks
    user1
        task1: true

To observe changes to a specific user's tasks, you might be tempted to implement something like this -

ref.child("user-tasks").child(uid).observe(.childChanged...

But the problem is that any changes to title won't reflect in user-tasks, so childChanged event won't trigger. If you observe all tasks, then any changes to tasks for other users would cause the observer to execute needlessly. Additionally, the desired implementation would handle updates to the task by other users/devices and the update would trigger correctly.

My only thought thus far is to replace true with a timestamp that gets updated on change and would trigger the observer. Is there a better way?

Andy
  • 5
  • 4
  • What does `task1: true` represent? – 3stud1ant3 Sep 23 '17 at 04:01
  • Yes, I have used timestamp approach as well and I locally cache the last sync timestamp (in core data). Subsequent times I query for entries greater than that last synced timestamp (from core data). – user1046037 Sep 23 '17 at 04:26
  • @3stud1ant3 is a "look up" table or an index list, represents every task owned by the user. This way that node contains each key for the tasks that the user have. – cutiko Sep 26 '17 at 15:30

3 Answers3

0

It depends on what you're trying to accomplish. For example: if you want to show a list of task names for the current user, you will indeed need two types of listeners with your current data model:

  1. A listener for the user's task list under /user-tasks.
  2. A listener for each visible task of the user under /tasks to get the name.

This leads to 1+N listeners, where N depends on the number of tasks that is visible in your UI. While this is typically not a technical problem for Firebase, it takes some effort to manage the listeners correctly in your code.

An alternative would be to duplicate the name of each task under /user-tasks:

user-tasks
    user1
        task1: "My first task"

With this data structure you will only need to listen to /user-tasks to get all information to display the task list. But in turn you will now have to deal with keeping the duplicated data up to date in write operations.

There is no single best practice here: it all depends on the need of your application and your willingness to duplicate data, manage listeners, and the read-vs-write performance requirements for your app.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
0

In that case, I would indeed recommend you to use your task timestamp instead of true.

Then everytime you change something in your object (like the title or something else), you would need to update this value as well (using updateChildValues)

This would allow you to know if (and when) an update occured, and it would also call your childChanged or childAdded observer.

Your database would look like this, which is exactly the same as what you showed but replacing true by a more meaningful value, your timestamp:

root/
|___ tasks/
|      |___ taskID1
|              |___ title : task_title
|              |___ timestamp : task_timestamp1
|         ...
|
|___ user_tasks/
|            |___ userID1
|                      |___ taskID1: task_timestamp1
|                      |___ taskID2: task_timestamp2
|                       ...
|           ...

There are maybe some other solutions, but this is the easiest I can think of now, and doing this would not require you many changes in your project.

0

Use a query.

let taskQuery = taskRef.queryOrdered(byChild: "uid").queryEqual(toValue: "user1")
taskQuery.observe(.childAdded, with: { snapshot in
    print(snapshot)
})

This will only fire when a task is added to the tasks node for user1. Add similar code for .childChanged and .childRemoved.

Also, storing task1: true within the user node may be redundant since you can always query the tasks node for user1 tasks. It depends on what else you may need that data for though.

Jay
  • 34,438
  • 18
  • 52
  • 81