As far as I can tell, there’s no equivalent to Core Data’s NSFetchedResultsController in SwiftData. It would be very helpful to have a way to respond to sets of changes in a ModelContext outside of a SwiftUI view. For instance, this is very helpful when using a Model-View-ViewModel architecture.
The @Query property wrapper is great for use in a SwiftUI view, but sometimes it’s helpful to process data outside of the view itself. The fetch() method on ModelContext is helpful for one-time operations, but as far as I can't tell it doesn't address receiving changes on an ongoing basis.
Am I missing some equivalent for this use case?
Also filed as feedback FB12288916
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
Is there a way to use SwiftData without automatic iCloud sync? I’d like to do that manually using my own CloudKit solution or CKSyncEngine. SwiftData automatically picks up any CloudKit containers though and I have not seen an option to disable this behavior. Setting cloudKitContainerIdentifier to nil does still pick the first available CloudKit container.
Just in case this is a bug: FB12276416
Sample:
let configuration = ModelConfiguration(cloudKitContainerIdentifier: nil)
let modelContainer = try! ModelContainer(for: [GamesCollection.self], configuration)
print(modelContainer.configurations)
// [SwiftData.ModelConfiguration(url: …, name: …, sharedAppContainerIdentifier: …, cloudKitContainerIdentifier: Optional("iCloud.CloudKit.com.+++")
Did anyone successfully used transformable in SwiftData to store UIColor or SwiftUI Color type?
@Attribute(.transformable) var color: UIColor
In the "old" world we could define a model property as an integer (e.g. "Integer 64") and then have the actual class representing that model define the property as an enum instead and have getters/setters using methods such as primitiveValue(forKey:)
When migrating to use SwiftData I changed the model to be an actual enum type and even though the underlying type of the enum is an integer and therefore supported by the primitive storage it requires me to use Codable on the enum instead and doing so makes it incompatible with the old CoreData model.
Does anyone have any ideas or workarounds here or do you feel it is a bug and should be reported?
enum FooType: Int64 {
case awesome
case super
}
@Model
final class Note {
var type: FooType // ERROR
}
Hi, does anyone know how to get @Relationship in SwiftData to cascade to the CloudKit container? I've managed to get SwiftData classes to show up in CloudKit but they are not related in line with the @Relationship as per the example project:
@Relationship(.cascade)
var bucketListItem: [BucketListItem] = []`
They are simply separate records. As result the cascade of deletes doesn't work.
Any ideas?
I'm trying out SwiftData and converting my existing data models using Structs and Codable to Classes and SwiftData. I've got a model that has a Measurement<UnitMass> type. When I run the app after converting everything to use classes with the @Model macro, I get a fatal error: SwiftData/SchemaProperty.swift:325: Fatal error: Unexpected type for CompositeAttribute: NSUnitMass.
Since Measurement conforms to Codable, and SwiftData should work with Codable types, why is this not working?
I also tried marking the property with @Attribute(.transformable) and it didn't make a difference.
Hi,
if I have a @Model class there's always an id: PersistentIdentifier.ID underneath which, according to the current documentation "The value that uniquely identifies the associated model within the containing store.".
So I am wondering if it is (good) enough to rely on this attribute to uniquely identify @Model class entities, or if there are edge cases where it does not work (like maybe when using CloudKit)?
If anybody saw some information regarding this, please let me know :-)
Cheers,
Michael
I have an app that uses CoreData and I want to migrate to SwiftData. After following the Migrate to SwiftData session, I only need to point to my old Core Data file to read the old data and convert it to the new SwiftData format.
My question is how do I do this? Maybe worth mentioning is that my NSPersistentContainer(name: "Model") is different to my app name.
Possible Solution?
According to a Tweet by Donny Wals this is done this way:
By default a SwiftData ModelContainer will create its underlying storage in a file called default.store. If you want to change this so you can use an existing Core Data SQLite file, you can point your container to that file instead:
// point to your old sqlite file
let url = URL.applicationSupportDirectory.appending(path: "Model.sqlite")
let config = ModelConfiguration(url: url)
modelContainer = try ModelContainer(for: [
Movie.self
], config)
My Tested Code
@main
struct SwiftData_TestApp: App {
let url = URL.applicationSupportDirectory.appending(path: "Model.sqlite")
let config = ModelConfiguration(url: url)
let modelContainer = try ModelContainer(for: [
Item.self
], config)
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(modelContainer)
}
}
The problem here is that I don’t get it to work in the main app struct. When using this the way described in Dive deeper into SwiftData (at 6:58) I only get the error: Cannot use instance member 'url' within property initializer; property initializers run before 'self' is available
PS: There seems to be an issue with this WWDC session method anyway – see this post.
I have a WidgetExtension using SwiftData (same ModelContainer setup used in the WatchApp:
@main
struct Watch_Widget: Widget {
let kind: String = "Watch_Widget"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
Watch_WidgetEntryView(entry: entry)
.modelContainer(for: [Model1.self, Model2.self])
}
.configurationDisplayName("App")
.description("descriptions")
}
}
Here's the same model used in the widget view:
struct Watch_WidgetEntryView : View {
@Query var models: [Model1]
var body: some View {
let gaugeVal = models.count
Gauge(value: gaugeVal,
in: 0...60) {
Text("min")
} currentValueLabel: {
Text(String(Int(gaugeVal)))
}
.gaugeStyle(.accessoryCircular)
.widgetLabel {
Text("\(models.count) models")
}
.containerBackground(.fill.tertiary, for: .widget)
}
}
When I build the WidgetExtension, the following error was thrown and the CK data isn't loaded properly:
CloudKit setup failed because there is another instance of this persistent store actively syncing with CloudKit in this process.
when I run my project, I get this error and the simulator open app and show blank page.
I don't understand because I have to add some stores after I entry the app. but now I cannot entry it.
I used VersionedSchema and SchemaMigrationPlan
I am trying to run a lightweight migration in which I am changing the name of a model property from name to title. The database is already populated with few records. Those records must be preserved.
Here is my schema versions:
enum TripsSchemaV1: VersionedSchema {
static var versionIdentifier: String? = "Initial version"
static var models: [any PersistentModel.Type] {
[Trip.self]
}
@Model
class Trip {
var name: String
init(name: String) {
self.name = name
}
}
}
enum TripsSchemaV2: VersionedSchema {
static var versionIdentifier: String? = "name changed to title"
static var models: [any PersistentModel.Type] {
[Trip.self]
}
@Model
class Trip {
@Attribute(originalName: "name") var title: String
init(title: String) {
self.title = title
}
}
}
Migration plan:
enum TripsMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[TripsSchemaV1.self, TripsSchemaV2.self]
}
static var stages: [MigrationStage] {
[migrateV1toV2]
}
static let migrateV1toV2 = MigrationStage.lightweight(fromVersion: TripsSchemaV1.self, toVersion: TripsSchemaV2.self)
}
And finally the usage:
@main
struct TripsApp: App {
let container: ModelContainer
init() {
do {
container = try ModelContainer(for: [Trip.self], migrationPlan: TripsMigrationPlan.self, ModelConfiguration(for: [Trip.self]))
} catch {
fatalError("Could not initialize the container.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(container)
}
}
}
When I run the app, all my data for the Trips is gone and I get the following message on the output window.
Unresolved error loading container Error Domain=NSCocoaErrorDomain Code=134504 "Cannot use staged migration with an unknown coordinator model version." UserInfo={NSLocalizedDescription=Cannot use staged migration with an unknown coordinator model version.}
Any ideas?
According the WWDC23 presentation “What’s new in Core Data”, “Composite attributes may be nested within each other”.
However, in the current macOS and iOS beta builds, only single level composite attributes (without nested composite attributes) are persisted in the SQLite database.
Is this still work in progress?
See example project in FB12552092.
I've been trying to build an example of NSStagedMigrationManager from some Core Data migration tests to replace a custom migration manager solution I'd constructed, without much success.
The Core Data model has seven model versions. Most support lightweight migration, but two of the migrations in the middle of the sequence used NSMappingModel.
In the first beta, just attempting to construct an NSStagedMigrationManager from the series of stages failed with an unrecognized selector. That no longer happens in b4, but I now get an error that "Duplicate version checksums across stages detected."
If I restrict myself to just the first three versions of the model (that only require lightweight migration), I can build the migration manager. But if I attempt to use it to migrate a persistent store, it fails somewhere in NSPersistentStoreCoordinator with a nilError.
The documentation is almost nonexistent for this process, and the WWDC session that introduced it isn't much more than a breezy overview. So maybe I'm holding it wrong?
(And, yes: FB12339663)
In the application iCloud integration but in the container, it displays the name of the bundle that owns the container.
Configuration iCloud capability from developer account
Enable iCloud capability from Xcode
Added keys and value into info.plist file as below
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.com.example.applepaydemo</key>
<dict>
<key>NSUbiquitousContainerName</key>
<string>Apple Demo</string>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
Issue: It displays applepaydemo name of container not Apple Demo in iCloud Manage Account Storage
I understand by default SwiftData uses SQLite database but it can be configured to persist data in XML, binary and even in-memory databases. Does anybody know where SwiftData the SQLite database is located?
Hi,
has anybody managed to get two sqlite stores working? If I define the stores with a configuration for each it seems like that only the first configuration and and therefore the store is recognised.
This is how I define the configuration and container:
import SwiftData
@main
struct SwiftDataTestApp: App {
var modelContainer: ModelContainer
init() {
let fullSchema = Schema([
SetModel.self,
NewsModel.self
])
let setConfiguration = ModelConfiguration(
"setconfig",
schema: Schema([SetModel.self]),
url: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Sets.sqlite"),
readOnly: false)
let newsConfiguration = ModelConfiguration(
"newsconfig",
schema: Schema([NewsModel.self]),
url: FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("News.sqlite"),
readOnly: false)
modelContainer = try! ModelContainer(for: fullSchema, configurations: [setConfiguration,newsConfiguration])
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(modelContainer)
}
}
ContentView is just a basic TabView with a tab for news and a tab for sets.
If I run the program this way the sets tab is shown correctly but switching to News fails. If I change the order of the configurations and write the one for news first like this:
modelContainer = try! ModelContainer(for: fullSchema, configurations: [newsConfiguration, setConfiguration])
then the news tab is shown correctly and switching to sets tab fails.
NewsModel and SetModel only differ in the class name
Import Foundation
import SwiftData
@Model
public class NewsModel{
public var name: String
init(name: String) {
self.name = name
}
}
Also the tab content differs only for referencing the respecting model and the name:
import SwiftData
struct NewsTab: View {
@Query private var news: [NewsModel]
@Environment(\.modelContext) private var modelContext
var body: some View {
ScrollView{
LazyVStack{
ForEach(news){actNews in
Text("Hello, News \(actNews.name)")
}
}
.onAppear {
let news = NewsModel(name: "News from \(Date())")
modelContext.insert(news)
try! modelContext.save()
}
}
}
}
The error message is "NSFetchRequest could not locate an NSEntityDescription for entity name 'NewsModel'" (and SetsModel respectively when change the order of the configuration)
Do I explicitly need to tell the modelContext which configuration it should use or is this done automatically?
I'm a little lost here and hope someone can help me.
Best regards,
Sven
It's been frustrating to solve this error. My iOS device and Xcode are fully updated. I can easily run app on simulator, but issue happens on my iPhone.
dyld[23479]: Symbol not found: _$s9SwiftData12ModelContextC6insert6objectyx_tAA010PersistentC0RzlFTj
Referenced from: <6FC773BB-E68B-35A9-B334-3FFC8B951A4E> Expected in: /System/Library/Frameworks/SwiftData.framework/SwiftData
.modelContainer(for: MyMode.self, isUndoEnabled: true)
This may work for single model containers, but I have a number of models. I don't see how to enable undo for multiple model containers.
I converted PhotosPickerItems into Data via loadTransferrable, and stored those into an array of data in @Model class, But whenever I try to retrieve it, processing time is very high. Has anybody come up wit an easy way to do this?
In Core Data, I can merge two predicates (perform an OR operation) using the following code:
let compound3 = NSCompoundPredicate(type: .or, subpredicates: [compound1, compound2])
How can I achieve a similar operation in SwiftData with new Predicate?