One-to-one relationship realm with the new Live Inverse Relationships - ios

I have a a class similar to this:
class Flight: Object {
dynamic var flightNumber = ""
dynamic var departureApt = ""
dynamic var arrivalApt = ""
dynamic var nextFlight : Flight?
dynamic var previousFlight : Flight?
}
A 'flight' may contain nextFlight and/or previousFlight, but it is not guaranteed. My goal is to query for a flight and check if it is a child of another flight (either next or previous of a parent flight).
Before Realm 0.100 i retrieved the parent by using this code:
if let parent = flight.linkingObjects(Flight.self, forProperty: "previousFlight").first {
print("This flight has a parent flight: \(parent.flightNumber)")
}
I am now unsure how to achieve the same with Realm 0.100 and would love some feedback!
EDIT
I have now been able to achieve the result I wanted, but I am not sure if it is the correct way to do it. I have added a variable that finds the parent this way:
let parentForNext = LinkingObjects(fromType: Flight.self, property: "nextFlight")
let parentForPrevious = LinkingObjects(fromType: Flight.self, property: "previousFlight")
var parent : Flight? {
get {
return parentForNext.first ?? parentForPrevious.first ?? nil
}
}

The Object.linkingObjects(_:property:) method was deprecated in Realm Swift 0.100 (see release post for more).
This is because there's a more powerful (though slightly more verbose) way to declare inverse relationships in Realm: through the LinkingObjects property type on models. This allows linking objects to behave like live, auto-updating List properties or Results queries. So you know the objects you get back will never be out of date, which is especially important given that all other Realm types auto-update. It also allows querying inverse relationships as if they were forward relationships (List properties).
So if you did this before 0.100:
let parent = flight.linkingObjects(Flight.self, forProperty: "previousFlight").first
The equivalent is this in 0.100 and later (on the model):
let parents = LinkingObjects(fromType: Flight.self, property: "previousFlight")
var parent: Flight? { return parents.first }
In your second code sample, you have multiple "parents", and yes you can have a computed property that combines them in whatever way you like:
let nextParent = LinkingObjects(fromType: Flight.self, property: "nextFlight")
let previousParent = LinkingObjects(fromType: Flight.self, property: "previousFlight")
var parent: Flight? { return nextParent.first ?? previousParent.first }
You can read more about inverse relationships in Realm's docs on the subject: https://realm.io/docs/swift/latest/#inverse-relationships

Related

Swift Realm - get types of all database objects

I have a list of various objects in my Realm Database all of which are created as default ClassName: Object classes. Is there any way to get types (classes names) of those objects, that can be saved as variables, or create an enum of these types?
The issue is that Realm Results objects are homogenous - they only contain one object type. That translates to meaning you will never have a situation where Results would contain Person and Dog classes at the same time.
That being said we have the option of using the Realm AnyRealmValue
Let me set this up: Suppose you have a PersonClass and a DogClass
let dog = DogClass()
let person = PersonClass()
and a class with a List that can contain AnyRealmValue
class MyClass: Object {
#Persisted var myList = List<AnyRealmValue>()
}
when then need to cast those objects to AnyRealmValue to make this work
let obj0: AnyRealmValue = .object(dog)
let obj1: AnyRealmValue = .object(person)
and we add those to the List
let m = MyClass()
m.myList.append(obj0)
m.myList.append(obj1)
You mentioned switch but here's a simple if...then clause to handle them differently - depending on the class
if let person = item.object(PersonClass.self) {
print("is a person")
} else if let dog = item.object(DogClass.self) {
print("is a dog")
}

Realm: property declared as origin of linking objects property does not exist

I'm using Realm Swift for the following code:
class Item: Object {
#Persisted(primaryKey: true) var name = ""
#Persisted(originProperty: "items") var collection: LinkingObjects<SomeCollection>
}
class SomeCollection: Object {
#Persisted(primaryKey: true) var name = ""
let items = List<Item>()
}
On app startup when I initialize the Realm instance I got:
Property 'SomeCollection.items' declared as origin of linking objects property 'Item.collection' does not exist" UserInfo={NSLocalizedDescription=Schema validation failed due to the following errors:
Interestingly, if I change the line for LinkingObjects to:
let collection = LinkingObjects(fromType: SomeCollection.self, property: "items")
it then works fine, however, after adding an Item instance to the items list of SomeCollection, the collection property of Item instance doesn't show any linked SomeCollection, which I thought should be linked automatically?
What am I doing wrong here?
Thanks for help!
This
let items = List<Item>()
Needs to be this
#Persisted var items = List<Item>()
or
#Persisted var items: List<Item>
depending on the use case.
The let syntax is how we used to do it.
That's called and Inverse Relationship and is covered in the documentation a bit further.

How Query and Count entries in a Realm Database

