Converting Core Data programs to work with SwiftData - Part 2
Swift, SwiftUI, SwiftData
List views with SwiftData objects
Typical handling of CoreData objects in a list view is to include a static FetchRequest that will fetch all (or a filtered subset of) objects and would also dynamically change based on some state variables. Here is what it would look like for the Recipe class:
The corresponding SwiftData implementation is seen below.
CoreData uses the ViewContext (passed to relevant views in an environment variable) to create/update/delete objects. The static @FetchRequest statement defines a query that will be automatically executed when the view loads. The result is a FetchedResults collection which stores the retrieved objects. It is also required to define the sorting rule for the collection.
In contrast, SwiftData uses the ModelContext and the result of the query is an array or set of objects.
Creating and Saving
While creating an object to be persisted with CoreData (so, a subclass of NSManagedObject), we have to pass the context to the initialiser so that CoreData can already add the new object to the context (viewContext).
However, in SwiftData we do not need to pass the model context to the initialiser and would have to insert the object to the model context after it has been created.
One major difference is that you do not have to explicitly save the model context in SwiftData, since SwiftData saves automatically when required. This sounds tricky but I have not yet seen any drawbacks.
Observability
When we define classes with CoreData, we make sure these inherit from NSManagedObject. This allows us to deal with them using CoreData methods. Codable is a protocol we would frequently need classes to conform to.
When we move to SwiftData, objects no longer inherit from NSManagedObject. We can keep them at root level and annotate them with @Model and we can still use Codable if required.
There is an invisible benefit to using SwiftData. @Model also makes the class that it annotates to conform to Observable.
Under CoreData, objects can be passed to views in different ways. One way to do this is to pass the object with the @ObservedObject qualifier. The calling view would pass this as a parameter.
It also acts as a state variable, so any changes to the object would trigger updates in the view, if there is any part of the view dependent on the object. It would also be possible to pass this object further down in the view hierarchy or use it in any function that requires a binding to a state variable.
Since recipe is an ObservableObject, it can be passed to any view that requires a binding, using $recipe.
With SwiftData, there is no need to declare ObservableObject since all SwiftData objects are observable. However, if you need to use a binding with the object then you have to declare it as @Bindable. If the passed object is going to be used as read-only in the view or does not need a binding, then you can leave out the @Bindable marking.
Future
I expect Apple to introduce more API elements for SwiftData in the future, thus enabling developers to fine-tune queries or optimize retrieval for SwiftData-persisted objects.
I have not yet attempted to do SwiftData queries in a separate thread, but that is already possible with the ModelActor protocol, but I have to yet try it out. I expect optimisations in this area as well.
As to usability, I would say there is still a risk in full conversion of existing applications from CoreData to SwiftData, as it is a quite early implementation, but I would seriously consider using SwiftData in new development.