We have a situation where we are receiving the compiler error
Cycles containing embedded objects are not currently supported
but are not clear why.
From what we gather, cyclic lists containing embedded objects are not supported. e.g. an embedded object can’t reference other of the same object in a List. Do we have that?
Here’s our setup
class Widget: Object {
@Persisted(originProperty: "parentWidget") var subWidgets: LinkingObjects<SubWidget>
}
class SubWidget: EmbeddedObject {
@Persisted var parentWidget: Widget!
@Persisted var thingList: RealmSwift.List<Thing>
@Persisted var subSubWidgetList = RealmSwift.List<SubSubWidget>()
}
class SubSubWidget: EmbeddedObject {
@Persisted var parentSubWidget: SubWidget!
}
class Thing: Object {
@Persisted(originProperty: "thingList") var linkedSubWidgets: LinkingObjects<SubWidget>
}
We have a Widget that has related and computed Subwidgets, and each of the Subwidgets needs to know about its parent Widget.
The SubWidget has a List of SubSubWidgets, and each of those SubSubWidgets need to know about it’s parent SubWidget.
Meanwhile, the SubWidget has a List of Things, with each Thing with a computed inverse relationship to the SubWidget.
Ultimately, the Thing need to get info from the SubWidget; it’s parent Widget and it’s SubSubWidgets.
Where is the recursiveness in the above? Here’s the error
Error!
Schema validation failed due to the following errors:
Cycles containing embedded objects are not currently supported: ‘SubSubWidget.parentSubWidget.subSubWidgetList’
Cycles containing embedded objects are not currently supported: ‘SubWidget.subSubWidgetList.parentSubWidget’ 1
The cycle is Subwidget pointing to SubSubWidget via subSubWidgetList and SubSubWidget pointing to SubWidget via parentSubWidget. Even if they’re not pointing to the same object, it’s a schema cycle, which is not supported.
It’s not entirely clear that this is the schema design you want anyway - since embedded objects are contained in their parent, SubSubWidget.parentSubWidget would be a new object instance rather than the SubWidget that contains it. Visualizing this as json, this would look something like:
{
// This is SubWidget 1
"subSubWidgetList": [
{
// This is SubSubWidget 1
"parentSubWidget": {
// This is SubWidget 2 - different from SubWidget 1
"subSubWidgetList": [
{
// This is SubSubWidget 2 - different from SubSubWidget 1
"parentSubWidget": { ... }
}
]
}
}
]
}
As you can see, since there are no links here, it has the potential to continue forever as each object will need to contain the entirety of the embedded object it links to. Instead, you probably want to have the parentSubWidget be a computed property, calculated as the first element of backlinks collection (free style typing here, so might not compile, but hopefully conveys the idea):
class SubSubWidget: EmbeddedObject {
@Persisted(originProperty: "subSubWidgetList") private var linkingParents: LinkingObjects<SubWidget>
var parentSubWidget: SubWidget {
// A managed SubSubWidget will always have exactly one object linking to it
linkingParents.first!
}
}
And a final note, embedded objects cannot be free-standing (i.e. have no parent pointing to them). Perhaps you’ve only posted a portion of your schema, but it doesn’t appear like SubWidget is being pointed-to from a standalone object. An inverse relationship (i.e. linking objects) doesn’t count for this - it has to be either a direct link or a persisted (non-computed) collection containing the embedded object.
The original intent was for the Widget to have a List of SubWidgets (embedded objects) which provided an actual managed parent.
The use case changed along the way where I needed to transverse the graph from the Widget to the SubWidget without modifying the managed Widget within a write so the LinkingObjects computed aspect was an attempt.
As you point out, an embedded object cannot exist outside a parent so I kinda coded myself into a corner on that one. I am re-thinking this based on your super helpful suggestion.