I'd like to build a search on which the user can filter down the results step-by-step. So with no choice set, there is a button which says e.g. "1,234,567 Results" and if you choose a color for example the results set shrinks... we all know this kind of search. I did build it many times, but this is the first time in Realm (and swift).
Lets Say I have 5 Persons in my Person Table, then there are about 145,224 Dog entries per Person and about 2,507,327 Cat entries per Dog. How do I query and Count nested Objects in Realm?
class Person: Object {
#objc dynamic var name = ""
let dogs = List<Dog>()
// ...other Properties
}
extension Person {
static func all(in realm: Realm = try! Realm()) -> Results<Person> {
return realm.objects(Person.self)
}
}
// counts -> 145,224 db entries per person
class Dog: Object {
#objc dynamic var name = ""
dynamic var Person: Person?
let cats = List<Cats>()
// ...other Properties as well
}
extension Dog {
static func all(in realm: Realm = try! Realm()) -> Results<Dog> {
return realm.objects(Dog.self)
}
}
// counts -> 2,507,327 db entries per dogs
class Cat: Object {
#objc dynamic var name = ""
dynamic var Cat: Cat?
}
extension Cat {
static func all(in realm: Realm = try! Realm()) -> Results<Cat> {
return realm.objects(Cat.self)
}
}
// Get the default Realm
let realm = try! Realm()
// Query Realm for all dogs
let dogs = Person.all(in: realm).flatMap { $0.dogs }
dogs.count // => takes ~20 seconds to count
In other words, what is the fastest way to get (count) all Dog entries of all Persons (let the cats by side for now).
I tried to workaround the problem by limit the results to 1000. If the results are >1000, then label the button like so "> 1000 Results". But even than it takes very long (I guess the get all count anyway).
So what did I do wrong?
They way you were computing the count required all Dog objects to be loaded into memory which is really inefficient. This is why you were seeing such poor performance. You want to take advantage of Realm's lazy loading features. You may want to read up on that.
I would update your Dog object by getting rid of your managed Person property and replace it with LinkingObjects. If you store a Dog in a Person.dogs List, then realm will create a back link to the Person for you. You will likely want to do the same thing with Cat. That way you can set up some really powerful nested queries.
For convenience you can add a computed Person property to index into the LinkingObjects. Just know that you won't be able to use that property in any of your queries.
class Dog: Object {
#objc dynamic var name = ""
let persons = LinkingObjects(fromType: Person.self, property: "dogs")
var person: Person? { persons.first }
}
One way to compute the count is to query of all Person objects and sum the count of dogs for each Person.
let count = realm.objects(Person.self).reduce(0) { result, person in
return result + person.dogs.count
}
Another options is to query for Dog objects that have a corresponding Person. If a Dog is not in a Person.dogs List, then it won't show up the query.
let realm = try! Realm()
let count = realm.objects(Dog.self).filter("persons.#count > 0").count
Either option is going to be much, much more efficient than what you had.
Hope this helps.

Retrieving object properties with relation to their parent categories

I am new to realm and iOS development so I apologize in advance if something isn’t explained properly or is just incorrect.
I have 2 Realm Object classes:
class Category: Object {
#objc dynamic var name: String = ""
#objc dynamic var color: String = ""
let trackers = List<Tracker>()
}
and
class Tracker: Object {
#objc dynamic var timeSegment: Int = 0
var parentCategory = LinkingObjects(fromType: Category.self, property:
"trackers")
}
I’m able to store new timeSegment properties consistently; however, the issue is that I cannot retrieve & display a collection of timeSegment values relating to their parentCategory. setting
var entries : Results<Tracker>?
produces all results for every category, which is the only result i'm able to pull so far after testing.
Any help is appreciated, and can follow up with any additional details. Thanks
You need to call objects on your Realm object with a filter for fetching only results that match a predicate. The realm object in this code is an instance of the Realm class.
func getTrackersWithName(_ name: String) -> Results<Tracker> {
return realm.objects(Tracker.self).filter("name = \"\(name)\"")
}
This tells Realm to fetch all objects that match the filter predicate. In this case, the filter predicate matches any object where the value of the "name" property matches the string that is passed into the method.

Computed properties using Realm LinkedObject instances return nil

I'm experiencing slightly unusual behaviour when attempting to use computed properties to access linked Objects in a Realm Object subclass.
final class Patient: Object {
dynamic var name: String = ""
var parameters = List<Parameter>()
}
final class Parameter: Object {
dynamic var name: String = ""
dynamic var patient: Patient? {
return LinkingObjects(fromType: Patient.self, property: "parameters").first
}
}
The patient property on the Parameter class returns nil but, if you replace the code with the following, we get the expected behaviour:
var p = LinkingObjects(fromType: Patient.self, property: "parameters")
var q: Patient? {
return p.first
}
I suspect this is something to do with Realm's internal representation of LinkingObject. The code I used originally was referenced in a previous StackOverflow question and was accepted as a functional solution thus I guess it worked then so perhaps something has changed? Xcode 7, Swift 2.2
When Realm added the ability to query inverse relationships, that became the syntax to specify them. See https://realm.io/news/realm-objc-swift-0.100.0/ and https://realm.io/docs/swift/latest/#inverse-relationships for details.

Resources