iCloud & Data

RSS for tag

Learn how to integrate your app with iCloud and data frameworks for effective data storage

CloudKit Documentation

Post

Replies

Boosts

Views

Activity

SwiftData custom migration crash
Starting point I have an app that is in production that has a single entity called CDShift. This is the class: @Model final class CDShift { var identifier: UUID = UUID() var date: Date = Date() ... } This is how this model is written in the current version. Where I need to go Now, I'm updating the app and I have to do some modifications, that are: add a new entity, called DayPlan add the relationship between DayPlan and CDShift What I did is this: enum SchemaV1: VersionedSchema { static var versionIdentifier = Schema.Version(1, 0, 0) static var models: [any PersistentModel.Type] { [CDShift.self] } @Model final class CDShift { var identifier: UUID = UUID() var date: Date = Date() } } To encapsulate the current CDShift in a version 1 of the schema. Then I created the version 2: enum SchemaV2: VersionedSchema { static var versionIdentifier = Schema.Version(2, 0, 0) static var models: [any PersistentModel.Type] { [CDShift.self, DayPlan.self] } @Model final class DayPlan { var identifier: UUID = UUID() var date: Date = Date() @Relationship(inverse: \CDShift.dayPlan) var shifts: [CDShift]? = [] } @Model final class CDShift { var identifier: UUID = UUID() var date: Date = Date() var dayPlan: DayPlan? = nil } } The migration plan Finally, I created the migration plan: enum MigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [SchemaV1.self, SchemaV2.self] } static let migrateV1toV2 = MigrationStage.custom( fromVersion: SchemaV1.self, toVersion: SchemaV2.self) { context in // willMigrate, only access to old models } didMigrate: { context in // didMigrate, only access to new models let shifts = try context.fetch(FetchDescriptor<SchemaV2.CDShift>()) for shift in shifts { let dayPlan = DayPlan(date: shift.date) dayPlan.shifts?.append(shift) context.insert(dayPlan) } } static var stages: [MigrationStage] { print("MigrationPlan | stages called") return [migrateV1toV2] } } The ModelContainer Last, but not least, how the model container is created in the App: struct MyApp: App { private let container: ModelContainer init() { container = ModelContainer.appContainer } var body: some Scene { WindowGroup { ... } .modelContainer(container) } } This is the extension of ModelContainer: extension ModelContainer { static var appContainer: ModelContainer { let schema = Schema([ CDShift.self, DayPlan.self ]) let modelConfiguration = ModelConfiguration( schema: schema, isStoredInMemoryOnly: Ecosystem.current.isPreview, groupContainer: .identifier(Ecosystem.current.appGroupIdentifier) ) do { // let container = try ModelContainer(for: schema, configurations: modelConfiguration) let container = try ModelContainer(for: schema, migrationPlan: MigrationPlan.self, configurations: modelConfiguration) AMLogger.verbose("SwiftData path: \(modelConfiguration.url.path)") return container } catch (let error) { fatalError("Could not create ModelContainer: \(error)") } } } The error This has always worked perfectly until the migration. It crashes on the fatalError line, this is the error: Unable to find a configuration named 'default' in the specified managed object model. Notes It seems that the version of the store is never updated to 2, but it keeps staying on 1. I tried also using the lightweight migration, no crash, it seems it recognizes the new entity, but the store version is always 1. iCloud is enabled I thought that the context used in the custom migration blocks is not the "right" one that I use when I create my container If I use the lightweight migration, everything seems to work fine, but I have to manually do the association between the DayPlan and the CDShift objects Do you have an idea on how to help in this case?
5
0
246
1w
Saving SwiftData in Background Does Not Update @Query
I'm trying to use SwiftData for a new app after ~20 years of Core Data (and EOF before that). So while I'm new to SwiftData, I'm not new to Apple persistence frameworks. I've got a pretty typical workflow - need to load some JSON from the network, convert that into model objects. I've created an actor using the @ModelActor macro and I'm using that to do the network fetch and insert. I can set breakpoints in this code and see that it does indeed run and it's running on a non-main queue. That all seems fine. The problem is that my @Query powered variable in my SwiftUI user interface does not get updated when this data is loaded and saved as I would expect it to. I'm passing in the container to the actor using modelContext.container from the same modelContext that is powering the view / in the environment. My understanding was that like Core Data before it, the SwiftData framework was listening for the relevant notifications passed by the container/context, processing those and updating the UI but I can only see my data if I quit and relaunch the app. This seems like it should be a very common use case but I've not found much online. Not sure if that means I'm just doing something fundamentally wrong or what. Tested on both iOS 18 and 17 with the same results. Anyone else doing this successfully? What could I be doing wrong?
1
4
224
1w
How to save to specific stores/configurations in Core Data
I'm currently syncing core data with the CloudKit private and public databases, as you can see in the code below, I'm saving the private database in the default configuration in Core Data and the public in a configuration called Public everything works fine when NSPersistentCloudKitContainer syncs, what I'm having an issue with is trying to save to the public data store PublicStore, for instance when I try to save with func createIconImage(imageName: String) it saves the image to the "default" store, not the PublicStore(Public configuration). What could I do to make the createIconImage() function save to the PublicStore sqlite database? class CoreDataManager: ObservableObject{ static let instance = CoreDataManager() private let queue = DispatchQueue(label: "CoreDataManagerQueue") @AppStorage(UserDefaults.Keys.iCloudSyncKey) private var iCloudSync = false lazy var context: NSManagedObjectContext = { return container.viewContext }() lazy var container: NSPersistentContainer = { return setupContainer() }() init(inMemory: Bool = false){ if inMemory { container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null") } } func updateCloudKitContainer() { queue.sync { container = setupContainer() } } private func getDocumentsDirectory() -> URL { return FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] } private func getStoreURL(for storeName: String) -> URL { return getDocumentsDirectory().appendingPathComponent("\(storeName).sqlite") } func setupContainer()->NSPersistentContainer{ let container = NSPersistentCloudKitContainer(name: "CoreDataContainer") let cloudKitContainerIdentifier = "iCloud.com.example.MyAppName" guard let description = container.persistentStoreDescriptions.first else{ fatalError("###\(#function): Failed to retrieve a persistent store description.") } description.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) description.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) if iCloudSync{ if description.cloudKitContainerOptions == nil { let options = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier) description.cloudKitContainerOptions = options } }else{ print("Turning iCloud Sync OFF... ") description.cloudKitContainerOptions = nil } // Setup public database let publicDescription = NSPersistentStoreDescription(url: getStoreURL(for: "PublicStore")) publicDescription.configuration = "Public" // this is the configuration name if publicDescription.cloudKitContainerOptions == nil { let publicOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: cloudKitContainerIdentifier) publicOptions.databaseScope = .public publicDescription.cloudKitContainerOptions = publicOptions } container.persistentStoreDescriptions.append(publicDescription) container.loadPersistentStores { (description, error) in if let error = error{ print("Error loading Core Data. \(error)") } } container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy return container } func save(){ do{ try context.save() //print("Saved successfully!") }catch let error{ print("Error saving Core Data. \(error.localizedDescription)") } } } class PublicViewModel: ObservableObject { let manager: CoreDataManager @Published var publicIcons: [PublicServiceIconImage] = [] init(coreDataManager: CoreDataManager = .instance) { self.manager = coreDataManager } func createIconImage(imageName: String) { let newImage = PublicServiceIconImage(context: manager.context) newImage.imageName = imageName newImage.id = UUID() save() } func save() { self.manager.save() } }
1
0
168
1w
When to use structs with swift data?
I am unsure the correct way to model my data. I have a swift data object and then some referenced objects. Is there ever a reason those should just be structs or should they always be swift data objects themselves? for example right now this is what I'm doing: @Model final class Exam { var timestamp: Date @Attribute(.unique) var examID: UUID var title: String var questions: [Question] ... } and Question is struct Question: Codable, Identifiable { var id: UUID var number: Int var points: Int var prompt: String var answer: String } is there any problem with this or should I not be using a Struct for Question and instead use another Swift Data object with @Relationship ? I thought since its a simple object just using a struct would be fine, however... when I create a new Question object, it seems to create SwiftUI retain cycles with the warning === AttributeGraph: cycle detected through attribute 633984 === in the terminal for example, Button("Add Question", systemImage: "questionmark.diamond") { let newQuestion = Question(id: UUID(), number: exam.questions.count+1, points: 1, prompt: "", answer: "", type: .multipleChoice) exam.questions.append(newQuestion) } So, is it ok to mix structs with swift data objects or is it not best practice? And is this causing the SwiftUI retain cycles or are the issues unrelated?
1
0
162
2w
Adding indexes more difficult with updated cloudkit console
Can the cloudkit console team please look into managing indexes with their updated cloudkit console tool? Working with Record indexes used to be straightforward but it has now become cumbersome and unintuitive. For example, why does the tool force you to fill in some name field if adding a queryable index for recordName? Why can't you create several index types at once for a given field? It is possible to manage schemas in some other ways but for small changes the console used to be handy. It's now become a pain. Thanks!
0
0
120
2w
Writing to Production using an app not on TestFlight / AppStore
Is this possible? Here's what I'm trying: I'm making an app that reads from a CloudKit database. That's working fine. I made a second "admin" type app to update the database. But, of course, I don't intend to release the admin app to the public. So it was all working fine while testing in the development environment, but now that my public app is in TestFlight, and I have updated the necessary stuff that should allow me to write to production, but every attempt successfully writes to development, not production. I'm wondering if I submitted my admin app to TestFlight if it would work then. But that doesn't seem like a long term solution, since I think I would have to re-upload every 90 days... just doesn't seem ideal or correct. Do I HAVE to write the admin functionality in to the public app and hide it? What are better ways I could write to production other than manually through the console? Thanks everyone!
1
0
180
2w
Crash by SwiftData MigarionPlan
I am a develop beginner. Recently, my App used SwiftData's MigraitonPlan, which caused it to crash when I opened it for the first time after updating in TestFlight or the store. After clicking it a second time, I could enter the App normally, and I could confirm that all the models were the latest versions.However, when I tested it in Xcode, everything was normal without any errors. Here is my MigrationPlan code: import Foundation import SwiftData enum MeMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [MeSchemaV1.self, MeSchemaV2.self, MeSchemaV3.self, MeSchemaV4.self] } static var stages: [MigrationStage] { [migrateV1toV2, migrateV2toV3, migrateV3toV4] } //migrateV1toV2, because the type of a data field in MeSchemaV1.TodayRingData.self was modified, the historical data was deleted during the migration, and the migration work was successfully completed. static let migrateV1toV2 = MigrationStage.custom( fromVersion: MeSchemaV1.self, toVersion: MeSchemaV2.self, willMigrate: { context in try context.delete(model: MeSchemaV1.TodayRingData.self) }, didMigrate: nil ) //migrateV2toV3, because a new Model was added, it would crash at startup when TF and the official version were updated, so I tried to delete the historical data during migration, but the problem still exists. static let migrateV2toV3 = MigrationStage.custom( fromVersion: MeSchemaV2.self, toVersion: MeSchemaV3.self, willMigrate: { context in try context.delete(model: MeSchemaV2.TodayRingData.self) try context.delete(model: MeSchemaV2.HealthDataStatistics.self) try context.delete(model: MeSchemaV2.SportsDataStatistics.self) try context.delete(model: MeSchemaV2.UserSettingTypeFor.self) try context.delete(model: MeSchemaV2.TodayRingData.self) try context.delete(model: MeSchemaV2.TodayHealthData.self) try context.delete(model: MeSchemaV2.SleepDataSource.self) try context.delete(model: MeSchemaV2.WorkoutTargetData.self) try context.delete(model: MeSchemaV2.WorkoutStatisticsForTarget.self) try context.delete(model: MeSchemaV2.HealthDataList.self) }, didMigrate: nil ) //migrateV3toV4, adds some fields in MeSchemaV3.WorkoutList.self, and adds several new Models. When TF and the official version are updated, it will crash at startup. Continue to try to delete historical data during migration, but the problem still exists. static let migrateV3toV4 = MigrationStage.custom( fromVersion: MeSchemaV3.self, toVersion: MeSchemaV4.self, willMigrate: { context in do { try context.delete(model: MeSchemaV3.WorkoutList.self) try context.delete(model: MeSchemaV3.HealthDataStatistics.self) try context.delete(model: MeSchemaV3.SportsDataStatistics.self) try context.delete(model: MeSchemaV3.UserSettingTypeFor.self) try context.delete(model: MeSchemaV3.TodayRingData.self) try context.delete(model: MeSchemaV3.TodayHealthData.self) try context.delete(model: MeSchemaV3.SleepDataSource.self) try context.delete(model: MeSchemaV3.WorkoutTargetData.self) try context.delete(model: MeSchemaV3.WorkoutStatisticsForTarget.self) try context.delete(model: MeSchemaV3.HealthDataList.self) try context.delete(model: MeSchemaV3.SleepStagesData.self) try context.save() } catch { print("Migration from V3 to V4 failed with error: \(error)") throw error } }, didMigrate: nil ) }
18
0
340
2w
SwiftData crash: PersistentModel.keyPathToString(keypath:)
I'm distributing an iOS (17.4+) and visionOS (1.2+) app via TestFlight that's using SwiftData. The most common crash by far is from SwiftData when deleting models from a context using a predicate (first snippet below), but so far I've been unable to reproduce it myself locally (second snippet below). I'm using SwiftData outside of SwiftUI views, via my own wrapper, and converting between my app models and SwiftData models. Does anyone have any ideas how I could potentially narrow this issue down (or reproduce), or know of any similar issues? Thanks! — Seb do { try context.transaction { context in let predicate = #Predicate<PersistedFeed> { $0.id == id } do { try context.delete(model: PersistedFeed.self, where: predicate) } catch { // .. Omitted for brevity } } } catch { // .. Omitted for brevity } Crash: Thread 0 Crashed: 0 libswiftCore.dylib 0x000000018dd558c0 _assertionFailure(_:_:file:line:flags:) + 264 (AssertCommon.swift:144) 1 SwiftData 0x000000022f7f323c static PersistentModel.keyPathToString(keypath:) + 1496 (DataUtilities.swift:0) 2 SwiftData 0x000000022f83312c PredicateExpressions.KeyPath.convert(state:) + 492 (FetchDescriptor.swift:394) 3 SwiftData 0x000000022f834a24 protocol witness for ConvertibleExpression.convert(state:) in conformance PredicateExpressions.KeyPath<A, B> + 16 (<compiler-generated>:0) 4 SwiftData 0x000000022f830a70 PredicateExpression.convertToExpressionOrPredicate(state:) + 724 (FetchDescriptor.swift:203) 5 SwiftData 0x000000022f831874 PredicateExpression.convertToExpression(state:) + 36 (FetchDescriptor.swift:217) 6 SwiftData 0x000000022f83b6c8 PredicateExpressions.Equal.convert(state:) + 328 7 SwiftData 0x000000022f8360ec protocol witness for ConvertibleExpression.convert(state:) in conformance PredicateExpressions.Equal<A, B> + 64 (<compiler-generated>:0) 8 SwiftData 0x000000022f830a70 PredicateExpression.convertToExpressionOrPredicate(state:) + 724 (FetchDescriptor.swift:203) 9 SwiftData 0x000000022f82fd60 PredicateExpression.convertToPredicate(state:) + 28 (FetchDescriptor.swift:224) 10 SwiftData 0x000000022f82edb4 nsPredicate<A>(for:) + 956 (FetchDescriptor.swift:88) 11 SwiftData 0x000000022f807c2c ModelContext.delete<A>(model:where:includeSubclasses:) + 596 (ModelContext.swift:1846) 12 SwiftData 0x000000022f81994c dispatch thunk of ModelContext.delete<A>(model:where:includeSubclasses:) + 56
0
0
176
2w
Is SwiftData's #Unique currently broken or am I missing something?
Hi, I am inserting two models where the "unique" attribute is the same. I was under the impression, that this should result in an upsert and not two inserts of the model, but that is not the case. See the test coding below for what I am doing (it is self contained, so if you want to try it out, just copy it into a test target). The last #expect statement fails because of the two inserts. Not sure if this is a bug (Xcode 16 beta 2 on Sonoma running an iOS 18 simulator) or if I am missing something here... // MARK: - UniqueItem - @Model final class UniqueItem { #Unique<UniqueItem>([\.no]) var timestamp = Date() var title: String var changed = false var no: Int init(title: String, no: Int) { self.title = title self.no = no } } // MARK: - InsertTests - @Suite("Insert Tests", .serialized) struct InsertTests { var sharedModelContainer: ModelContainer = { let schema = Schema([ UniqueItem.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() @Test("Test unique.") @MainActor func upsertAndModify() async throws { let ctx = sharedModelContainer.mainContext try ctx.delete(model: UniqueItem.self) let item = UniqueItem(title: "Item \(1)", no: 0) ctx.insert(item) let allFD = FetchDescriptor<UniqueItem>() let count = try ctx.fetchCount(allFD) #expect(count == 1) let updatedItem = UniqueItem(title: "Item \(1)", no: 0) updatedItem.changed = true ctx.insert(updatedItem) // we should still have only 1 item because of the unique constraint let allCount = try ctx.fetchCount(allFD) #expect(allCount == 1) } }
0
1
194
2w
SwfitData Crashes after MigrationPlan Completed
There are multiple versions of VersionedSchema in my App. I used MigrationPlan to migrate data. It works well in Xcode, but in TestFlight and App Store, it always crashes when opening the App for the first time. MeMigrationPlan Code: import Foundation import SwiftData enum MeMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [MeSchemaV1.self, MeSchemaV2.self, MeSchemaV3.self, MeSchemaV4.self] } static var stages: [MigrationStage] { [migrateV1toV2, migrateV2toV3, migrateV3toV4] } //migrateV1toV2, because the type of a data field in MeSchemaV1.TodayRingData.self is modified, the historical data is deleted during migration, and the migration work is successfully completed. static let migrateV1toV2 = MigrationStage.custom( fromVersion: MeSchemaV1.self, toVersion: MeSchemaV2.self, willMigrate: { context in try context.delete(model: MeSchemaV1.TodayRingData.self) }, didMigrate: nil ) //migrateV2toV3, because a new Model was added, it would crash when starting up when TF and the official version were updated, so I tried to delete the historical data during migration, but the problem still exists. static let migrateV2toV3 = MigrationStage.custom( fromVersion: MeSchemaV2.self, toVersion: MeSchemaV3.self, willMigrate: { context in try context.delete(model: MeSchemaV2.TodayRingData.self) try context.delete(model: MeSchemaV2.HealthDataStatistics.self) try context.delete(model: MeSchemaV2.SportsDataStatistics.self) try context.delete(model: MeSchemaV2.UserSettingTypeFor.self) try context.delete(model: MeSchemaV2.TodayRingData.self) try context.delete(model: MeSchemaV2.TodayHealthData.self) try context.delete(model: MeSchemaV2.SleepDataSource.self) try context.delete(model: MeSchemaV2.WorkoutTargetData.self) try context.delete(model: MeSchemaV2.WorkoutStatisticsForTarget.self) try context.delete(model: MeSchemaV2.HealthDataList.self) }, didMigrate: nil ) //migrateV3toV4, adds some fields in MeSchemaV3.WorkoutList.self, and adds several new Models. When TF and the official version are updated, it will crash at startup. Continue to try to delete historical data during migration, but the problem still exists. static let migrateV3toV4 = MigrationStage.custom( fromVersion: MeSchemaV3.self, toVersion: MeSchemaV4.self, willMigrate: { context in do { try context.delete(model: MeSchemaV3.WorkoutList.self) try context.delete(model: MeSchemaV3.HealthDataStatistics.self) try context.delete(model: MeSchemaV3.SportsDataStatistics.self) try context.delete(model: MeSchemaV3.UserSettingTypeFor.self) try context.delete(model: MeSchemaV3.TodayRingData.self) try context.delete(model: MeSchemaV3.TodayHealthData.self) try context.delete(model: MeSchemaV3.SleepDataSource.self) try context.delete(model: MeSchemaV3.WorkoutTargetData.self) try context.delete(model: MeSchemaV3.WorkoutStatisticsForTarget.self) try context.delete(model: MeSchemaV3.HealthDataList.self) try context.delete(model: MeSchemaV3.SleepStagesData.self) try context.save() } catch { print("Migration from V3 to V4 failed with error: \(error)") throw error } }, didMigrate: nil ) }
1
0
124
2w
CloudKit user 'throttling'
Hi, I have some small amount of users who are receiving a lot of "throttling" error messages from CloudKit when they try to upload / download data from CloudKit. I can see this from the user reports as well as the CloudKit dashboard. It's erratic, unpredictable, and is causing all sorts of bad experience in my app. I would like to understand this more: what causes a particular user to 'throttle' vs others, and what can they do to avoid it? as an e.g if we are uploading 4000 records, having split them up into a 100 CKModifyRecordsOperations with 400 records each ... would that result in some operations getting 'throttled' in the middle of the upload? Would all the operations receive the 'throttled' error message, or only some operations? if I replay all the operations after the recommended timeout, could they also get a 'throttle' response? how do I reproduce something like this in the development environment? With my testing and development so far, I haven't run into such an issue myself. Would love to hear some insight and suggestions about how to handle this.
2
0
237
2w
SwiftData via CloudKit Only Syncing Upon Relaunch
Hello I'm a new developer and am learning the ropes. I have an app that I'm testing and seem to have run into a bug. The data is syncing from one device to another, however it takes closing the app on the Mac or force closing the app on iOS/iPadOS to get the app to reflect the new data. Is there specific code I code share to help solve this issue or any suggestions that someone may have? Thank you ahead of time for your assistance. import SwiftData @main struct ApplicantProcessorApp: App { var sharedModelContainer: ModelContainer = { let schema = Schema([ Applicant.self, ]) let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false) do { return try ModelContainer(for: schema, configurations: [modelConfiguration]) } catch { fatalError("Could not create ModelContainer: \(error)") } }() var body: some Scene { WindowGroup { ContentView() } .modelContainer(sharedModelContainer) } } struct ContentView: View { var body: some View { FilteredApplicantListView() } } #Preview { ContentView() .modelContainer(SampleData.shared.modelContainer) } struct FilteredApplicantListView: View { @State private var searchText = "" var body: some View { NavigationSplitView { ApplicantListView(applicantFilter: searchText) .searchable(text: $searchText, prompt: "Enter Name, Email, or Phone Number") .autocorrectionDisabled(true) } detail: { } } } import SwiftData struct ApplicantListView: View { @Environment(\.modelContext) private var modelContext @Query private var applicants: [Applicant] @State private var newApplicant: Applicant? init(applicantFilter: String = "") { // Filters } var body: some View { Group { if !applicants.isEmpty { List { ForEach(applicants) { applicant in NavigationLink { ApplicantView(applicant: applicant) } label: { HStack { VStack { HStack { Text(applicant.name) Spacer() } HStack { Text(applicant.phoneNumber) .font(.caption) Spacer() } HStack { Text(applicant.email) .font(.caption) Spacer() } HStack { Text("Expires: \(formattedDate(applicant.expirationDate))") .font(.caption) Spacer() } } if applicant.applicationStatus == ApplicationStatus.approved { Image(systemName: "checkmark.circle") .foregroundStyle(.green) .font(.title) } else if applicant.applicationStatus == ApplicationStatus.declined { Image(systemName: "xmark.circle") .foregroundStyle(.red) .font(.title) } else if applicant.applicationStatus == ApplicationStatus.inProgress { Image(systemName: "hourglass.circle") .foregroundStyle(.yellow) .font(.title) } else if applicant.applicationStatus == ApplicationStatus.waitingForApplicant { Image(systemName: "person.circle") .foregroundStyle(.yellow) .font(.title) } else { Image(systemName: "yieldsign") .foregroundStyle(.yellow) .font(.title) } } } } .onDelete(perform: deleteItems) } } else { ContentUnavailableView { Label("No Applicants", systemImage: "pencil.fill") } } } .navigationTitle("Applicants") .toolbar { ToolbarItem(placement: .navigationBarTrailing) { EditButton() } ToolbarItem { Button(action: addApplicant) { Label("Add Item", systemImage: "plus") } } } .sheet(item: $newApplicant) { applicant in NavigationStack { ApplicantView(applicant: applicant, isNew: true) } } } private func addApplicant() { withAnimation { let newItem = Applicant() modelContext.insert(newItem) newApplicant = newItem } } private func deleteItems(offsets: IndexSet) { withAnimation { for index in offsets { modelContext.delete(applicants[index]) } } } func formattedDate(_ date: Date) -> String { let dateFormatter = DateFormatter() dateFormatter.dateStyle = .short dateFormatter.timeStyle = .none return dateFormatter.string(from: date) } } import SwiftData @Model final class Applicant { var name = "" var email = "" var phoneNumber = "" var applicationDate = Date.now var expirationDate: Date { return Calendar.current.date(byAdding: .day, value: 90, to: applicationDate)! }
1
0
193
2w
Using NSFetchedResultsController with viewContext seems to have bad performance
Hello! Our team have been using a NSFetchedResultsController with viewContext for list UI. But we found some UI Hang logs, and I'm guessing that it happens because of Faulting in viewContext. The main thread call stacks in the logs look like below. 0 libsystem_kernel.dylib 0x00000001f378dbcc kevent_id + 8 1 libdispatch.dylib 0x00000001b4047b0f _dispatch_kq_poll + 227 2 libdispatch.dylib 0x00000001b40484df _dispatch_event_loop_wait_for_ownership + 435 3 libdispatch.dylib 0x00000001b4034983 __DISPATCH_WAIT_FOR_QUEUE__ + 339 4 libdispatch.dylib 0x00000001b403454b _dispatch_sync_f_slow + 147 5 CoreData 0x00000001b42859b3 _perform + 203 6 CoreData 0x00000001b4372767 -[NSManagedObjectContext+ 1275751 (_NestedContextSupport) newValuesForObjectWithID:withContext:error:] + 163 7 CoreData 0x00000001b424cf87 _PFFaultHandlerLookupRow + 275 8 CoreData 0x00000001b424cbbf _PF_FulfillDeferredFault + 191 9 CoreData 0x00000001b424cab7 _pvfk_header + 167 10 CoreData 0x00000001b424c9d7 _sharedIMPL_pvfk_core + 31 11 Works 0x000000010445517b closure #1 in static MailUIMailFactory.make(mail:needFolderName:) + 26530171 (MailUIMailFactory.swift:109) ... 0 libsystem_kernel.dylib 0x00000001dbd4254c kevent_id + 8 1 libdispatch.dylib 0x000000019b7057d3 _dispatch_kq_poll + 227 2 libdispatch.dylib 0x000000019b7061bb _dispatch_event_loop_wait_for_ownership + 435 3 libdispatch.dylib 0x000000019b6f2593 __DISPATCH_WAIT_FOR_QUEUE__ + 339 4 libdispatch.dylib 0x000000019b6f215b _dispatch_sync_f_slow + 147 5 CoreData 0x000000019b94f24b _perform + 203 6 CoreData 0x000000019ba3c41b -[NSManagedObjectContext+ 1274907 (_NestedContextSupport) newValueForRelationship:forObjectWithID:withContext:error:] + 167 7 CoreData 0x000000019b914387 -[NSFaultHandler retainedFulfillAggregateFaultForObject:andRelationship:withContext:] + 391 8 CoreData 0x000000019b9118bf -[_NSFaultingMutableSet willReadWithContents:] + 391 9 CoreData 0x000000019b91e663 -[_NSFaultingMutableSet count] + 23 10 Foundation 0x0000000192dbeb3f -[NSSet+ 7609151 (NSKeyValueSorting) sortedArrayUsingDescriptors:] + 55 11 Works 0x000000010249ec1f specialized static MailUIMailFactory.make(mail:needFolderName:) + 27470879 (MailUIMailFactory.swift:102) ... We just use NSFetchedResultsController's fetchedObjects in table view's cellForRowAt method, and make a new model by MailUIMailFactory.make. So we're thinking that we need to initialize NSFetchedResultsController with bg Context. What you guys think? Do I avoid using viewContext in heavy UI?
0
0
155
2w
iPadOS 18 Beta and SwiftData issues
I had a series of @Model classes with some mandatory attributes and some optional. Pre-move to 18, everything was working fine. After the migration, it reports that every single non-Optional attribute is nil upon trying to save. The error is CoreData related but not sure if its in the Core layer or Swift layer. Sample error (with app data removed) is : SwiftData.DefaultStore save failed with error: Error Domain=NSCocoaErrorDomain Code=1560 "Multiple validation errors occurred." Error Domain=NSCocoaErrorDomain Code=1570 \"%{PROPERTY}@ is a required value.\" UserInfo={NSValidationErrorObject=<NSManagedObject: 0x30388b2a0> NSLocalizedDescription=%{PROPERTY}@ is a required value., NSValidationErrorKey=systemName, NSValidationErrorValue=null}" I have modified the code to provide default values for all constructors in an attempt to see a difference, but get the same errors
0
1
221
3w
CoreData result class has 'Sendable' warnings
In an iOS viewController, I use the NSDiffableDataSource to populate a tableView from the results of a CoreData fetchController. The result class is defined by CoreData - in this case a class named CDFilterStack. In xCode 16.0 Beta with strict concurrency checking = 'Complete' the CDFilterStack class has this warning everywhere it is referenced. "Type 'CDFilterStack' does not conform to the 'Sendable' protocol; this is an error in the Swift 6 language mode." The class definition of CDFilterStack is not editable because it is generated by CoreData. I think I need to mark this class (and other similar classes) as @preconcurrency... but how & where? Here's one method sample that generates three of these warnings func initialSnapShot() -> NSDiffableDataSourceSnapshot<Int, CDFilterStack> { var snapshot = NSDiffableDataSourceSnapshot<Int, CDFilterStack>() if let sections = dataProvider.fetchedResultsController.sections { for index in 0..<sections.count { let thisSection = sections[index] guard let sectionStacks = thisSection.objects as? [CDFilterStack] else { continue} snapshot.appendSections([index]) snapshot.appendItems(sectionStacks) } // for loop that will continue on error in objects } return snapshot } Careful reading of the various migration guides hasn't produced an example of how to handle this.. (btw the migration guides are nicely done:)
1
0
223
3w
Social network app & posting content via a server
Hello, I have had an app that I created myself with Xcode for a long time and I also had it briefly for about two months as a beta in the App Store. At some point I had to take them out because I couldn't solve the problem with a server. I wanted to have a server via iCloud, Kit and Fire Base to upload and display content such as text images, videos. It should be an app that is very similar or the same as TikTok and Instagram just more data protection more 3-D visual design just a modern design and much better built than TikTok Instagram. Unfortunately, I didn't get it or almost Fire Base did it but the problem is that he always says yes this picture or this video or other error message were displayed, but among other things this error message was displayed. Hold this content can't be found in the server or something like that maybe someone here knows what she can do or knows about Fire Base maybe I did something wrong but actually I really taped everything at Fire Base and checked and switched everything on but nothing worked. I hope you can help me.
0
0
186
3w
SwiftData Update Item View from Background Thread
I have a background thread that is updating a swift data model Item using a ModelActor. The background thread runs processing an Item and updates the Item's status field. I notice that if I have a view like struct ItemListView: View { @Query private var items: [Items] var body: some View { VStack { ForEach(items) { item in ItemDetailView(item) } } } } struct ItemDetailView: View { var item: Item var body: some View { // expected: item.status automatically updates when the background thread updates the `Item`'s `status`. Text(item.status) // actual: This text never changes } } Then background updates to the Item's status in SwiftData does not reflect in the ItemDetailView. However, if I inline ItemDetailView in ItemListView like this: struct ItemListView: View { @Query private var items: [Items] var body: some View { VStack { ForEach(items) { item in // Put the contents of ItemDetailView directly in ItemListView Text(item.status) // result: item.status correctly updates when the background thread updates the item. } } } } Then the item's status text updates in the UI as expected. I suspect ItemDetailView does not properly update the UI because it just takes an Item as an input. ItemDetailView would need additional understanding of SwiftData, such as a ModelContext. Is there a way I can use ItemDetailView to show the Item's status and have the UI show the status as updated in the background thread? In case details about my background thread helps solve the problem, my thread is invoked from another view's controller like @Observable class ItemCreateController { func queueProcessingTask() { Task { let itemActor = ItemActor(modelContainer: modelContainer) await itemActor.setItem(item) await itemActor.process() } } } @ModelActor actor ItemActor { var item: Item? func setItem(_ item: Item) { self.item = modelContext.model(for: item.id) as? Item } func process() async { // task that runs processing on the Item and updates the Item's status as it goes. }
4
1
310
3w
I'm using CloudKit+CoreData to sync ... looking for faster alternative channel when multiple computers are editing at same time
I am using CloudKit+CoreData to store and sync and share my apps data. In my somewhat limited testing this is working quite well, but sync performance is unpredictable. Often it is very fast, sometimes it just stops until I put Mac app in background or restart. I guess from this thread this behavior is by design: https://developer.apple.com/forums/thread/756315 I'm accepting that! :) My plan is that I will use CloudKit+CoreData for source of truth syncing, but now I'm looking for an alternative channel that I can use to sync when multiple devices are working on this data at the same time. I think the basic design could be: When a device starts editing a CKRecord Post device IP and record ID to well known location/discovery service Watch that location to see other devices that are editing that record Make direct connection to those devices and sync through that connection (still also saving/merging with iCloud). I think I know how to solve the data merge problems that will show up in this scenario, but I don't know what technologies I should use to create the sync channel. I'm looking for ideas. I don't want to run my own server. SharePlay session seems almost perfect, but I'm not sure if it's really intended for this purpose. In particular I would want the session to start automatically (using participants from CKShare) without users having to manually join. Also I would like it to work with a single account (when I am viewing same data on my Mac and iOS device). My other thought is that I would store active users+ip's in the synced CloudKit+CoreData store and then use Network.framework to connect and sync those active users. I think this could work, but is quite low level and might be a lot of work. Are there other options that I'm missing or things I should think about? Thanks, Jesse
0
0
141
3w
SwiftData FatalError Unknown Relationship Key
Some of our users on iOS 17.5+ started to encounter crash due to: SwiftData/BackingData.swift:669: Fatal error: Unknown Relationship Key - subscription) We have tried using SwiftData on the MainActor only but an issues still effects some of our users. Our models look like these: @Model public final class ProfileModel { public static let fetchDescriptor = FetchDescriptor<ProfileModel>.self public enum Gender: Codable { case female case male case other } public enum Units: Codable { case imperial case metric } public enum Eating: Codable { case empath case enthusiast case explorer case guardian case harmonizer case pacifier case regulator case stoic case iosDefault } public enum RegistrationSource: Codable { case iOS case web } public enum NHE: Codable { case combined case emotional case mindless } public enum Goal: Codable { case beHealthier case energyIncrease case healthyHabits case looseWeight case relationshipsWithFood } public enum WeightLossFocus: Codable { case activity case healthyHabits case nutrition case other } @Attribute(.unique) public let id: String public let isPaid: Bool public let eating: Eating public let registrationSource: RegistrationSource public let nhe: NHE? public let firstName: String? public let lastName: String? public let gender: Gender? public let height: String? public let age: Int? public let weight: String? public let targetWeight: String? public let bmi: String? public let goal: Goal? public let units: Units? public let weightLossFocus: WeightLossFocus? @Relationship(deleteRule: .cascade) public var subscription: ProfileSubscriptionModel? public init( id: String, isPaid: Bool, eating: Eating, registrationSource: RegistrationSource, nhe: NHE?, firstName: String?, lastName: String?, gender: Gender?, height: String?, age: Int?, weight: String?, targetWeight: String?, bmi: String?, goal: Goal?, units: Units?, weightLossFocus: WeightLossFocus?, subscription: ProfileSubscriptionModel? ) { self.id = id self.isPaid = isPaid self.eating = eating self.registrationSource = registrationSource self.nhe = nhe self.firstName = firstName self.lastName = lastName self.gender = gender self.height = height self.age = age self.weight = weight self.targetWeight = targetWeight self.bmi = bmi self.goal = goal self.units = units self.weightLossFocus = weightLossFocus self.subscription = subscription } } @Model public final class ProfileSubscriptionModel { public static let fetchDescriptor = FetchDescriptor<ProfileSubscriptionModel>.self public let price: Double public let currency: String public let period: Int public let status: String public let expirationDate: Date public let cancelledAt: Date? public init(price: Double, currency: String, period: Int, status: String, expirationDate: Date, cancelledAt: Date?) { self.price = price self.currency = currency self.period = period self.status = status self.expirationDate = expirationDate self.cancelledAt = cancelledAt } }
0
0
214
3w