I'm working with the FamilyControls API and am running into an issue with sharing ActivityTokens between devices in the same family sharing network.
Based on this documentation, ActivityTokens are only accessible and readable by other members in the family sharing network. My app is based on the idea that if one user selects the Games category in the FamilyActivityPicker, then this token can be shared with another device in the same family-sharing network and this other device can read and display the category.
So my question is:
If a user in the network selects an activity category in the FamilyActivityPicker, can this category token be shared, read, and used by another user in the family-sharing network?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
My app has been in the App Store a few months. In that time I've added a few updates to my SwiftData schema using a MigrationPlan, and things were seemingly going ok. But then I decided to add CloudKit syncing. I needed to modify my models to be compatible. So, I added another migration stage for it, changed the properties as needed (making things optional or adding default values, etc.). In my tests, everything seemed to work smoothly updating from the previous version to the new version with CloudKit. So I released it to my users. But, that's when I started to see the crashes and error reports come in. I think I've narrowed it down to when users update from older versions of the app. I was finally able to reproduce this on my end, and Core Data is throwing an error when loading the ModelContainer saying "CloudKit integration requires that all attributes be optional, or have a default value set." Even though I did this in the latest schema. It’s like it’s trying to load CloudKit before performing the schema migration, and since it can’t, it just fails and won’t load anything. I’m kinda at a loss how to recover from this for these users other than tell them to delete their app and restart, but obviously they’ll lose their data that way. The only other idea I have is to setup some older builds on TestFlight and direct them to update to those first, then update to the newest production version and hope that solves it. Any other ideas? And what can I do to prevent this for future users who maybe reinstall the app from an older version too? There's nothing special about my code for loading the ModelContainer. Just a basic:
let container = try ModelContainer(
for: Foo.self, Bar.self,
migrationPlan: SchemaMigration.self,
configurations: ModelConfiguration(cloudKitDatabase: .automatic)
)
Hello, I’m upgrading my app from Core Data to SwiftData. Due to my old setup the Core Data store has an explicitly name like „Something.sqlite“, because it was defined via NSPersistentContainer(name: "Something") before switching to SwiftData.
Now my goal is to migrate the Core Data stack to SwiftData, while moving it to an App Group (for Widget support) as well as enable iCloud sync via CloudKit.
Working Migration without App Group & CloudKit
I’ve managed to get my migration running without migrating it to an App Group and CloudKit support like so:
@main
struct MyAppName: App {
let container: ModelContainer
init() {
// Legacy placement of the Core Data file.
let dataUrl = URL.applicationSupportDirectory.appending(path: "Something.sqlite")
do {
// Create SwiftData container with migration and custom URL pointing to legacy Core Data file
container = try ModelContainer(
for: Foo.self, Bar.self,
migrationPlan: MigrationPlan.self,
configurations: ModelConfiguration(url: dataUrl))
} catch {
fatalError("Failed to initialize model container.")
}
}
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(container)
}
}
How To Migrate to App Group & CloudKit?
I’ve already tried to use the ModelConfiguration with a name, but it seems to only look for a .store file and thus doesn’t copy over the Core Data contents.
let fullSchema = Schema([Foo.self, Bar.self])
let configuration = ModelConfiguration("Something", schema: fullSchema)
Can someone help me how to do this migration or point me into the right direction? I can’t find anything relating this kind of migration …
I'm seeing a lot of these in my logs:
PersistentIdentifier PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-swiftdata://Course/BC9CF99A-DE6A-46F1-A18D-8034255A56D8), implementation: SwiftData.PersistentIdentifierImplementation) was remapped to a temporary identifier during save: PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata:///Course/t58C849CD-D895-4773-BF53-3F63CF48935B210), implementation: SwiftData.PersistentIdentifierImplementation). This is a fatal logic error in DefaultStore
... though everything seems to work.
Does anyone know what this means in this context? Anything I can do to not have this appear?
Currently, I have an Unversioned Schema, and lightweight changes are automatically applied to the Models.
However, I'm planning to transition to a VersionedSchema, and I have a few questions:
Do I need to write all lightweight migrations in the Migration Plan? or is it automatically applied like the Unversioned Schema?
What happens if I do not create a lightweight migration? and just directly do lightweight changes to the latest VersionedSchema (example: Add a new property)?
Hey there,
I’m feeling pretty desperate at this point, as my most recent update to Xcode 16.1 and the new 18.1 simulators has basically made it impossible for me to work on my apps.
The same app and same code run fine in the 18.0 simulators with the same iCloud account logged in.
I’ve tried multiple simulators with the same results, even on different computers. I’ve also tried logging in repeatedly without any luck. The CloudKit database logs don’t show any errors or suspicious entries. Reinstalling the app on the simulator doesn't help either.
Whenever I launch the application in Xcode, I'm getting:
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _performSetupRequest:]_block_invoke(1240): <NSCloudKitMirroringDelegate: 0x600003d213b0>: Failed to set up CloudKit integration for store: <NSSQLCore: 0x103f124e0> (URL: file:///Users/kerstenbroich/Library/Developer/CoreSimulator/Devices/57BC78CE-DB2A-4AC0-9D7A-43C386305F56/data/Containers/Data/Application/EFDE9B05-0584-47C5-80AE-F2FF5994860C/Library/Application%20Support/Model.sqlite)
<CKError 0x600000d3dfe0: "Partial Failure" (2/1011); "Failed to modify some record zones"; partial errors: {
com.apple.coredata.cloudkit.zone:__defaultOwner__ = <CKError 0x600000d7c090: "Internal Error" (1/5000); "Failed user key sync">
}>
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate recoverFromError:](2310): <NSCloudKitMirroringDelegate: 0x600003d213b0> - Attempting recovery from error: <CKError 0x600000d3dfe0: "Partial Failure" (2/1011); "Failed to modify some record zones"; partial errors: {
com.apple.coredata.cloudkit.zone:__defaultOwner__ = <CKError 0x600000d7c090: "Internal Error" (1/5000); "Failed user key sync">
}>
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _recoverFromPartialError:forStore:inMonitor:]_block_invoke(2773): <NSCloudKitMirroringDelegate: 0x600003d213b0>: Found unknown error as part of a partial failure: <CKError 0x600000d7c090: "Internal Error" (1/5000); "Failed user key sync">
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _recoverFromPartialError:forStore:inMonitor:](2820): <NSCloudKitMirroringDelegate: 0x600003d213b0>: Error recovery failed because the following fatal errors were found: {
"<CKRecordZoneID: 0x600000d62340; zoneName=com.apple.coredata.cloudkit.zone, ownerName=__defaultOwner__>" = "<CKError 0x600000d7c090: \"Internal Error\" (1/5000); \"Failed user key sync\">";
}
Any help/ideas would be much appreciated, because I have no clue what to try next.
Thanks a lot!
Hello,
I recently switched my app from flutter which was using sqlite3 database. I would like to migrate my existing users to SwiftData.
The issue is I do not know where to start in finding the database for the current users, reading the data and inserting into the SwiftData schema.
I tried searching but not clear on how to proceed. Any help pointing in the right direction would be great. Thanks.
I have a Core Data app on the App Store that places its persistent store in the default location within the Application Support directory. At some point, I add widgets to the app while utilizing SwiftData. I create a widget extension and the app group to link both targets. I migrate my persistent store from the default location to the app group container directory. Now, the SwiftData code pointing to the shared container in the widget extension can query data. Here's the sample project for illustration up to this point.
Then, I decided to get rid of the Core Data code entirely. Move everything in the main target to SwiftData. I need to keep my persistence controller object, however, because I can never assume that the entirety of my user base has migrated. I do need to ditch the Core Data model file. Otherwise, my new SwiftData models would conflict with the auto-generated ones from that file. But once I do that, my migration code breaks. I can't set up a persistent container without the model. And I need the container for migration. SwiftData has no native interface to migrate the store to a new location. There is a line in the documentation stating that the framework copies the store to an app group container automatically. But I wasn't able to verify that claim.
I'm using SwiftData, and I'm using iCloud's CloudKit feature to back up my data.
The problem here is that once you start backing up your data, you can't erase it completely.
Even if the user adds 4 data and erases 4 again, I'm using about 2.5kb.
I don't know how the user using the app will accept this.
I'm trying to provide the user with the ability to erase data at once, what should I do??
I am trying to add a custom JSON DataStore and DataStoreConfiguration for SwiftData. Apple kindly provided some sample code in the WWDC24 session, "Create a custom data store with SwiftData", and (once updated for API changes since WWDC) that works fine.
However, when I try to add a relationship between two classes, it fails. Has anyone successfully made a JSONDataStore with a relationship?
Here's my code; firstly the cleaned up code from the WWDC session:
import SwiftData
final class JSONStoreConfiguration: DataStoreConfiguration {
typealias Store = JSONStore
var name: String
var schema: Schema?
var fileURL: URL
init(name: String, schema: Schema? = nil, fileURL: URL) {
self.name = name
self.schema = schema
self.fileURL = fileURL
}
static func == (lhs: JSONStoreConfiguration, rhs: JSONStoreConfiguration) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
final class JSONStore: DataStore {
typealias Configuration = JSONStoreConfiguration
typealias Snapshot = DefaultSnapshot
var configuration: JSONStoreConfiguration
var name: String
var schema: Schema
var identifier: String
init(_ configuration: JSONStoreConfiguration, migrationPlan: (any SchemaMigrationPlan.Type)?) throws {
self.configuration = configuration
self.name = configuration.name
self.schema = configuration.schema!
self.identifier = configuration.fileURL.lastPathComponent
}
func save(_ request: DataStoreSaveChangesRequest<DefaultSnapshot>) throws -> DataStoreSaveChangesResult<DefaultSnapshot> {
var remappedIdentifiers = [PersistentIdentifier: PersistentIdentifier]()
var serializedData = try read()
for snapshot in request.inserted {
let permanentIdentifier = try PersistentIdentifier.identifier(for: identifier,
entityName: snapshot.persistentIdentifier.entityName,
primaryKey: UUID())
let permanentSnapshot = snapshot.copy(persistentIdentifier: permanentIdentifier)
serializedData[permanentIdentifier] = permanentSnapshot
remappedIdentifiers[snapshot.persistentIdentifier] = permanentIdentifier
}
for snapshot in request.updated {
serializedData[snapshot.persistentIdentifier] = snapshot
}
for snapshot in request.deleted {
serializedData[snapshot.persistentIdentifier] = nil
}
try write(serializedData)
return DataStoreSaveChangesResult<DefaultSnapshot>(for: self.identifier, remappedIdentifiers: remappedIdentifiers)
}
func fetch<T>(_ request: DataStoreFetchRequest<T>) throws -> DataStoreFetchResult<T, DefaultSnapshot> where T : PersistentModel {
if request.descriptor.predicate != nil {
throw DataStoreError.preferInMemoryFilter
} else if request.descriptor.sortBy.count > 0 {
throw DataStoreError.preferInMemorySort
}
let objs = try read()
let snapshots = objs.values.map({ $0 })
return DataStoreFetchResult(descriptor: request.descriptor, fetchedSnapshots: snapshots, relatedSnapshots: objs)
}
func read() throws -> [PersistentIdentifier : DefaultSnapshot] {
if FileManager.default.fileExists(atPath: configuration.fileURL.path(percentEncoded: false)) {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let data = try decoder.decode([DefaultSnapshot].self, from: try Data(contentsOf: configuration.fileURL))
var result = [PersistentIdentifier: DefaultSnapshot]()
data.forEach { s in
result[s.persistentIdentifier] = s
}
return result
} else {
return [:]
}
}
func write(_ data: [PersistentIdentifier : DefaultSnapshot]) throws {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let jsonData = try encoder.encode(data.values.map({ $0 }))
try jsonData.write(to: configuration.fileURL)
}
}
The data model classes:
import SwiftData
@Model
class Settings {
private(set) var version = 1
@Relationship(deleteRule: .cascade) var hack: Hack? = Hack()
init() {
}
}
@Model
class Hack {
var foo = "Foo"
var bar = 42
init() {
}
}
Container:
lazy var mainContainer: ModelContainer = {
do {
let url = // URL to file
let configuration = JSONStoreConfiguration(name: "Settings", schema: Schema([Settings.self, Hack.self]), fileURL: url)
return try ModelContainer(for: Settings.self, Hack.self, configurations: configuration)
}
catch {
fatalError("Container error: \(error.localizedDescription)")
}
}()
Load function, that saves a new Settings JSON file if there isn't an existing one:
@MainActor func loadSettings() {
let mainContext = mainContainer.mainContext
let descriptor = FetchDescriptor<Settings>()
let settingsArray = try? mainContext.fetch(descriptor)
print("\(settingsArray?.count ?? 0) settings found")
if let settingsArray, let settings = settingsArray.last {
print("Loaded")
} else {
let settings = Settings()
mainContext.insert(settings)
do {
try mainContext.save()
} catch {
print("Error saving settings: \(error)")
}
}
}
The save operation creates a JSON file, which while it isn't a format I would choose, is acceptable, though I notice that the "hack" property (the relationship) doesn't have the correct identifier.
When I run the app again to load the data, I get an error (that there wasn't room to include in this post).
Even if I change Apple's code to not assign a new identifier, so the relationship property and its pointee have the same identifier, it still doesn't load.
Am I doing something obviously wrong, or are relationships not supported in custom data stores?
I've developed an app that allow me to send messages to all users by entering a record in Public database. On each users device, the app takes that Public record and creates a record in their Private database to track the Read and Deleted status.
It works flawlessly on the simulator and about 1/3 of the devices enrolled in my TestFlight. The other 2/3 are unable to write to their Private database. In fact, the CKContainer.default().privateCloudDatabase.save() operation causes the app to crash if I let it run.
I've looked at every Google link and I just can't figure out why this works for 1/3 of the users, but not the other 2/3. Incidentally, the 2/3 of the group that can't save to their Private database are also unable to save their Notification Subscription.
All devices are running iOS 18.0 or better iPhone 11 through 16 in testing pool. Each portion of the working/not working group are a mix of iPhone versions.
if !found && checkIcloudStatus() {
CKContainer.default().privateCloudDatabase.save(loadPrivateData(n: processedMessages[index].recordName)) { returnedRecord, returnedError in
print("Adding new message to Private and error is: \(String(describing: returnedError))")
DispatchQueue.main.async {
self.processedMessages[index].record = returnedRecord ?? CKRecord(recordType: "messages_private")
self.processedMessages[index].unRead = true
self.processedMessages[index].deleted = false
}
}
}
func loadPrivateData(n: String) -> CKRecord {
let record = CKRecord(recordType: "messages_private")
record["deleted"] = "false"
record["messageId"] = n
record["unRead"] = "true"
return record
}
I got a preview crash info as this:
*** crashed due to fatalError in PersistentModel.swift at line 559.
This KeyPath does not appear to relate RecordInfo to anything - \RecordInfo.<computed 0x00000001921dfb4e (Array<SomeInfo>)>
If I change preview device to iPhoneSE, It is no crash.
I think the reason is I have some dirty data in iPhone16 Pro preview device.
If I'm right and how to delete it?
Model of RecordInfo like this:
@Model
class RecordInfo {
@Relationship(deleteRule: .cascade) var someInfo:[SomeInfo]
}
@Model
class SomeInfo {
var date: Date
var info: String
}
While reading the developer documentation article Adopting SwiftData for a Core Data App, one particular line piqued my interest.
For apps that evolve from a version that doesn’t have any app group container to a version that has one, SwiftData copies the existing store to the app group container.
Given how troublesome it has been to migrate the Core Data persistent store to an app group container, I decided to try this out myself. I created an Xcode project using the default Core Data template. I then added a few Item objects with timestamps. There, I had what we would consider a regular Core Data app.
I then created a widget extension for this app since this is one of the most common uses for adopting an app group in an Xcode project. After that, I linked the main target with the widget extension using an app group. In the widget extension, I tried to fetch the Item objects. I utilized the SwiftData code in the sample project associated with the article above.
struct Provider: TimelineProvider {
private let modelContainer: ModelContainer
init() {
let appGroupContainerID = "group.com.genebogdanovich.CoreDataSwiftDataAppGroup"
guard let appGroupContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: appGroupContainerID) else {
fatalError("Shared file container could not be created.")
}
let url = appGroupContainer.appendingPathComponent("CoreDataSwiftDataAppGroup.sqlite")
print("\(url)")
do {
modelContainer = try ModelContainer(for: Item.self, configurations: ModelConfiguration(url: url))
} catch {
fatalError("Failed to create the model container: \(error)")
}
}
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> ()) {
Task { @MainActor in
let fetchDescriptor = FetchDescriptor<Item>()
let items: [Item] = try! modelContainer.mainContext.fetch(fetchDescriptor)
print(items)
let entry = SimpleEntry(date: .now, emoji: "😀", count: items.count)
let timeline = Timeline(entries: [entry], policy: .never)
completion(timeline)
}
}
The fetch yielded no results. However, as I explored the app group directory in the file system, I found a .sqlite file. That is interesting because SwiftData creates .store files by default. So, I am guessing that SwiftData did copy something. Or the ModelContainer initializer just created another empty SQLite file since the fetch returned zero results.
I would highly appreciate someone elaborating on that quote from the documentation.
Both appleIDs(create and modify/save) sign in iCloud.
I use the following code to modify and save records:
self.containerIdentifier).publicCloudDatabase
database.fetch(withRecordID: CKRecord.ID(recordName:groupID), completionHandler: { record, error in
if error == nil && record != nil {
if let iDs : [String] = record!.object(forKey: "memberIDs") as? Array {
if iDs.count < self.maxMemberCount {
if let mems: [String] = record!.object(forKey: "memberNames") as? Array {
if !(mems as NSArray).contains(name) {
var members = mems
members.append(name)
record!.setObject(members as CKRecordValue, forKey: "memberNames")
var iDs : [String] = record!.object(forKey: "memberIDs") as! Array
iDs.append(self.myMemberID)
record!.setObject(iDs as CKRecordValue, forKey:"memberIDs")
database.save(record!, completionHandler: { record, error in
if error == nil {
} else {
completion(error as NSError?)
dPrint("Error : \(String(describing: error))")
}
})
}else{
let DBError : NSError = NSError(domain: "DBError", code: 89, userInfo: ["localizedDescription": NSLocalizedString("Your nickname already used.", comment:"")])
completion(DBError)
print("change your nickname")
}
}else{
print("group DB error")
let DBError : NSError = NSError(domain: "DBError", code: 88, userInfo: ["localizedDescription": NSLocalizedString("Please try later.", comment:"")])
completion(DBError)
}
}
}else{
print("Error : \(String(describing: error))")
}
})
I received the following error message:
?Error saving records: <CKError 0x600000bbe970: "Service Unavailable" (6/NSCocoaErrorDomain:4099); "Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error."; Retry after 5.0 seconds>
baseNSError@0 NSError domain: "CKErrorDomain" - code: 6
_userInfo __NSDictionaryI * 4 key/value pairs 0x000060000349e300
[0] (null) "NSLocalizedDescription" : "Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error."
key __NSCFConstantString * "NSLocalizedDescription" 0x00000001117155a0
value __NSCFConstantString * "Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error." 0x000000011057e700
[1] (null) "CKRetryAfter" : Int32(5)
key __NSCFConstantString * "CKRetryAfter" 0x000000011057c680
value NSConstantIntegerNumber? Int32(5) 0x00000001105c2ed0
[2] (null) "CKErrorDescription" : "Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error."
key __NSCFConstantString * "CKErrorDescription" 0x0000000110568d00
value __NSCFConstantString * "Error connecting to CloudKit daemon. This could happen for many reasons, for example a daemon exit, a device reboot, a race with the connection inactivity monitor, invalid entitlements, and more. Check the logs around this time to investigate the cause of this error." 0x000000011057e700
[3] (null) "NSUnderlyingError" : domain: "NSCocoaErrorDomain" - code: 4099
key __NSCFConstantString * "NSUnderlyingError" 0x0000000111715540
value NSError? domain: "NSCocoaErrorDomain" - code: 4099 0x00006000016cc300
I'm sorta baffled right now. I am trying to wonder how I might detect a updated SQL Store in an older app.
have a baseline app, and create a SQL-based repository
in an updated app, change the model and verify that you can see the updated model version. Using lightweight migration
re-run the older app (which will inherit the newer SQL repository).
YIKES - no error when creating the NSPersistenStoreCoordinator!
Nothing in the metadata to imply the store is newer than the model:
[_persistentStoreCoordinator metadataForPersistentStore:store]
My question: is there any way to detect this condition?
David
I want to clear all the data in the zone, but I can't delete it. Below is my code, no logs are printed when running.
static func clearData() {
let coordinator = shared.container.persistentStoreCoordinator
let fm = FileManager.default
for d in shared.container.persistentStoreDescriptions {
guard let url = d.url else { continue }
do {
if fm.fileExists(atPath: url.path()) {
try coordinator.destroyPersistentStore(at: url, type: .sqlite)
}
} catch {
logger.debug("Failed to delete db file, \(error)")
}
}
for description in shared.container.persistentStoreDescriptions {
guard let originalStoreURL = description.url else { continue }
let walFileURL = originalStoreURL.deletingPathExtension().appendingPathExtension("sqlite-wal")
let shmFileURL = originalStoreURL.deletingPathExtension().appendingPathExtension("sqlite-shm")
for url in [originalStoreURL, walFileURL, shmFileURL] {
do {
if fm.fileExists(atPath: url.path()) {
try fm.removeItem(at: url)
}
} catch {
logger.debug("Failed to delete db file, \(error)")
}
}
}
let container = CKContainer(identifier: appContainerID)
container.privateCloudDatabase.fetchAllRecordZones { zones, error in
if let error = error {
print("Error fetching zones: ")
} else if let zone = zones?.first, zone.zoneID.zoneName == "_defaultZone" {
PersistenceController.shared.container.purgeObjectsAndRecordsInZone(with: zone.zoneID, in: nil) { ckShare, error in
if let error = error {
print("Error purge zones: \(error)")
}
if ckShare == nil {
print("ckShare is nil")
}
}
}
}
}
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)
I created a new index on two record types on Oct 12th. I still cannot query the records using the new queryable index on records that were created before that date. There is no indication in the schema history that the reindexing has started, completed, failed, or still in progress.
What is the expectation for new indices being applied to existing records? Well over a week seems unacceptable for a database that has maybe 5000 records across a few record types.
When I query my data using an old index and an old record field, I get hundreds of matching results so I know the data is there.
FB15554144 - CloudKit / CloudKit Console: PRODUCTION ISSUE - Query against index created two weeks ago not returning all data as expected
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?
Background
I have an established app in the App Store which has been using NSPersistentCloudkitContainer since iOS 13 without any issues.
I've been running my app normally on an iOS device running the iOS 15 betas, mainly to see problems arise before my users see them.
Ever since iOS 15 (beta 4) my app has failed to sync changes - no matter how small the change. An upload 'starts' but never completes. After a minute or so the app quits to the Home Screen and no useful information can be gleaned from crash reports. Until now I've had no idea what's going on.
Possible Bug in the API?
I've managed to replicate this behaviour on the simulator and on another device when building my app with Xcode 13 (beta 5) on iOS 15 (beta 5).
It appears that NSPersistentCloudkitContainer has a memory leak and keeps ramping up the RAM consumption (and CPU at 100%) until the operating system kills the app. No code of mine is running.
I'm not really an expert on these things and I tried to use Instruments to see if that would show me anything. It appears to be related to NSCloudkitMirroringDelegate getting 'stuck' somehow but I have no idea what to do with this information.
My Core Data database is not tiny, but not massive by any means and NSPersistentCloudkitContainer has had no problems syncing to iCloud prior to iOS 15 (beta 4).
If I restore my App Data (from an external backup file - 700MB with lots of many-many, many-one relationships, ckAssets, etc.) the data all gets added to Core Data without an issue at all. The console log (see below) then shows that a sync is created, scheduled & then started... but no data is uploaded.
At this point the memory consumption starts and all I see is 'backgroundTask' warnings appear (only related to CloudKit) with no code of mine running.
CoreData: CloudKit: CoreData+CloudKit: -[PFCloudKitExporter analyzeHistoryInStore:withManagedObjectContext:error:](501): <PFCloudKitExporter: 0x600000301450>: Exporting changes since (0): <NSPersistentHistoryToken - {
"4B90A437-3D96-4AC9-A27A-E0F633CE5D9D" = 906;
}>
CoreData: CloudKit: CoreData+CloudKit: -[PFCloudKitExportContext processAnalyzedHistoryInStore:inManagedObjectContext:error:]_block_invoke_3(251): Finished processing analyzed history with 29501 metadata objects to create, 0 deleted rows without metadata.
CoreData: CloudKit: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _scheduleAutomatedExportWithLabel:activity:completionHandler:](2800): <NSCloudKitMirroringDelegate: 0x6000015515c0> - Beginning automated export - ExportActivity:
<CKSchedulerActivity: 0x60000032c500; containerID=<CKContainerID: 0x600002ed3240; containerIdentifier=iCloud.com.nitramluap.Somnus, containerEnvironment="Sandbox">, identifier=com.apple.coredata.cloudkit.activity.export.4B90A437-3D96-4AC9-A27A-E0F633CE5D9D, priority=2, xpcActivityCriteriaOverrides={ Priority=Utility }>
CoreData: CloudKit: CoreData+CloudKit: -[NSCloudKitMirroringDelegate executeMirroringRequest:error:](765): <NSCloudKitMirroringDelegate: 0x6000015515c0>: Asked to execute request: <NSCloudKitMirroringExportRequest: 0x600002ed2a30> CBE1852D-7793-46B6-8314-A681D2038B38
2021-08-13 08:41:01.518422+1000 Somnus[11058:671570] [BackgroundTask] Background Task 68 ("CoreData: CloudKit Export"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
2021-08-13 08:41:03.519455+1000 Somnus[11058:671570] [BackgroundTask] Background Task 154 ("CoreData: CloudKit Scheduling"), was created over 30 seconds ago. In applications running in the background, this creates a risk of termination. Remember to call UIApplication.endBackgroundTask(_:) for your task in a timely manner to avoid this.
Just wondering if anyone else is having a similar issue? It never had a problem syncing an initial database restore prior to iOS 15 (beta 4) and the problems started right after installing iOS 15 (beta 4).
I've submitted this to Apple Feedback and am awaiting a response (FB9412346). If this is unfixable I'm in real trouble (and my users are going to be livid).
Thanks in advance!