iCloud & Data

RSS for tag

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

CloudKit Documentation

Posts under iCloud & Data subtopic

Post

Replies

Boosts

Views

Activity

Core Data and Swift 6 concurrency: returning an NSManagedObject
We're in the process of migrating our app to the Swift 6 language mode. I have hit a road block that I cannot wrap my head around, and it concerns Core Data and how we work with NSManagedObject instances. Greatly simplied, our Core Data stack looks like this: class CoreDataStack { private let persistentContainer: NSPersistentContainer var viewContext: NSManagedObjectContext { persistentContainer.viewContext } } For accessing the database, we provide Controller classes such as e.g. class PersonController { private let coreDataStack: CoreDataStack func fetchPerson(byName name: String) async throws -> Person? { try await coreDataStack.viewContext.perform { let fetchRequest = NSFetchRequest<Person>() fetchRequest.predicate = NSPredicate(format: "name == %@", name) return try fetchRequest.execute().first } } } Our view controllers use such controllers to fetch objects and populate their UI with it: class MyViewController: UIViewController { private let chatController: PersonController private let ageLabel: UILabel func populateAgeLabel(name: String) { Task { let person = try? await chatController.fetchPerson(byName: name) ageLabel.text = "\(person?.age ?? 0)" } } } This works very well, and there are no concurrency problems since the managed objects are fetched from the view context and accessed only in the main thread. When turning on Swift 6 language mode, however, the compiler complains about the line calling the controller method: Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'fetchPerson(byName:)' Ok, fair enough, NSManagedObject is not Sendable. No biggie, just add @MainActor to the controller method, so it can be called from view controllers which are also main actor. However, now the compiler shows the same error at the controller method calling viewContext.perform: Non-sendable result type 'Person?' cannot be sent from nonisolated context in call to instance method 'perform(schedule:_:)' And now I'm stumped. Does this mean NSManageObject instances cannot even be returned from calls to NSManagedObjectContext.perform? Ever? Even though in this case, @MainActor matches the context's actor isolation (since it's the view context)? Of course, in this simple example the controller method could just return the age directly, and more complex scenarios could return Sendable data structures that are instantiated inside the perform closure. But is that really the only legal solution? That would mean a huge refactoring challenge for our app, since we use NSManageObject instances fetched from the view context everywhere. That's what the view context is for, right? tl;dr: is it possible to return NSManagedObject instances fetched from the view context with Swift 6 strict concurrency enabled, and if so how?
0
0
97
Apr ’25
How to distinguish which operations in the file provider are during offline period
Currently tested, if the file provider goes offline (referring to calling disconnect) and deletes a file, the system will automatically trigger the deleteItems event after reconnecting (note that only after calling reconnect again will the current deleteItems logic be reached). However, for offline deletion, I would like to pass it directly without operating on the cloud. Can mounting disks determine which operations were performed offline during reboot
0
0
12
10h
SwiftData Background Fetching?
Hi, I am experiencing main thread freezes from fetching on Main Actor. Attempting to move the function to a background thread, but whenever I reference the TestModel in a nonisolated context or in another model actor, I get this warning: Main actor-isolated conformance of 'TestModel' to 'PersistentModel' cannot be used in actor-isolated context; this is an error in the Swift 6 language mode Is there a way to do this correctly? Recreation, warning on line 13: class TestModel { var property: Bool = true init() {} } struct SendableTestModel: Sendable { let property: Bool } @ModelActor actor BackgroundActor { func fetch() throws -> [SendableTestModel] { try modelContext.fetch(FetchDescriptor<TestModel>()).map { SendableTestModel(property: $0.property) } } }
1
0
132
Jul ’25
Not able to save with SwiftData. "The file “default.store” couldn’t be opened."
I get this message when trying to save my Models. CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x303034540> , I/O error for database at /var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store. SQLite error code:1, 'no such table: ZCALENDARMODEL' with userInfo of { NSFilePath = "/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store"; NSSQLiteErrorDomain = 1; } SwiftData.DefaultStore save failed with error: Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store, NSSQLiteErrorDomain=1} The App has Recipes and Calendars and the user can select a Recipe for each Calendar day. The recipe should not be referenced, it should be saved by SwiftData along with the Calendar. import SwiftUI import SwiftData enum CalendarSource: String, Codable { case created case imported } @Model class CalendarModel: Identifiable, Codable { var id: UUID = UUID() var name: String var startDate: Date var endDate: Date var recipes: [String: RecipeData] = [:] var thumbnailData: Data? var source: CalendarSource? // Computed Properties var daysBetween: Int { let days = Calendar.current.dateComponents([.day], from: startDate.midnight, to: endDate.midnight).day ?? 0 return days + 1 } var allDates: [Date] { startDate.midnight.allDates(upTo: endDate.midnight) } var thumbnailImage: Image? { if let data = thumbnailData, let uiImage = UIImage(data: data) { return Image(uiImage: uiImage) } else { return nil } } // Initializer init(name: String, startDate: Date, endDate: Date, thumbnailData: Data? = nil, source: CalendarSource? = .created) { self.name = name self.startDate = startDate self.endDate = endDate self.thumbnailData = thumbnailData self.source = source } // Convenience initializer to create a copy of an existing calendar static func copy(from calendar: CalendarModel) -> CalendarModel { let copiedCalendar = CalendarModel( name: calendar.name, startDate: calendar.startDate, endDate: calendar.endDate, thumbnailData: calendar.thumbnailData, source: calendar.source ) // Copy recipes copiedCalendar.recipes = calendar.recipes.mapValues { $0 } return copiedCalendar } // Codable Conformance private enum CodingKeys: String, CodingKey { case id, name, startDate, endDate, recipes, thumbnailData, source } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id = try container.decode(UUID.self, forKey: .id) name = try container.decode(String.self, forKey: .name) startDate = try container.decode(Date.self, forKey: .startDate) endDate = try container.decode(Date.self, forKey: .endDate) recipes = try container.decode([String: RecipeData].self, forKey: .recipes) thumbnailData = try container.decodeIfPresent(Data.self, forKey: .thumbnailData) source = try container.decodeIfPresent(CalendarSource.self, forKey: .source) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(startDate, forKey: .startDate) try container.encode(endDate, forKey: .endDate) try container.encode(recipes, forKey: .recipes) try container.encode(thumbnailData, forKey: .thumbnailData) try container.encode(source, forKey: .source) } } import SwiftUI struct RecipeData: Codable, Identifiable { var id: UUID = UUID() var name: String var ingredients: String var steps: String var thumbnailData: Data? // Computed property to convert thumbnail data to a SwiftUI Image var thumbnailImage: Image? { if let data = thumbnailData, let uiImage = UIImage(data: data) { return Image(uiImage: uiImage) } else { return nil // No image } } init(recipe: RecipeModel) { self.name = recipe.name self.ingredients = recipe.ingredients self.steps = recipe.steps self.thumbnailData = recipe.thumbnailData } } import SwiftUI import SwiftData @Model class RecipeModel: Identifiable, Codable { var id: UUID = UUID() var name: String var ingredients: String var steps: String var thumbnailData: Data? // Store the image data for the thumbnail static let fallbackSymbols = ["book.pages.fill", "carrot.fill", "fork.knife", "stove.fill"] // Computed property to convert thumbnail data to a SwiftUI Image var thumbnailImage: Image? { if let data = thumbnailData, let uiImage = UIImage(data: data) { return Image(uiImage: uiImage) } else { return nil // No image } } // MARK: - Initializer init(name: String, ingredients: String = "", steps: String = "", thumbnailData: Data? = nil) { self.name = name self.ingredients = ingredients self.steps = steps self.thumbnailData = thumbnailData } // MARK: - Copy Function func copy() -> RecipeModel { RecipeModel( name: self.name, ingredients: self.ingredients, steps: self.steps, thumbnailData: self.thumbnailData ) } // MARK: - Codable Conformance private enum CodingKeys: String, CodingKey { case id, name, ingredients, steps, thumbnailData } required init(from decoder: Decoder) throws { ... } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(id, forKey: .id) try container.encode(name, forKey: .name) try container.encode(ingredients, forKey: .ingredients) try container.encode(steps, forKey: .steps) try container.encode(thumbnailData, forKey: .thumbnailData) } }
1
0
909
Jan ’25
Best approach to prevent SwiftData .transformable migration on iOS 26.1
We have an unreleased SwiftData app for iOS18+. While we were testing I saw reports on the forum about unexpected database migrations for codable arrays on iOS26.1. I'd like to ask a couple of questions: 1- Does this issue originate from the new Xcode version, or is it specific to iOS 26.1? 2- Is it possible to change our attribute so that users on older iOS versions receive the same model, preventing a migration from being triggered when they upgrade to iOS 26.1? One of our models looks like this: struct Point: Codable, Hashable { let x: Int let y: Int } @Model class Grid { private(set) var gridId: String = "" var points: [Point] = [] var updatedAt: Date = Date() private(set) var createdAt: Date = Date() #Index<Grid>([\.gridId]) ... } I can think of some options like: // 1 @Attribute(.transformable(by: CustomJsonTransformer.self)) var points: [Point] = [] // 2 @Attribute(.externalStorage) var points: [Point] = [] // 3 var points: Data = Data() // store points as data However, I'm not sure which one to use. What would you recommend to handle this, or is there a better strategy you would suggest?
0
0
101
3w
No persistent stores error in SwiftData
I am following Apple's instruction to sync SwiftData with CloudKit. While initiating the ModelContainer, right after removing the store from Core Data, the error occurs: FAULT: NSInternalInconsistencyException: This NSPersistentStoreCoordinator has no persistent stores (unknown). It cannot perform a save operation.; (user info absent) I've tried removing default.store and its related files/folders before creating the ModelContainer with FileManager but it does not resolve the issue. Isn't it supposed to create a new store when the ModelContainer is initialized? I don't understand why this error occurs. Error disappears when I comment out the #if DEBUG block. Code: import CoreData import SwiftData import SwiftUI struct InitView: View { @Binding var modelContainer: ModelContainer? @Binding var isReady: Bool @State private var loadingDots = "" @State private var timer: Timer? var body: some View { VStack(spacing: 16) { Text("Loading\(loadingDots)") .font(.title2) .foregroundColor(.gray) } .padding() .onAppear { startAnimation() registerTransformers() let config = ModelConfiguration() let newContainer: ModelContainer 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.my-container-identifier") desc.cloudKitContainerOptions = opts // Load the store synchronously so it completes before initializing the // CloudKit schema. desc.shouldAddStoreAsynchronously = false if let mom = NSManagedObjectModel.makeManagedObjectModel(for: [Page.self]) { let container = NSPersistentCloudKitContainer(name: "Pages", 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) } } // let fileManager = FileManager.default // let sqliteURL = config.url // let urls: [URL] = [ // sqliteURL, // sqliteURL.deletingLastPathComponent().appendingPathComponent("default.store-shm"), // sqliteURL.deletingLastPathComponent().appendingPathComponent("default.store-wal"), // sqliteURL.deletingLastPathComponent().appendingPathComponent(".default_SUPPORT"), // sqliteURL.deletingLastPathComponent().appendingPathComponent("default_ckAssets") // ] // for url in urls { // try? fileManager.removeItem(at: url) // } } #endif newContainer = try ModelContainer(for: Page.self, configurations: config) // ERROR!!! } catch { fatalError(error.localizedDescription) } modelContainer = newContainer isReady = true } .onDisappear { stopAnimation() } } private func startAnimation() { timer = Timer.scheduledTimer( withTimeInterval: 0.5, repeats: true ) { _ in updateLoadingDots() } } private func stopAnimation() { timer?.invalidate() timer = nil } private func updateLoadingDots() { if loadingDots.count > 2 { loadingDots = "" } else { loadingDots += "." } } } import CoreData import SwiftData import SwiftUI @main struct MyApp: App { @State private var modelContainer: ModelContainer? @State private var isReady: Bool = false var body: some Scene { WindowGroup { if isReady, let modelContainer = modelContainer { ContentView() .modelContainer(modelContainer) } else { InitView(modelContainer: $modelContainer, isReady: $isReady) } } } }
2
0
159
May ’25
Is it possible to use an additional local ModelContainer in a document based SwiftData app?
I have a document based SwiftData app in which I would like to implement a persistent cache. For obvious reasons, I would not like to store the contents of the cache in the documents themselves, but in my app's data directory. Is a use case, in which a document based SwiftData app uses not only the ModelContainers from the currently open files, but also a ModelContainer writing a database file in the app's documents directory (for cache, settings, etc.) supported? If yes, how can you inject two different ModelContexts, one tied to the currently open file and one tied to the local database, into a SwiftUI view?
0
0
64
Apr ’25
SwiftData with CloudKit Sync Issue
I am using SwiftData with CloudKit to synchronize data across multiple devices, and I have encountered an issue: occasionally, abnormal sync behavior occurs between two devices (it does not happen 100% of the time—only some users have reported this problem). It seems as if synchronization between the two devices completely stops; no matter what operations are performed on one end, the other end shows no response. After investigating, I suspect the issue might be caused by both devices simultaneously modifying the same field, which could lead to CloudKit's logic being unable to handle such conflicts and causing the sync to stall. Are there any methods to avoid or resolve this situation? Of course, I’m not entirely sure if this is the root cause. Has anyone encountered a similar issue?
0
0
80
2w
Stopping certain data models from syncing to cloudkit
Hi all, I am using SwiftData and cloudkit and I am having an extremely persistent bug. I am building an education section on a app that's populated with lessons via a local JSON file. I don't need this lesson data to sync to cloudkit as the lessons are static, just need them imported into swiftdata so I've tried to use the modelcontainer like this: static func createSharedModelContainer() -> ModelContainer { // --- Define Model Groups --- let localOnlyModels: [any PersistentModel.Type] = [ Lesson.self, MiniLesson.self, Quiz.self, Question.self ] let cloudKitSyncModels: [any PersistentModel.Type] = [ User.self, DailyTip.self, UserSubscription.self, UserEducationProgress.self // User progress syncs ] However, what happens is that I still get Lesson and MiniLesson record types on cloudkit and for some reason as well, whenever I update the data models or delete and reinstall the app on simulator, the lessons duplicate (what seems to happen is that a set of lessons comes from the JSON file as it should), and then 1-2 seconds later, an older set of lessons gets synced from cloudkit. I can delete the old set of lessons if I just delete the lessons and mini lessons record types, but if I update the data model again, this error reccurrs. Sorry, I don't know if I managed to explain this well but essentially I just want to stop the lessons and minilessons from being uploaded to cloudkit as I think this will fix the problem. Am I doing something wrong with the code?
0
0
88
Apr ’25
NSMetadataQuery not searching subdirectories in external ubiquity container
Testing Environment: iOS 18.4.1 / macOS 15.4.1 I am working on an iOS project that aims to utilize the user's iCloud Drive documents directory to save a specific directory-based file structure. Essentially, the app would create a root directory where the user chooses in iCloud Drive, then it would populate user generated files in various levels of nested directories. I have been attempting to use NSMetadataQuery with various predicates and search scopes but haven't been able to get it to directly monitor changes to files or directories that are not in the root directory. Instead, it only monitors files or directories in the root directory, and any changes in a subdirectory are considered an update to the direct children of the root directory. Example iCloud Drive Documents (Not app's ubiquity container) User Created Root Directory (Being monitored) File A Directory A File B An insertion or deletion within Directory A would only return a notification with userInfo containing data for NSMetadataQueryUpdateChangedItemsKey relating to Directory A, and not the file or directory itself that was inserted or deleted. (Query results array also only contain the direct children.) I have tried all combinations of these search scopes and predicates with no luck: query.searchScopes = [ rootDirectoryURL, NSMetadataQueryUbiquitousDocumentsScope, NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope, ] NSPredicate(value: true) NSPredicate(format: "%K LIKE '*.md'", NSMetadataItemFSNameKey) NSPredicate(format: "%K BEGINSWITH %@", NSMetadataItemPathKey, url.path(percentEncoded: false)) I do see these warnings in the console upon starting my query: [CRIT] UNREACHABLE: failed to get container URL for com.apple.CloudDocs [ERROR] couldn't fetch remote operation IDs: NSError: Cocoa 257 "The file couldn’t be opened because you don’t have permission to view it." "Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)"" But I am not sure what to make of that, since it does act normally for finding updates in the root directory. Hopefully this isn't a limitation of the API, as the only alternative I could think of would be to have multiple queries running for each nested directory that I needed updates for.
0
0
121
May ’25
SwiftData shared across apps?
The stuff I've found by searching has confused me, so hopefully someone can help simplify it for me? I have an app (I use it for logging which books I've given away), and I could either add a bunch of things to the app, or I could have another app (possibly a CLI tool) to generate some reports I'd like.
0
0
69
May ’25
A crash occurs when fetching history when Model has preserveValueOnDeletion attribute and using inheritance
Hello, In our app, we’ve modeled our schema using inheritance introduced in iOS 26.0, and we’re implementing SwiftData History to re-fetch models only when necessary. @Model public class Transaction { @Attribute(.preserveValueOnDeletion) public var date: Date = Date() public var amount: Double = 0 public var memo: String? } @Model public final class Spending: Transaction { public var installmentIndex: Int = 1 public var installment: Int = 1 public var installmentID: UUID? } If data has been deleted from database, we need to check a date property to determine whether to re-fetch datas. To do this, we added the preserveValueOnDeletion attribute to date property so we could retrieve it from the History tombstone value. However, after adding this attribute, a crash occurs. There is a console log Could not cast value of type 'Swift.ReferenceWritableKeyPath<Shared.ModelSchemaV5.Transaction, Foundation.Date>' (0x106bf8328) to 'Swift.PartialKeyPath<Shared.ModelSchemaV5.Spending>' (0x1094f21d8). and error log attached StrictMoneyChecking-2025-11-07-105108.txt I also tried this in the recent SampleTrip app, and fetching all history after a deletion causes the same crash. Is this issue currently being worked on or under investigation?
1
0
256
Nov ’25
SwiftData: Unexpected backing data for snapshot creation
When deleting a SwiftData entity, I sometimes encounter the following error in a document based SwiftUI app: Fatal error: Unexpected backing data for snapshot creation: SwiftData._FullFutureBackingData<MyEntityClass> The deletion happens in a SwiftUI View and the code used to retrieve the entity is standard (the ModelContext is injected from the @Environment): let myEntity = modelContext.model(for: entityIdToDelete) modelContext.delete(myEntity) Unfortunately, I haven't yet managed to isolate this any further in order to come up with a reproducible PoC. Could you give me further information about what this error means?
3
0
216
Oct ’25
SwiftData and CloudKit not synching between devices
Hi, Not sure how to describe my issue best: I am using SwiftData and CloudKit to store my data. In the past, when I tested my app on different devices, the data would sync between the devices automatically. For whatever reason this has stopped now and the data no longer syncs. No matter what I do, it feels as if all the data is actually stored just locally on each device. How can I check if the data is actually stored in the cloud and what could be reasons, why its no longer synching between my devices (and yes, I am logged in with the same Apple ID on all devices). Thanks for any hint! Max
6
0
192
Oct ’25
joblinkapp's registerview problem
我正在使用 Core Data 开发一个 SwiftUI 项目。我的数据模型中有一个名为 AppleUser 的实体,具有以下属性:id (UUID)、name (String)、email (String)、password (String) 和 createdAt (Date)。所有属性都是非可选的。 我使用 Xcode 的自动生成创建了相应的 Core Data 类文件(AppleUser+CoreDataClass.swift 和 AppleUser+CoreDataProperties.swift)。我还有一个 PersistenceController,它使用模型名称 JobLinkModel 初始化 NSPersistentContainer。 当我尝试使用以下方法保存新的 AppleUser 对象时: 让用户 = AppleUser(上下文:viewContext) user.id = UUID() user.name = “用户 1” user.email = “...” user.password = “密码 1” user.createdAt = Date()【电子邮件格式正确,但已替换为“...”出于隐私原因】 尝试?viewContext.save() 我在控制台中收到以下错误:核心数据保存失败:Foundation._GenericObjCError.nilError, [:] 用户快照: [“id”: ..., “name”: “User1”, “email”: “...”, “password”: “...”, “createdAt”: ...] 所有字段都有有效值,核心数据模型似乎正确。我还尝试过: • 检查 NSPersistentContainer(name:) 中的模型名称是否与 .xcdatamodeld 文件 (JobLinkModel) 匹配 • 确保正确设置 AppleUser 实体类、模块和 Codegen(类定义、当前产品模块) • 删除重复或旧的 AppleUser 类文件 • 清理 Xcode 构建文件夹并从模拟器中删除应用程序 • 对上下文使用 @Environment(.managedObjectContext) 尽管如此,在保存新的 AppleUser 对象时,我仍然会收到 _GenericObjCError.nilError。 我想了解: 为什么即使所有字段都不是零且正确分配,核心数据也无法保存? 这可能是由于一些残留的旧类文件引起的,还是我缺少设置中的其他内容? 我应该采取哪些步骤来确保 Core Data 正确识别 AppleUser 实体并允许保存? 任何帮助或指导将不胜感激。
2
0
115
Sep ’25
SwiftUI Sheet view with @Query loses model context
I've run into a strange issue. If a sheet loads a view that has a SwiftData @Query, and there is an if statement in the view body, I get the following error when running an iOS targetted SwiftUI app under MacOS 26.1: Set a .modelContext in view's environment to use Query While the view actually ends up loading the correct data, before it does, it ends up re-creating the sqlite store (opening as /dev/null). The strange thing is that this only happens if there is an if statement in the body. The statement need not ever evaluate true, but it causes the issue. Here's an example. It's based on the default xcode new iOS project w/ SwiftData: struct ContentView: View { @State private var isShowingSheet = false var body: some View { Button(action: { isShowingSheet.toggle() }) { Text("Show Sheet") } .sheet(isPresented: $isShowingSheet, onDismiss: didDismiss) { VStack { ContentSheetView() } } } func didDismiss() { } } struct ContentSheetView: View { @Environment(\.modelContext) private var modelContext @Query public var items: [Item] @State var fault: Bool = false var body: some View { VStack { if fault { Text("Fault!") } Button(action: addItem) { Label("Add Item", systemImage: "plus") } List { ForEach(items) { item in Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard)) } } } } private func addItem() { withAnimation { let newItem = Item(timestamp: Date()) modelContext.insert(newItem) } } } It requires some data to be added to trigger, but after adding it and dismissing the sheet, opening up the sheet with trigger the Set a .modelContext in view's environment to use Query. Flipping on -com.apple.CoreData.SQLDebug 1 will show it trying to recreate the database. If you remove the if fault { Text("Fault!") } line, it goes away. It also doesn't appear to happen on iPhones or in the iPhone simulator. Explicitly passing modelContext to the ContentSheetView like ContentSheetView().modelContext(modelContext) also seems to fix it. Is this behavior expected?
2
0
173
3w
CKSyncEngine: Duplicate FetchedRecordZoneChanges & Sync Handling Questions
Hi everyone, I've recently implemented CKSyncEngine in my app, and I have two questions regarding its behavior: Duplicate FetchedRecordZoneChanges After Sending Changes: I’ve noticed that the engine sometimes receives a FetchedRecordZoneChanges event containing modifications and deletions that were just sent by the same device a few moments earlier. This event arrives after the SentRecordZoneChanges event, and both events share the same recordChangeTag, which results in double-handling the record. Is this expected behavior? I’d like to confirm if this is how CKSyncEngine works or if I might be overlooking something. Handling Initial Sync with a "Sync Screen": When a user opens the app for the first time and already has data stored in iCloud, I need to display a "Sync Screen" temporarily to prevent showing partial data or triggering abrupt, rapid UI changes. I’ve found that canceling current operations, then awaiting sendChanges() and fetchChanges() works well to ensure data is fully synced before dismissing the sync screen: displaySyncScreen = true await syncEngine.cancelOperations() try await syncEngine.sendChanges() try await syncEngine.fetchChanges() displaySyncScreen = false However, I’m unsure if canceling operations like this could lead to data loss or other issues. Is this a safe approach, or would you recommend a better strategy for handling this initial sync state?
1
0
637
Feb ’25
Issues during CloudKit record sharing
I am trying to implement record sharing in my project, but when I try to copy the link on the UICloudSharingController, the sheet closes and the link doesn't get copied. My CloudKitManager function: public func shareTeam(_ team: Team) -> AnyPublisher<CKShare, Error> { Future { [weak self] promise in guard let self = self else { promise(.failure(CloudKitError.unknown)) return } let record = team.toCKRecord() let share = CKShare(rootRecord: record) share[CKShare.SystemFieldKey.title] = "Join \(team.name)" as CKRecordValue share.publicPermission = .readWrite let operation = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: nil) operation.savePolicy = .ifServerRecordUnchanged operation.qualityOfService = .userInitiated operation.modifyRecordsResultBlock = { result in switch result { case .success: promise(.success(share)) case .failure(let error): promise(.failure(error)) } } self.privateDatabase.add(operation) } .eraseToAnyPublisher() } ViewModel function: func shareTeam() { guard let selectedTeam = selectedTeam else { return } CloudKitManager.shared.shareTeam(selectedTeam) .receive(on: DispatchQueue.main) .sink { [weak self] completion in switch completion { case .finished: break case .failure(let error): self?.didError = true self?.error = error } } receiveValue: { share in let sharePresenter = SharePresenter( share: share, container: CloudKitManager.shared.container, teamName: selectedTeam.name, rootRecord: selectedTeam.toCKRecord() ) sharePresenter.presentShareSheet() } .store(in: &cancellables) }
1
0
751
Dec ’24
SwiftData propertiesToFetch question
I have a simple model @Model final class Movie: Identifiable { #Index\<Movie\>(\[.name\]) var id = UUID() var name: String var genre: String? init(name: String, genre: String?) { self.name = name self.genre = genre } } I turned on SQL debugging by including '-com.apple.CoreData.SQLDebug 3' argument on launch. When I fetch the data using the following code, it selects 3 records initially, but then also selects each record individually even though I am not referencing any other attributes. var fetchDescriptor = FetchDescriptor\<Movie\>() fetchDescriptor.propertiesToFetch = \[.id, .name\] fetchDescriptor.fetchLimit = 3 do { print("SELECT START") movies = try modelContext.fetch(fetchDescriptor) print("SELECT END") } catch { print("Failed to load Movie model.") } I see it selecting the 3 rows initially, but then it selects each one separately. Why would it do this on the initial fetch? I was hoping to select the data that I want to display and let the system select the entire record only when I access a variable that I did not initially fetch. CoreData: annotation: fetch using NSSQLiteStatement <0x600002158af0> on entity 'Movie' with sql text 'SELECT 1, t0.Z_PK, t0.ZID, t0.ZNAME FROM ZMOVIE t0 LIMIT 3' returned 3 rows with values: ( "<NSManagedObject: 0x600002158d70> (entity: Movie; id: 0xa583c7ed484691c1 <x-coredata://71E60F4C-1A40-4DB7-8CD1-CD76B4C11949/Movie/p1>; data: <fault>)", "<NSManagedObject: 0x600002158d20> (entity: Movie; id: 0xa583c7ed482691c1 <x-coredata://71E60F4C-1A40-4DB7-8CD1-CD76B4C11949/Movie/p2>; data: <fault>)", "<NSManagedObject: 0x600002158f00> (entity: Movie; id: 0xa583c7ed480691c1 <x-coredata://71E60F4C-1A40-4DB7-8CD1-CD76B4C11949/Movie/p3>; data: <fault>)" ) CoreData: annotation: fetch using NSSQLiteStatement <0x600002154d70> on entity 'Movie' with sql text 'SELECT 0, t0.Z_PK, t0.Z_OPT, t0.ZGENRE, t0.ZID, t0.ZNAME FROM ZMOVIE t0 WHERE t0.Z_PK = ? ' returned 1 rows CoreData: annotation: with values: ( "<NSSQLRow: 0x600000c89500>{Movie 1-1-1 genre=\"Horror\" id=4C5CB4EB-95D7-4DC8-B839-D4F2D2E96ED0 name=\"A000036\" and to-manys=0x0}" ) This all happens between the SELECT START and SELECT END print statements. Why is it fulfilling the faults immediately?
2
0
356
Feb ’25