I have created three tables in Realm and there are relationships identified between them (LBContests <-> LBQuizzes <-> LBLeaders) and each is many to many relationships. This is to represent leader boards where there are "LBLeaders" for "LBQuizzes" within "LBContests" and the LBLeaders contains userNames (I spared the additional field details where the score is tracked as an example - for simplicity).
import Foundation
import RealmSwift
class LBContests: Object {
@objc dynamic var contestName: String = ""
let childrenLBQuizzes = List<LBQuizzes>()
}
class LBQuizzes: Object {
@objc dynamic var quizName: String = ""
let childrenLBLeaders = List<LBLeaders>()
var parentContest = LinkingObjects(fromType: LBContests.self, property: "childrenLBQuizzes")
}
class LBLeaders: Object {
@objc dynamic var userName: String = ""
var parentQuizzes = LinkingObjects(fromType: LBQuizzes.self, property: "childrenLBLeaders")
}
With knowing the contestName and the quizName, I am trying to identify how to add userNames within LBLeaders related to LBQuizzes (with the known quizName) and further related to LBContests (with the known contestName).
I have tried searching, reading, watching videos - but they are all for simple one or two level relationships and I need to handle 3 levels of relationships. I have only been able to append LBQuizzes to the LBContests.childrenLBQuizzes, but haven't been able to find a way to then append a LBLeaders record to the LBContests record that has a child LBQuizzes record and its childrenLBLeaders record(s)...(for the known contestName and quizName)
I know that part of my problem is that I don't fully understand the differences and when to use Objects versus Results versus Lists... I can perform a filter to get a Result, but I need an object to either reference the child relationships and/or to append a record...
Can someone please:
Identify the Swift Realm code to add LBLeader records (userName) for the known related LBQuizzes and LBContests
Provide an overview of how, when, and why to use Objects, Results, and Lists properly
Provide insights on how the Objects, Results, and Lists can be used together or definitively identify if they cannot be used together (this is my understanding) - you get Results and/or Lists from or Objects being filtered, but they are not convertible from one to the other (i.e. you can't use a Result or a List like an Object)
Let me see if I can tie it all together.
1) You've got a known contest and a known quiz and you want to add a new LB Leader to the quiz. There are a few ways to tackle this but here's a long answer:
First, I set up two contests with contest 0 having two quizzes and contest 1 having a single quiz. Printing it to the console results in
contest 0
quiz 0
quiz 1
contest 1
quiz 2
Then, suppose I want to add a new LBLeader, John, to contest 0, quiz 1. Here's the code.
let knownContest = "contest 0"
let knownQuiz = "quiz 1"
let leaderToAdd = LBLeaders()
leaderToAdd.userName = "John"
let contestResults = realm.objects(LBContests.self).filter("contestName == %@", knownContest)
if let thisContest = contestResults.first {
let quizResults = thisContest.childrenLBQuizzes.filter("quizName == %@", knownQuiz)
if let thisQuiz = quizResults.first {
try! realm.write {
thisQuiz.childrenLBLeaders.append(leaderToAdd)
}
}
}
and when printing the same information results in John being added to contest 0, quiz 1.
contest 0
quiz 0
quiz 1
John
contest 1
quiz 2
There are other ways to accomplish the same thing (e.g. the shorter answer). For example, this one liner returns the same quiz and leverages the inverse relationship
let quizResults = realm.objects(LBQuizzes.self).filter("quizName == %@ AND ANY parentContest.contestName == %@", knownQuiz, knownContest)
2) Objects, Results and Lists are three different Realm concepts and are pretty will covered in the Realm Documentation.
Speaking generically:
A Realm object is a single instance of an object, like a Person or a Dog. (there are a number of things considered 'Realm Objects' but we'll keep it limited for brevity)
A realm Results (object) is the set of objects returned from the Realm database. Keeping in mind that as shown in my first solution, contestResults were returned first, but then I can get a subset of those objects in quizResults. Furthermore, Realm results are live updating - they stay 'attached' to the database so as objects come into or leave the scope of the results, the results always reflect that.
A List is a 'container' that defines a one to many relationship. Very similar to an array and has a lot of the same functionality. A person has many dogs, so that would be a good use of a List, which you seem to be doing. The one gotcha is the inverse relationship of LinkingObjects. Notice its LinkingObjects (plural) so if you leverage that, keep in mind it also means a dog can have many people. The advantage is that it automagically creates those relationships as data is written but the downside is that it's not a singular inverse relationship, so you have to code accordingly (using .first as in my example).
3) HUGE question and not really answerable within the scope of SO as it would take a LOT of documentation to explain all of those interactions.
Some food for thought:
Primary Keys can, often times, make things a lot easier and faster, working with relationships as specific objects can be addressed by their unique primary key instead of relying on queries for, in this case, a quiz name.
For example, if your objects all had a primary key, adding a new leader to a specific quiz becomes
class LBQuizzes: Object {
@objc dynamic var quiz_id = UUID().uuidString
and then you can get that specific object via its quiz_id and add the leader.
let quizId = //whatever the primary key is of this quiz object
let quizObject = realm.object(ofType: LBQuizzes.self, forPrimaryKey: quizId)
try! realm.write {
quizObject?.childrenLBLeaders.append(leaderToAdd)
}