I've been trying to setup a successful migration, but it keeps failing with this error:
NSCloudKitMirroringDelegate are not reusable and should have a lifecycle tied to a given instance of NSPersistentStore.
I can't find any information about this online. I added breakpoints throughout the code in willMigrate, and it originally failed on this line:
try? context.save()
I removed that, and it still failed. After I reload the app, it doesn't run the migration again and the app loads successfully. I figured since it crashed, it would keep trying, but I guess not. Here's how my migration is setup.
enum MigrationV1ToV2: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[SchemaV1.self, SchemaV2.self]
}
static var stages: [MigrationStage] {
[stage]
}
static let stage = MigrationStage.custom(
fromVersion: SchemaV1.self,
toVersion: SchemaV2.self,
willMigrate: { context in
// Get cycles
let cycles = try? context.fetch(FetchDescriptor<SchemaV1.Cycle>())
if let cycles {
for cycle in cycles {
// Create new recurring objects based on what's in the cycle
for income in cycle.income {
let recurring = SchemaV2.Recurring(name: income.name, frequency: income.frequency, kind: .income)
recurring.addAmount(.init(date: cycle.startDate, amount: income.amount))
context.insert(recurring)
}
for expense in cycle.expenses {
let recurring = SchemaV2.Recurring(name: expense.name, frequency: expense.frequency, kind: .expense)
recurring.addAmount(.init(date: cycle.startDate, amount: expense.amount))
context.insert(recurring)
}
for savings in cycle.savings {
let recurring = SchemaV2.Recurring(name: savings.name, frequency: savings.frequency, kind: .savings)
recurring.addAmount(.init(date: cycle.startDate, amount: savings.amount))
context.insert(recurring)
}
for investment in cycle.investments {
let recurring = SchemaV2.Recurring(name: investment.name, frequency: investment.frequency, kind: .investment)
recurring.addAmount(.init(date: cycle.startDate, amount: investment.amount))
context.insert(recurring)
}
}
//try? context.save()
} else {
print("The cycles were not able to be fetched.")
}
},
didMigrate: { context in
// Get new recurring objects
let newRecurring = try? context.fetch(FetchDescriptor<SchemaV2.Recurring>())
if let newRecurring {
for recurring in newRecurring {
// Get all recurring with the same name and kind
let sameName = newRecurring.filter({ $0.name == recurring.name && $0.kind == recurring.kind })
// Add amount history to recurring object, and then remove matching
for match in sameName {
recurring.amountHistory.append(contentsOf: match.amountHistory)
context.delete(match)
}
}
//try? context.save()
} else {
print("The new recurring objects could not be fetched.")
}
}
)
}
Here's is my modelContainer in the app file. There is a fatal error occurring here that's crashing the app.
var sharedModelContainer: ModelContainer = {
let schema = Schema(versionedSchema: SchemaV2.self)
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(
for: schema,
migrationPlan: MigrationV1ToV2.self,
configurations: [modelConfiguration]
)
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
Does anyone have any suggestions for this?
EDIT:
I found this error in the console that may be relevant.
BUG IN CLIENT OF CLOUDKIT: Registering a handler for a CKScheduler activity identifier that has already been registered (com.apple.coredata.cloudkit.activity.export.8F7A1261-4324-40B4-B041-886DF36FBF0A).
CloudKit setup failed because it couldn't register a handler for the export activity. There is another instance of this persistent store actively syncing with CloudKit in this process.
And here is the fatal error
Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil)
SwiftData
RSS for tagSwiftData is an all-new framework for managing data within your apps. Models are described using regular Swift code, without the need for custom editors.
Posts under SwiftData tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Adding an Import... menu item using .commands{CommandGroup... on
DocumentGroup(
...
) {
ContentView()
}
.commands {
CommandGroup(replacing: .importExport) {
Button("Import…") {
isImporting = true
}
.keyboardShortcut("I", modifiers: .option)
.fileImporter(
isPresented: $isImporting,
allowedContentTypes: [.commaSeparatedText],
allowsMultipleSelection: false
) { result in
switch result {
case .success(let urls):
print("File import success")
ImportCSV.importCSV(url: urls, in: context) // This is the issue
case .failure(let error):
print("File import error: \(error)")
}
}
}
I could get this to work if I were not using DocumentGroup but instead programmatically creating the modelContext.
using the shared modelContext in ImportCSV is not possible since that is not a View
passing the context as shown above would work if I knew how to get the modelContext but does it even exist yet in Main?
Is this the right place to put the commands code?
Perhaps the best thing is to have a view appear on import, then used the shared modelContext. In Xcode menu File/Add Package Dependencies... opens a popup. How is that done?
struct ModelContainerSetup {
static let shared = ModelContainerSetup()
private static let containerIdentifier = "iCloud.Journal"
func setupModelContainer() -> ModelContainer {
let schema = Schema([User.self])
let modelConfiguration = ModelConfiguration(
schema: schema,
isStoredInMemoryOnly: false,
cloudKitDatabase: .private(Self.containerIdentifier)
)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}
**Expected Behavior:
**
When CloudKit storage is full, the app should continue functioning with local storage
Data should persist locally even if cloud sync fails
Sync should resume when storage becomes available
**Actual Behavior:
**
ModelContainer initialization fails completely
Local data also stops getting saved
**Environment:
**
iOS 17.0+
SwiftData
Private CloudKit database
Ideal Behaviour:
When iCloud fails, the data should still be saved locally. I do not want to have two different containers so that I can maintain data consistency.
I created 2 different schemas, and made a small change to one of them. I added a property to the model called "version". To see if the migration went through, I setup the migration plan to set version to "1.1.0" in willMigrate. In the didMigrate, I looped through the new version of Tags to check if version was set, and if not, set it. I did this incase the willMigrate didn't do what it was supposed to. The app built and ran successfully, but version was not set in the Tag I created in the app.
Here's the migration:
enum MigrationPlanV2: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[DataSchemaV1.self, DataSchemaV2.self]
}
static let stage1 = MigrationStage.custom(
fromVersion: DataSchemaV1.self,
toVersion: DataSchemaV2.self,
willMigrate: { context in
let oldTags = try? context.fetch(FetchDescriptor<DataSchemaV1.Tag>())
for old in oldTags ?? [] {
let new = Tag(name: old.name, version: "Version 1.1.0")
context.delete(old)
context.insert(new)
}
try? context.save()
},
didMigrate: { context in
let newTags = try? context.fetch(FetchDescriptor<DataSchemaV2.Tag>())
for tag in newTags ?? []{
if tag.version == nil {
tag.version = "1.1.0"
}
}
}
)
static var stages: [MigrationStage] {
[stage1]
}
}
Here's the model container:
var sharedModelContainer: ModelContainer = {
let schema = Schema(versionedSchema: DataSchemaV2.self)
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(
for: schema,
migrationPlan: MigrationPlanV2.self,
configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
I ran a similar test prior to this, and got the same result. It's like the code in my willMigrate isn't running. I also had print statements in there that I never saw printed to the console. I tried to check the CloudKit console for any information, but I'm having issues with that as well (separate post).
Anyways, how can I confirm that my migration was successful here?
Hey folks, I'm having an issue where iCloud sync is only working in the Development environment, not on Prod. I have deployed the schema to Prod through the CloudKit console, although I did it after the app went live on the AppStore. Even though the two schema are identical, iCloud sync just doesn't work on Prod.
Things I tried on the code side:
Initially I did the most basic SwiftData+CloudKit setup:
var modelContainer: ModelContainer {
let schema = Schema([Book.self, Goal.self])
let config = ModelConfiguration(isStoredInMemoryOnly: false, cloudKitDatabase: doesUserSyncToiCloud ? .automatic : .none)
var container: ModelContainer
do {
container = try ModelContainer(for: schema, configurations: config)
} catch {
fatalError()
}
return container
}
var body: some Scene {
WindowGroup {
AnimatedSplashScreen {
MainTabView()
}
}
.modelContainer(modelContainer)
}
This is enough to make iCloud sync work at the Development level. Then when I noticed the issues on Prod I did some digging and found this on the Docs (https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices):
let config = ModelConfiguration()
do {
#if DEBUG
// Use an autorelease pool to make sure Swift deallocates the persistent
// container before setting up the SwiftData stack.
try autoreleasepool {
let desc = NSPersistentStoreDescription(url: config.url)
let opts = NSPersistentCloudKitContainerOptions(containerIdentifier: "iCloud.com.example.Trips")
desc.cloudKitContainerOptions = opts
// Load the store synchronously so it completes before initializing the
// CloudKit schema.
desc.shouldAddStoreAsynchronously = false
if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [Trip.self, Accommodation.self]) {
let container = NSPersistentCloudKitContainer(name: "Trips", managedObjectModel: mom)
container.persistentStoreDescriptions = [desc]
container.loadPersistentStores {_, err in
if let err {
fatalError(err.localizedDescription)
}
}
// Initialize the CloudKit schema after the store finishes loading.
try container.initializeCloudKitSchema()
// Remove and unload the store from the persistent container.
if let store = container.persistentStoreCoordinator.persistentStores.first {
try container.persistentStoreCoordinator.remove(store)
}
}
}
#endif
modelContainer = try ModelContainer(for: Trip.self, Accommodation.self,
configurations: config)
} catch {
fatalError(error.localizedDescription)
}
I've no idea why Apple would include this CoreData setup in a SwiftData documentation, but I went ahead and adapted it to my code as well. I see now that some new "assets" were added to my Development schema, but I'm afraid to deploy these changes to Prod, since I'm not even confident that this CoreData setup is necessary in a SwiftData app.
Does anyone have any thoughts on this? Have you run into similar issues? Any help would be much appreciated; thanks!
I've realized that I need to use migration plans, but those required versioned schemas. I think I've updated mine, but I wanted to confirm if this was the proper procedure. To start, none of my models were versioned. I've since wrapped them in a VersionedSchema like this:
enum TagV1: VersionedSchema {
static var versionIdentifier: Schema.Version = .init(1, 0, 0)
static var models: [any PersistentModel.Type] {
[Tag.self]
}
@Model
final class Tag {
var id = UUID()
var name: String = ""
// Relationships
var transactions: [Transaction]? = nil
init(name: String) {
self.name = name
}
}
}
I also created a type alias to point to this.
typealias Tag = TagV1.Tag
This is what my container looks like in my app file.
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Tag.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
The application builds and run successfully. Does this mean that my models are successfully versioned now? I'm trying to avoid an error I came across in earlier testing. That occurred because none of my models were versioned and I tried to setup a migration plan
Cannot use staged migration with an unknown coordinator model version.
Is it possible to reset SwiftData to a state identical to that of a newly installed app?
I have experienced some migration issues where, when I add a new model, I need to reinstall the entire application for the ModelContainer creation to work.
Deleting all existing models does not seem to make any difference.
A potential solution I currently have, which appears to work but feels quite hacky, is as follows:
let _ = try! ModelContainer()
modelContainer = try! ModelContainer(for: Student.self, ...)
This seems to force out this error CoreData: error: Error: Persistent History (66) has to be truncated due to the following entities being removed: (...) which seems to reset SwiftData.
Any other suggestions?
I want to support Genmoji input in my SwiftUI TextField or TextEditor, but looking around, it seems there's no SwiftUI only way to do it?
If none, it's kind of disappointing that they're saying SwiftUI is the path forward, but not updating it with support for new technologies.
Going back, does this mean we can only support Genmoji through UITextField and UIViewRepresentable? or there more direct options?
Btw, I'm also using SwiftData for storage.
Let's say I have a model like this:
@Model
final class DataModel {
var firstProperty: String = ""
}
Later on I create a new property as such:
@Model
final class DataModel {
enum DataEnum {
case dataCase
}
var firstProperty: String = ""
var secondProperty: DataEnum? = .dataCase
}
My expectation is for the data that is already stored, the secondProperty would be added with a default value of .dataCase. However, it's being set to nil instead. I could have sworn it would set to the default value given to it. Has that changed, or has it always been this way? Does this require a migration plan?
I have an app that uses SwiftData with CloudKit to synchronize data across a users devices. I'm able to replicate data created on one device on another and when removing data, it is also removed on the other device. So, I know that SwiftData and CloudKit are configured correctly.
What I'd like to do though, is to ensure that if a user installs the app on an additional device, that the data is synchronized upon app start.
When testing my app on a third device, via TestFlight, there was no data in the app upon launch even though all three devices are using the same Apple account (e.g. Apple ID).
What is the best way to achieve this?
I'm trying to add Cloud Kit integration to SwiftData app (that is already in the App Store, btw).
When the app is installed on devices that are directly connected to Xcode, it works (a bit slow, but pretty well).
But when the app is distributed to Testflight internal testers, the synchronization doesn't happen at all.
So, is this situation normal and how can I test apps with iCloud integration properly?
I have ran into an issue that is illustrated by the code in the following GitHub repository.
https://github.com/dougholland/ColorTest
When a SwiftUI color originates from the ColorPicker it can be persisted correctly and renders the same as the original color. When the color originates from the MapFeature.backgroundColor, it is always rendered with the light appearance version of the color and not the dark appearance version. The readme in the GitHub repo has screenshots that show this.
Any assistance would be greatly appreciated as this is affecting an app that is in development and I'd like to resolve this before the app is released.
If this is caused by a framework bug, any possible workaround would be greatly appreciated also. I suspect it maybe a framework issue, possibly with some code related to the MapFeature.backgroundColor, because the issue does not occur when the color originates from the ColorPicker.
This is a critical bug with Document-Based Apps (SwiftData). If you download the WWDC 2023 sample code for"Building a document-based app using SwiftData" , open it in Xcode 16.1, and run it on an iOS 18+ simulator, you'll encounter a major issue. When you exit a document and reopen it, you'll find that the changes you just made were not saved.
iOS 18 has effectively rendered last year's WWDC 2023 sample code obsolete!
Has anyone managed to successfully save data in a Document-Based App using SwiftData?
My macOS app is developed using SwfitUI, SwiftData, and CloudKit. In the development environment, CloudKit works well. Locally added models can be quickly viewed in the CloudKit Console. macOS app and iOS app with the same BundleID can also synchronize data normally when developing locally. However, in the production environment, the macOS app cannot synchronize data with iCloud. But iOS app can. The models added in the production environment are only saved locally and cannot be viewed in CloudKit Console Production.
I am sure I have configured correctly, container schema changes to deploy to the Production environment. I think there may be a problem with CloudKit in macOS.
Please help troubleshoot the problem. I can provide you with any information you need.
var body: some Scene {
WindowGroup {
MainView()
.frame(minWidth: 640, minHeight: 480)
.environment(mainViewModel)
}
.modelContainer(for: [NoteRecord.self])
}
I didn't do anything special. I didn’t do anything special. I just used SwiftData hosted by CloudKit.
I'm getting a crash in SwiftData but only on one specific device (iPhone 16 pro running 18.2 22C5131e) and not on an ipad or simulator
I cant troubleshoot this crash and its quite frustrating, all I am getting is
@Query(sort: \Todo.timestamp, order: .reverse) private var todos: [Todo]
ForEach(todos.filter { !$0.completed }) { item in // <---crash
TodoListView()
}
and the error is
Thread 1: signal SIGABRT
An abort signal terminated the process. Such crashes often happen because of an uncaught exception or unrecoverable error or calling the abort() function.
and
_SwiftData_SwiftUI.Query.wrappedValue.getter : τ_0_1
-> 0x105b98b58 <+160>: ldur x8, [x29, #-0x40]
0x105b98b5c <+164>: ldur x0, [x29, #-0x38]
0x105b98b60 <+168>: ldur x1, [x29, #-0x30]
0x105b98b64 <+172>: ldur x9, [x29, #-0x20]
0x105b98b68 <+176>: stur x9, [x29, #-0x28]
0x105b98b6c <+180>: ldr x8, [x8, #0x8]
0x105b98b70 <+184>: blr x8
0x105b98b74 <+188>: ldur x0, [x29, #-0x28]
0x105b98b78 <+192>: sub sp, x29, #0x10
0x105b98b7c <+196>: ldp x29, x30, [sp, #0x10]
0x105b98b80 <+200>: ldp x20, x19, [sp], #0x20
0x105b98b84 <+204>: ret
How do I fix this?
Are SwiftData queries lazy loaded when used in conjunction with SwiftUI List?
@Query
var posts: [PostModel]
List {
ForEach(posts, id: \.id) { post in
PostView(post)
}
}
If the code above is not lazy loaded, how can we make it lazy loaded?
I have an app and widget that share access to a SwiftData container using App Groups. I have implemented a SwiftData migration plan, but I am unsure whether I should allow the widget to perform the migration (in addition to the app). I am concerned about two possible issues:
If the app and widget are run at approximately the same time (e.g. the user taps Open after doing a manual update in the App Store), then both the app and widget might try to perform the migration at the same time, which could lead to race conditions / data corruption.
If the widget is first to run but the widget gets suspended for some reasons (e.g., iOS decides it's using too many resources), then the migration might be suspended leaving the database in an corrupted state.
To me, it feels like the safest option is to only allow the app itself to perform the migration – this will ensure that the migration can only happen once in a safe state. However, this will lead to problems for the widget. For example, if the user does not open the app for several days after an automatic update, the widget will be in a broken state, since it will not be able to open the container until it has been migrated by the app.
Possible solutions I'm considering:
Allow both the app and widget to perform the migration and cross my fingers. (Ignore Issue 1 and Issue 2)
Implement some kind of UserDefaults flag that is set to true during migration, so that the app and widget will avoid performing the migration concurrently. (Solves Issue 1 but not Issue 2)
Only perform the migration in the app, and then add code to the widget to detect which container version the widget has access to, so that the widget can continue to work with a v1 container until the app eventually updates it to a v2 container. (Solves Issue 1 and Issue 2, but leads to very convoluted code – especially over time)
Things I'm unsure about:
Will iOS continue to use v1 of the widget until the app is opened for the first time, at which point v2 of the widget is installed? Or does iOS immediately update the widget to v2 on update? Does iOS immediately refresh the widget timeline on update?
Does SwiftData already have some logic to avoid migrations being performed twice, even from different threads? If so, how does it respond if one process tries to access a container while another process is performing a migration?
Does anyone have any recommendations about how to handle these possible issues? What are best practices?
Cheers!
I changed an enum value from this:
enum Kind: String, Codable, CaseIterable {
case credit
}
to this:
enum Kind: String, Codable, CaseIterable {
case credit = "Credit"
}
And now it fails to load the data. This is inside of a SwiftData model. I get why the error is occurring, but is there a way to resolve this issue without having to revert back or delete the data?
Error: dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "Cannot initialize Kind from invalid String value credit", underlyingError: nil))
Hi,
Im developing a data centric App using SwiftData, I noticed that the device I use for testing doesn't sync its data with the simulator although both have same Apple Account ? What's Im missing here ? arched is my project settings.
Kind Regards
I want to create master details relationship between patient and vitals signs so which of option codes below are better performance wise ? Option one is master - details done manually ..
option 1
@Model
class TestResult {
@Attribute(.primaryKey) var id: UUID
var patientID: UUID
Option 2
@Model
final class Vital {
var patient: Patient?