How to handle required @relationship optionals in SwiftData CloudKit?

Hi all,

As you know, when using SwiftData Cloudkit, all relationships are required to be optional.

In my app, which is a list app, I have a model class Project that contains an array of Subproject model objects. A Subproject also contains an array of another type of model class and this chain goes on and on.

In this type of pattern, it becomes really taxxing to handle the optionals the correct way, i.e. unwrap them as late as possible and display an error to the user if unable to.

It seems like most developers don't even bother, they just wrap the array in a computed property that returns an empty array if nil.

I'm just wondering what is the recommended way by Apple to handle these optionals. I'm not really familiar with how the CloudKit backend works, but if you have a simple list app that only saves to the users private iCloud, can I just handwave the optionals like so many do? Is it only big data apps that need to worry? Or should we always strive to handle them the correct way? If that's the case, why does it seem like most people skip over them? Be great if an Apple engineer could weigh in.

Answered by DTS Engineer in 860915022

SwiftData + CloudKit uses NSPersistentCloudKitContainer under the hood, which requires "all relationships must be optional." For more information, see Creating a Core Data Model for CloudKit.

The requirement exists because of the latency of the synchronization: When you create an object graph in device A, which is being synchronized to device B, the system doesn't guarantee to synchronize the whole graph all at once. As a result, it's possible that an object is synchronized but its relationship is not. This situation is expressed as the relationship being nil. By checking if the relationship is nil, the app instance running on device B can consume the object appropriately.

In your case, wrapping a relationship with a computed property to return an empty array if nil makes sense to me, if the other part of your app prefers to consume an empty array. It doesn't matter if the data is big or small.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Accepted Answer

SwiftData + CloudKit uses NSPersistentCloudKitContainer under the hood, which requires "all relationships must be optional." For more information, see Creating a Core Data Model for CloudKit.

The requirement exists because of the latency of the synchronization: When you create an object graph in device A, which is being synchronized to device B, the system doesn't guarantee to synchronize the whole graph all at once. As a result, it's possible that an object is synchronized but its relationship is not. This situation is expressed as the relationship being nil. By checking if the relationship is nil, the app instance running on device B can consume the object appropriately.

In your case, wrapping a relationship with a computed property to return an empty array if nil makes sense to me, if the other part of your app prefers to consume an empty array. It doesn't matter if the data is big or small.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

@DTS Engineer Thanks for that. Just had one last question, when you say "if the other part of your app prefers to consume an empty array", what do you mean by that?

Sorry for not being clear. As an example, assuming you have some SwiftUI views that render the relationship, and the views accept only an non-optional array, it will make sense that your SwiftData model creates a computed property to wrap relationship. Here, the Swift views are "the other part of your app" that prefers to consume a non-optional array. I hope this makes the point clear.

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

How to handle required @relationship optionals in SwiftData CloudKit?
 
 
Q