0
votes

I've following Realm objects (init and other non-essential properties removed for brevity) in my Swift app

@objcMembers
class Person: Object {
    dynamic var id: String = ""
    dynamic var name: String = ""

    override static func primaryKey() -> String? {
        return "id"
    }
}

@objcMembers
class Project: Object {
    dynamic var projectId: Int = 0
    dynamic var manager: Person?
    var tasks = List<Task>()
    dynamic var lastTask: Task?

    override static func primaryKey() -> String? {
        return "projectId"
    }
}

@objcMembers
class Task : Object {
    dynamic var project = LinkingObjects(fromType: Project.self, property: "tasks")
    dynamic var taskId: String = ""
    dynamic var description: String = ""
    dynamic var createDate: Date = Date()

    override static func primaryKey() -> String? {
        return "taskId"
    }
}

So if there are 2 project members

Person(id: 1, name: "Foo")

Person(id: 2, name: "Bar")

and multiple projects,

Project(projectId: 100, manager: , messages: [a1, a2, a3], lastMessage: )

a1 = Task(project: <#100>, taskId: "a1", description: "Task 1 about project 100", createDate: Date() )

a2 = Task(project: <#100>, taskId: "a2", description: "Task 2 about project 100", createDate: Date() )

a3 = Task(project: <#100>, taskId: "a3", description: "Task 3 about project 100", createDate: Date() )

Project(projectId: 101, manager: , messages: [a1, a2, a3], lastMessage: )

b1 = Task(project: <#101>, taskId: "b1", description: "Task 1 about project 101", createDate: Date() )

b2 = Task(project: <#101>, taskId: "b2", description: "Task 2 about project 101", createDate: Date() )

b3 = Task(project: <#101>, taskId: "b3", description: "Task 3 about project 101", createDate: Date() )

b4 = Task(project: <#101>, taskId: "b3", description: "Task 3 about project 101", createDate: Date() )

Project(projectId: 102, manager: , messages: [a1, a2, a3], lastMessage: )

c1 = Task(project: <#102>, taskId: "c1", description: "Task 1 about project 102", createDate: Date() )

Project(projectId: 103, manager: , messages: [a1, a2, a3], lastMessage: )

d1 = Task(project: <#103>, taskId: "d1", description: "Task 1 about project 103", createDate: Date() )

d2 = Task(project: <#103>, taskId: "d2", description: "Task 2 about project 103", createDate: Date() )

d3 = Task(project: <#103>, taskId: "d3", description: "Task 3 about project 103", createDate: Date() )

In my ProjectsViewController

I can get my Realm results as

func getProjects() -> Results<Project> {
    let results: Results<Conversation> = database
        .objects(Conversation.self)
        .sorted(byKeyPath: "lastTask.createDate", ascending: false)

    return results
}

[Side note - If there is a better way of sorting the results based on last item of tasks List then do let me know. That'll make the use lastTask var redundant.]

which will display in my table view as

===================
Projects
-------------------
Foo 
Project 100
Task a3 
-------------------
Bar 
Project 101
Task b4
-------------------
Foo
Project 102
Task c1 
-------------------
Bar
Project 103
Task d3 
-------------------

Question: How do I group the results in Realm query so I get a dictionary of grouped Array of results, such as

Foo -> [Project 100, Array of Tasks], [Project 102, Array of Tasks]

Bar -> [Project 101, Array of Tasks], [Project 103, Array of Tasks]

and they're tracked via NotificationToken for all inserts / updates. Also I want to dislay them grouped by sections in the table view.

===================
Projects (Grouped)
-------------------
Foo (Section Header)
-------------------
Project 100
Task a3 
-------------------
Project 102
Task c1 
-------------------


-------------------
Bar (Section Header)
-------------------
Project 101
Task b4
-------------------
Project 103
Task d3 
-------------------
1
dynamic var lastTask: Task? may be problematic as Realm cannot manage user defined objects. Are you expecting it to in this case? Also on this a dictionary of grouped Array of results can you clarify what the key: value pairs would be in the dictionary? It's a bit unclear from the question. - Jay
Also, there is no Conversation.self object in your code... how does that correlate to the questions? Also, why do results need to be grouped? If you know an object has a section of Foo as as the section header, it would be added to that section in the tableview via the tableView delegate methods. - Jay

1 Answers

1
votes

I'll answer based on what I think you're asking.

If I understand correctly, you want to find a dictionary with the Person as the key, the values being a dictionary with Project ID (or Project) as the key and values of an array of tasks. i.e.

let result : [Person:[Project:[Task]]] = .....

If this isn't correct, you can probably take the ideas here and massage them into your ideal.

As you want to key by Person, I'd add a reverse lookup property to the Person class - and this is probably the main thing you're missing. Look at this section of the manual for guidance: (https://realm.io/docs/swift/latest/#inverse-relationships), and then add this property to Person:

let projects = LinkingObjects(fromType: Project.self, property: "manager")

I would then add a computed property to Person to retrieve the projects and tasks. This creates an array since that was asked for in the question.

var projectsAndTasks : [Project:[Task]]
{
  var result : [Project:[Task]] = [:]
  for project in projects
  {
    result[project] = project.tasks.map { $0 }
  }

  return result
}

If you want the tasks to be sorted, then add a computed property to Projects called sortedTasks that does that for you.

Once that's done, you can add a function somewhere (either global or static to Person) to add the results for all objects of Person into a single dictionary. E.g. :

extension Realm
{
  var allPersonsAndProjects : [Person:[Project:[Task]]]
  {
    var result : [Person:[Project:[Task]]] = []
    let persons = objects(Person.self)
    for person in persons
    {
      result[person] = person.projectsAndTasks
    }

    return result
  }
}

Forgive any compile errors.