Saving Lists does not automatically happen during a write

I am quite confused about how lists are saved in a realm.

If I have the following schema:

class First: Object {
  @Persisted var children = List<Second>()

  convenience init(children: [Second]) {
    self.init()
    self.children.append(objectsIn: children)
  }
}

class Second: Object {
  @Persisted var children = List<Third>()

  convenience init(children: [Third]) {
    self.init()
    self.children.append(objectsIn: children)
  }
}

class Third: Object {

}

If I try to save a First object like this:

let first = First(children: [Second(children : [Third()])])
let realm = try! Realm() 
realm.write {
  realm.add(first, update: .modified)
}

The children Second objects are not saved.  What I have to do is this:

let first = First(children: [Second(children: [Third()])])
let realm = try! Realm()
let children = first.children
try! realm.write {
realm.add(children, update: .modified)
realm.add(first, update:.modified)
first.children.append(objectsIn: children)
}
`
and what is even more odd is that the children of the Second objects are save to the realm successfully.

Am I missing something here, or am I doing something wrong?

HI @Nathan_Mann, thank you for using the Swift SDK.

I believe it is because of the 2 levels of arrays of embedded objects. If the Second class did not have the children array of objects, or if it was an array of primitives (e.g. String, int, etc.) then I think it should be saved successfully.

I have reached out to the Swift SDK team to confirm.

The Swift SDK team confirmed that your original code should work and I created a ticket for them to investigate.

Can you please provide some additional information:

  • Swift SDK version
  • Target platform (ios, macos, ipad, etc.) and version

Thank you!

The Swift SDK team go back to me.
Their recommended changes to your example code are:

  • the lists should be @Persisted let children: List<Second>
  • First needs a primary key
  • Third needs a property of some kind

Example code:

class First: Object {
    @Persisted(primaryKey: true) var pk = 0
    @Persisted var children = List<Second>()

    convenience init(children: [Second]) {
        self.init()
        self.children.append(objectsIn: children)
    }
}

class Second: Object {
    @Persisted var children = List<Third>()

    convenience init(children: [Third]) {
        self.init()
        self.children.append(objectsIn: children)
    }
}

class Third: Object {
    @Persisted var value = 0
}

let first = First(children: [Second(children : [Third()])])
let realm = try! Realm()
try! realm.write {
  realm.add(first, update: .modified)
}
print("\(realm.objects(First.self).count) \(realm.objects(Second.self).count) \(realm.objects(Third.self).count)")

Hope this helps!

To add a bit more clarity

If this is the case now

  • the lists should be @Persisted let children: List<Second>

then the documentation should be updated

  • First needs a primary key

Well it does and doesn’t. In this case it needs a primary key because of how realm is being written to. Primary keys are required* if you want to update an object BY that primary key - that’s called an upsert

To upsert an object, call Realm.add(_:update:) with the second parameter, update policy, set to .modified . Upserts require the data model to have a primary key.

On the other hand, if you just want to create the object(s) and not use a primary key, then change the write code

    try! realm.write {
        realm.add(first)
    }

On a somewhat unrelated note, List is actually a naming collision with SwiftUI List. So it’s probably a good idea to name your list properties with that

@Persisted var children = RealmSwift.List<Second>()

So - in a nutshell, if you take your code and add a property of any kind to Third, and then write to realm per above, it works perfectly.

*primaryKeys must be used if you use Realm Sync.