Discover how you can start using SwiftData in your apps. We'll show you how to use Xcode to generate model classes from your existing Core Data object models, use SwiftData alongside your previous implementation, or even completely replace your existing solution.
Before watching this session, make sure you check out "Meet SwiftData."
♪ ♪ Luvena: Hi everyone! My name is Luvena, and I am excited to share with you how you may migrate your Core Data application to SwiftData, the Swift-native persistence framework that can coexist with Core Data.
I will cover common use cases for how you may adopt SwiftData for your Core Data app, whether you are ready for a complete transition from Core Data to SwiftData or if you would like to adopt SwiftData incrementally and coexist with Core Data.
I will first walk through how you can generate SwiftData Model Classes using the Managed Object Model Editor assistant.
Then, I will demonstrate the flow of a complete SwiftData adoption for an existing Core Data application.
And finally, I will introduce you to coexistence between Core Data and SwiftData, for cases where a complete transition into SwiftData may not be a feasible solution for your use case.
Let me first walk through the flow of how you can generate model classes.
In SwiftData, you are transitioning to using models in code to set up your schema.
One easy way to do this is to use your Core Data managed object model to help generate your SwiftData models. Let me demonstrate to you how to make this transition.
I created a Core Data application here called SampleTrips, which helps users plan their itineraries for upcoming trips. Here is the model file, which contains information about my data and its organization.
I have the entities Trip, LivingAccommodation, and BucketListItem, organized such that each trip has one corresponding living accommodation and bucket list items representing desired trip activities.
From here, I will generate SwiftData classes that capture this same information. While the Core Data managed object model is not necessary to create these SwiftData classes, if I have a preexisting Core Data model, I am able to use that to help generate SwiftData classes based on the organization that I already have in my preexisting model, which I will be demonstrating for this use case.
To do this, I go through our Managed Object Model Editor assistant.
After selecting my model file, I navigate to the menu bar, select Editor, and click on Create SwiftData Code. Now I can generate files for my three preexisting entities. Creating SwiftData models from scratch is also an option in the case of creating a new Swift app, and would not require this assistant.
Here are the files I have just created.
Your Swift types should conform to Model, and each one captures information for the entity as variables, including its attributes, which for Trip is its name, destination, and start and end dates, and its relationships to other entities, LivingAccommodation and BucketListItem. Now, let me take you through the complete adoption process for SwiftData. When fully migrating your application to SwiftData, you are replacing your Core Data stack with your SwiftData stack to harness the Swift-native language features with SwiftData. This allows for more legible code for persisting your data, as well as implicitly managing some features. Before making this transition though, you should consider how your preexisting core data model designs are structured. Your Core Data model designs refer to your schema, including the entities and their properties and relationships.
You need to make sure that your Core Data model designs are supported in SwiftData as well.
This means that for each entity that was defined in Core Data, there needs to be a corresponding model type with exact matches for entity name and properties in SwiftData.
Make sure you thoroughly test your models to verify that all features are supported in SwiftData.
Let me cover some highlights of the complete SwiftData adoption process from a Core Data app in more detail. I already generated Swift types to capture my model earlier. Once I have those files ready to go, I can delete the Core Data managed object model file that had been used previously, and manage the model through these Swift files instead.
Additionally, I can delete the Persistence file that previously helped to set up the Core Data stack.
Now I can set up the modelContainer for my SwiftData stack.
modelContainer is a modifier that ensures that all windows in the group are configured to access the same persistent container.
By adding this container here in TripsApp, I am setting up both my container and the context, as modelContainer also creates and sets a default ModelContext in the environment.
ModelContext is used to track changes to instances of my app’s types. It can be read from within any scene or view using this environment property.
My SwiftData persistent stack for SampleTrips is now set up, but there are a couple more changes that need to happen.
I am first going to demonstrate how object creation works.
Previously, in Core Data, creating a new Trip would resemble something like this. I would create an instance of Trip, passing in a managed object context, before setting its attributes.
In SwiftData, I can create a new instance of Trip with this line of code, which is already comparatively more legible.
Once I have this new trip, I insert it into the model context to ensure that it is persisted.
Now that my trip is being persisted, I want to look at how I can save it and any future changes that I make to it.
SwiftData has an implicit save feature that triggers saves on UI lifecycle events and on a timer after the context is changed if possible.
I can therefore remove Core Data’s explicit save that is called on the context, and rely on implicit saves to persist my data when the context changes.
Now, let me take you through how I fetch my data.
Instead of using a fetch request like I had in Core Data, I can use Query to fetch the full list of upcoming trips by wrapping an array of trips in Query.
This allows me to fetch those Trip objects from the SwiftData container.
I am also defining a sort order for the query. I want to sort it according to my trip start dates in forward order, such that the closest upcoming trip is at the top.
Query can also be used for use cases that would need to include a predicate. Now that you know how to migrate your Core Data app to SwiftData completely, let me take you through the case where you want coexistence between Core Data and SwiftData.
A full migration may not always be feasible or practical, in which case you may consider a partial conversion to SwiftData.
Coexistence is when there are two completely separate persistent stacks, one Core Data stack and one SwiftData stack, talking to the same persistent store.
This means that there is no need to completely rewrite existing Core Data code in order to be able to start adding SwiftData code.
This option provides you more flexibility when adopting SwiftData into your application, whether you may already have some data in Core Data, or if you are simply faced with other constraints that don’t allow you to transition completely to SwiftData. This is how you can set up both stacks to be talking to the same store.
Before loading the persistent store, you need to set the persistent store URL for the container description to ensure that both stacks are writing to the same URL. Additionally, you need to turn on persistent history tracking.
While SwiftData automatically turns on persistent history tracking, Core Data does not.
If you try to open a persistent store when you have both Core Data and SwiftData coexisting in the same application without setting the persistent history, your store will be put into read-only mode.
There are a couple different scenarios where you would find coexistence to be the best option for your use case.
One scenario is to allow backwards compatibility with existing clients.
Because SwiftData is only available in iOS 17 and macOS Sonoma, your current Core Data applications could be impacted by a full switch to SwiftData.
Alternatively, you might be facing resource constraints that make a full conversion to SwiftData difficult.
In these cases, it would make sense to incorporate SwiftData partially by doing new development using SwiftData, or converting only portions of your application to SwiftData. There are a couple requirements for coexisting between Core Data and SwiftData that you should consider before making this transition. First, you must either namespace preexisting NSManagedObject-based entity subclasses or the SwiftData classes such that they do not collide.
This means that no two classes can be called the same thing, and that at least one of them needs to change.
Keep in mind that even though you are changing the class name, the entity name stays the same. For example, here I have the same SampleTrips project from earlier, except this time I want to coexist with Core Data and SwiftData. The two class names clash.
To differentiate between both the SwiftData and Core Data names Trip, I changed the Core Data trip class name to CDTrip instead.
You can also do this in the managed object model editor.
Additionally, you would need to keep the Core Data and SwiftData schemas in sync. New parts of the SwiftData schema must be materialized as an NSManagedObjectModel, and the two schemas cannot diverge. This means that properties and relationships must be added to the models in the exact same way.
This is so that the entity version hashes are matching every step of the way, as mismatched hashes would potentially trigger a migration and delete information that you did not want to remove.
Finally, you need to keep track of the schema versions when incorporating coexistence.
When working with multiple versions of the SwiftData model, you must make sure that the changes are represented correctly such that SwiftData may evaluate the differences.
There are a couple ways you could go about doing this. Check out the “Model your schema with SwiftData” talk to understand how to use versioned schemas. For your Swift apps that rely on UIKit or AppKit, there are still viable options for you to use SwiftData.
The first approach is the coexistence solution.
You can bind your UIKit code to Core Data, and it could work in parallel with SwiftData.
Alternatively, you could treat your SwiftData classes as Swift classes, and wrap your Swift code with UIKit code instead.
That’s all from me! Now go and discover how you may flexibly migrate your Core Data application to SwiftData, either fully or incrementally. In the meantime, check out these other cool talks about SwiftData for a deeper dive.
Thanks for joining me! I’m excited to see what you will build! ♪ ♪