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

Is History Tracking in Cloudkit shared database needed?
I’ve setup the Cloudkit persistent container with private and shared database (see code below). I’ve enabled NSPersistentHistoryTrackingKey to true also for .shared database. I’ve noticed in the example from Apple that the History Tracking is only enabled in .private but not for .shared. Questions: For a CloudKit setup to sync (a) between owners’ own devices (only private database), and (b) between multiple iCloud Users through .private and .shared databases, Do I need to enable history tracking for .shared database if I want to check the remote changes in the .shared database (or is the history tracking of the .private database of the owner also accessible in the .shared database)? ======================== let APP_BUNDLE_IDENTIFIER = Bundle.main.bundleIdentifier! let APP_GROUP_IDENTIFIER = "group." + APP_BUNDLE_IDENTIFIER private func setupPersistentContainer(_ container: NSPersistentCloudKitContainer? = nil, isStartup: Bool = true) -> NSPersistentCloudKitContainer { let container = container ?? getCloudKitContainer(name: CORE_DATA_DATA_MODEL_NAME) let defaultDirectoryURL: URL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: APP_GROUP_IDENTIFIER) ?? NSPersistentCloudKitContainer.defaultDirectoryURL() let privateDataStoreURL = defaultDirectoryURL.appendingPathComponent("PrivateDataStore.store") let sharedDataStoreURL = defaultDirectoryURL.appendingPathComponent("SharedDS.store") // MARK: Private Store configuration let privateDataStoreDescription = NSPersistentStoreDescription(url: privateDataStoreURL) privateDataStoreDescription.configuration = "PrivateDataStore" // Enable lightweight migration privateDataStoreDescription.shouldInferMappingModelAutomatically = true privateDataStoreDescription.shouldMigrateStoreAutomatically = true // Turn History Tracking privateDataStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) let logOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: CLOUDKIT_LOG_CONTAINER_ID) logOptions.databaseScope = .private privateDataStoreDescription.cloudKitContainerOptions = logOptions // turn on remote change notifications privateDataStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) container.persistentStoreDescriptions = [privateDataStoreDescription] // MARK: Share Store configuration let sharedDataStoreDescription = NSPersistentStoreDescription(url: sharedDataStoreURL) sharedDataStoreDescription.configuration = "SharedDS" // MARK: Enable lightweight migration sharedDataStoreDescription.shouldInferMappingModelAutomatically = true sharedDataStoreDescription.shouldMigrateStoreAutomatically = true sharedDataStoreDescription.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey) let sharedOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: CLOUDKIT_LOG_CONTAINER_ID) sharedOptions.databaseScope = .shared sharedDataStoreDescription.cloudKitContainerOptions = sharedOptions // turn on remote change notifications sharedDataStoreDescription.setOption(true as NSNumber, forKey: NSPersistentStoreRemoteChangeNotificationPostOptionKey) container.persistentStoreDescriptions.append(sharedDataStoreDescription) self.stores = [StoreType : NSPersistentStore]() container.loadPersistentStores(completionHandler: { [self] (storeDescription, error) in if let error = error as NSError? { print(error) } if let cloudKitContainerOptions = storeDescription.cloudKitContainerOptions { if cloudKitContainerOptions.databaseScope == .private { self.stores[.privateStore] = container.persistentStoreCoordinator.persistentStore(for: storeDescription.url ?? privateDataStoreURL) } else if cloudKitContainerOptions.databaseScope == .shared { self.stores[.sharedStore] = container.persistentStoreCoordinator.persistentStore(for: storeDescription.url ?? sharedDataStoreURL) } } else { self.stores[.privateStore] = container.persistentStoreCoordinator.persistentStore(for: storeDescription.url ?? privateDataStoreURL) } }) /// Automatically merge changes in background context into View Context /// Since we always use background context to save and viewContext to read only. The store values should always trump container.viewContext.automaticallyMergesChangesFromParent = true container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy // Create separate context for read and write container.viewContext.name = VIEW_CONTEXT_NAME container.viewContext.transactionAuthor = self.contextAuthor self.setQueryGeneration(context: container.viewContext, from: .current) return container }
1
0
80
3d
How to safely create root and branch objects in a custom zone?
I encountered issues with some branch objects being assigned to multiple zones error (on iOS 17.5.1 and above). I get the errors when calling persistentContainer.shareshare(:to:completion:) and persistentContainer.persistUpdatedShare(:in:completion:). "The operation couldn't be completed. Request '89D3F62D-548D-4816-9F1B-594390BD8F70' was aborted because the mirroring delegate never successfully initialized due to error: Error Domain=NSCocoaErrorDomain Code=134060 "A Core Data error occurred." UserInfo={NSLocalizedFailureReason=Object graph corruption detected. Objects related to 'Oxa2255fdc1fa980c5 x-coredata://CB800FA2-6054-4D91-8EBC-E9E31890344F/CDChildObject/p588' are assigned to multiple zones: {l <CKRecordZonelD: 0x3026a1170; zoneName=com.apple.coredata.cloud-kit.share.5D30F204-5970-489F- BC2E-F863F1808A93, ownerName=defaultOwner>, <CKRecordZonelD: 0x302687b40; zoneName=com.apple.coredata.cloud-kit.zone, ownerName=_defaultOwner>" In my setup, I moved all my root objects into one custom zone (there is only one custom zone in my private database). In one of my root object, there are 6 'one-to-one' and 2 'one-to-many' relationships. The branch objects can contains other relationships. Create root object flow: func saveToPersistent(_ object: ViewModelObject) { serialQueue.async { let context = backgroundContext() context.performAndWait { // Create new baby with its one-to-one child objects. let cdNewBaby = self.newCDBaby(object, context) if let share = self.getShareZone(.privateStore).first { self.moveToShareZone(pObjects, share: share, store: .privateStore) } CoreDataManager.single.saveContext(context) self.updateZoneNSaveContext([cdNewBaby], context: context) } // context.perform } // serialQueue.async } func backgroundContext() -> NSManagedObjectContext { let context = persistentContainer.newBackgroundContext() context.transactionAuthor = contextAuthor context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy return context } func getShareZone(_ storeType: StoreType, zoneName: String? = nil) -> [CKShare] { var shares: [CKShare] = [] do { shares = try persistentContainer.fetchShares(in: stores[storeType]) } catch { print(error) return shares } if let zoneName = zoneName { shares = shares({ $0.recordID.zoneID.zoneName == zoneName }) } return shares } func moveToShareZone(_ sharedObjects: [NSManagedObject], share: CKShare, store: StoreType) { self.persistentContainer.share(sharedObjects, to: share) { managedObjects, share, container, error in if let error = error { print(error) } else if let share = share, let store = self.stores[store] { self.persistentContainer.persistUpdatedShare(share, in: store) { (share, error) in if let error = error { print(error) } } } } } // moveToShareZone Create one-to-many relationship branch object flow: serialQueue.async { let context = self.backgroundContext() context.performAndWait { // MARK: Retrieve the Root record let pObjects = CDRootRecord.fetchRecord(rootRecord.uuidString, store: store, zoneName: zoneName, context: context) if let pRootRecord = pObjects.first { self.newCDLogContent(pRootRecord.self, viewModelObject: viewModelObject, context: context) // MARK: Save Log CoreDataManager.single.saveContext(context) } } // context.performAndWait } // serialQueue Questions: (1) Should I save a root object first before share to custom zone; or share to custom zone first before save? (I implemented save before share to zone in the past and found some issues on iOS16 where the object is not saved; and end of sharing object before save which works) (2) As I understand, if a branch record is saved under a root record, it should automatically go into the root record. Or do I have to also share the branch record to the custom zone?
1
0
69
3d
SwiftData Initial sync on MacOS is partial
Yesterday I bumped into a weird issue. I've got an application that was previously only available on iOS, however I'm now creating a version for MacOS. As I released the "working" application to TestFlight, a friend of mine told me that they don't see any of their data in the mac application. Thus, I started to investigate, and found out that on the Testflight version, the initial sync fails to sync data that didn't exist before the installation. However, in the debug version of the application (directly ran from XCode) the initial sync syncs all data, and after downloading the production version from TestFlight it works like a charm.
0
0
65
4d
Core Data Crash: Could not merge changes
We have one Persistent store coordinator(PSC). On top of this we have two managed object contexts(MOC), one with private queue concurrency(bgMOC) persistentContainer.newBackgroundContext() and another with main queue concurrency(mainMOC). persistentContainer.viewContext We have verified that only one PSC is connected to the sqlite. We only have one PSC object and only one bgMOC. We always use bgMOC for writes. And main for UI just reads and UI purposes. We strictly follow access of managed objects in the contexts .perform { } always. Problem: We see lots of Merge Conflicts We have printed the conflict attributes and found object snapshot is always null cached snapshot is printed persisted snapshot is printed Our findings: Sometimes cached snapshot is same as persisted snapshot and sometimes there is a diff in some property(not sure relationships diff as snapshot dont have these info). The NSManagedObject current values match the persistent store snapshot always. In one of the logs we concluded that the cachedRow was stale, and both the managed object and persisted values were same with newer values. How do we proceed to fix this? Is cached row supposed to be synchronously updated with every save that happens? We dont want to put a merge policy as we think conflicts should not happen at first place as we are always writing via same bgMoc Additional information Our app does lots of quick(within 1-2 ms) bgMoc.perform { change, bgMoc.save } back to back and on same objets as well. Some places we have noticed if we try to dump a same change two times in quick succession via same bgMoc this crash happens. But we were not able to reproduce this on dev systems. something like bgMoc.perform { obj1.changeX, bgMoc.save } bgMoc.perform {obj1. changeX, bgMoc.save } We also do bgMocPerorm inside bgMoc.perofrm like this bgMoc.perform { on some managed objects validate a condition early exist if fails. onSomeOtherThreadComputeSomeStuff { success in bgMoc.perform { someChange bMoc.save() } } } Crash log [Debug] [NSManagedObjectContext 0x303b11e10] [NSManagedObjectContext+Enhancements.swift:32] > Save error. Error : Error Domain=NSCocoaErrorDomain Code=133020 "Could not merge changes." UserInfo={conflictList=( "NSMergeConflict (0x301aa97c0) for NSManagedObject (0x302045c70) with objectID '0x8d881a3c1b3524ba <x-coredata://412EE632-D802-451E-99DF-50ADF230B800/MailThread/p1416>' with oldVersion = 2 and newVersion = 3 and old cached row = {\n attachmentCount = 0;\n firstScheduledDate = \"<null>\";\n id = 2164154918090767;\n lastMessageDraftDate = \"<null>\";\n lastMessageReceivedDate = \"2024-07-16 18:31:34 +0000\";\n lastMessageSentDate = \"<null>\";\n messageCount = 1;\n rawFlags = 0;\n snippet = \"snippet1\";\n subject = \"subject1\";\n transactionIdAsNumber = 2164277798615057;\n umc = 1;\n} and new database row = {\n attachmentCount = 0;\n firstScheduledDate = \"<null>\";\n id = 2164154918090767;\n lastMessageDraftDate = \"<null>\";\n lastMessageReceivedDate = \"2024-07-16 18:31:34 +0000\";\n lastMessageSentDate = \"<null>\";\n messageCount = 1;\n rawFlags = 0;\n snippet = \"snippet1\";\n subject = \"subject1\";\n transactionIdAsNumber = 2164277798615057;\n umc = 1;\n}"
0
0
94
4d
Collaboration Preview Image and Title for CKShare When Collaborating With CloudKit
I recently updated our CloudKit collaboration invite codebase to use the new UIActivityController and NSItemProvider invitation as described in Apple's documentation. We previously used UICloudSharingController's init(preparationHandler:), which is since deprecated. We have all of the previous functionality in place: we successfully create a CKShare, send the invite out, engage the share, and collaborate. However, we cannot get the Messages CKShare preview to use our custom image and title (henceforth referred to as “collaboration metadata”). Previously, while using UICloudSharingController's init(preparationHandler:) to commence the share invite, the collaboration metadata successfully displayed in the Messages conversation. Now, we have a generic icon of our app and “Shared with App-Name" title, leading to a loss of contextual integrity for the invite flow. My question: How do we make the collaboration metadata appear in the Messages conversation? Here is our code for creating the UIActivityController, NSItemProvider, CKShare, and other related entities. It encapsulates the entire CloudKit CKShare invite setup. You will note that we do configure the CKShare with metadata, and we do set the LPLinkMetadata on the UIActivityItemsConfiguration. GitHub Gist. The metadata does successfully appear in the UIActivityController and the CKShare's image and title are available to the person receiving the share once they engage it and open it in our app – but the Messages preview item retains the generic message content. Also please note that this issue does occur in the production environment. As a final note, examining UICloudSharingController's definition leads me to believe that supplying a UIActivityItemSource is the key to getting correct Messages collaboration metadata in place. My efforts at using an item adhering to UIActivityItemSource in the UIActivityViewController used to send the share did not yield the rich previews and displayed metadata I am aiming for.
2
0
78
5d
ModelActors not persisting relationships in iOS 18 beta
I've already submitted this as a bug report to Apple, but I am posting here so others can save themselves some troubleshooting. This is submitted as FB14337982 with an attached complete Xcode project to replicate. In iOS 17 we use a ModelActor to download data which is saved as an Event, and then save it to SwiftData with a relationship to a Location. In iOS 18 22A5307d we are seeing that this code no longer persists the relationship to the Location, but still saves the Event. If we put a breakpoint in that ModelActor we see that the object graph is correct within the ModelActor stack trace at the time we call modelContext.save(). However, after saving, the relationship is missing from the default.store SQLite file, and of course from the app UI. Here is a toy example showing how inserting an Employee into a Company using a ModelActor gives unexpected results in iOS 18 22A5307d but works as expected in iOS 17. It appears that no relationships data survives being saved in a ModelActor.ModelContext. Also note there seems to be a return of the old bug that saving this data in the ModelActor does not update the @Query in the UI in iOS 18 but does so in iOS 17. Models @Model final class Employee { var uuid: UUID = UUID() @Relationship(deleteRule: .nullify) public var company: Company? /// For a concise display @Transient var name: String { self.uuid.uuidString.components(separatedBy: "-").first ?? "NIL" } init(company: Company?) { self.company = company } } @Model final class Company { var uuid: UUID = UUID() @Relationship(deleteRule: .cascade, inverse: \Employee.company) public var employees: [Employee]? = [] /// For a concise display @Transient var name: String { self.uuid.uuidString.components(separatedBy: "-").first ?? "NIL" } init() { } } ModelActor import OSLog private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "SimpleModelActor") @ModelActor final actor SimpleModelActor { func addEmployeeTo(CompanyWithID companyID: PersistentIdentifier?) { guard let companyID, let company: Company = self[companyID, as: Company.self] else { logger.error("Could not get a company") return } let newEmployee = Employee(company: company) modelContext.insert(newEmployee) logger.notice("Created employee \(newEmployee.name) in Company \(newEmployee.company?.name ?? "NIL")") try! modelContext.save() } } ContentView import OSLog private let logger = Logger(subsystem: Bundle.main.bundleIdentifier!, category: "View") struct ContentView: View { @Environment(\.modelContext) private var modelContext @Query private var companies: [Company] @Query private var employees: [Employee] @State private var simpleModelActor: SimpleModelActor! var body: some View { ScrollView { LazyVStack { DisclosureGroup("Instructions") { Text(""" Instructions: 1. In iOS 17, tap Add in View. Observe that an employee is added with Company matching the shown company name. 2. In iOS 18 beta (22A5307d), tap Add in ModelActor. Note that the View does not update (bug 1). Note in the XCode console that an Employee was created with a relationship to a Company. 3. Open the default.store SQLite file and observe that the created Employee does not have a Company relationship (bug 2). The relationship was not saved. 4. Tap Add in View. The same code is now executed in a Button closure. Note in the XCode console again that an Employee was created with a relationship to a Company. The View now updates showing both the previously created Employee with NIL company, and the View-created employee with the expected company. """) .font(.footnote) } .padding() Section("**Companies**") { ForEach(companies) { company in Text(company.name) } } .padding(.bottom) Section("**Employees**") { ForEach(employees) { employee in Text("Employee \(employee.name) in company \(employee.company?.name ?? "NIL")") } } Button("Add in View") { let newEmployee = Employee(company: companies.first) modelContext.insert(newEmployee) logger.notice("Created employee \(newEmployee.name) in Company \(newEmployee.company?.name ?? "NIL")") try! modelContext.save() } .buttonStyle(.bordered) Button("Add in ModelActor") { Task { await simpleModelActor.addEmployeeTo(CompanyWithID: companies.first?.persistentModelID) } } .buttonStyle(.bordered) } } .onAppear { simpleModelActor = SimpleModelActor(modelContainer: modelContext.container) if companies.isEmpty { let newCompany = Company() modelContext.insert(newCompany) try! modelContext.save() } } } }
0
0
93
5d
Context.insert() but at index 0
Hello! I am an inexperienced programmer learning swiftUI and swiftData so apologies in advance if this answer can be found elsewhere. I want to display a list of recent transcriptions, stored in modelContainer, in reversed order. I attempted to do transcriptionsList.reversed() in a ForEach (which is in a swiftUI List), but .onDelete gets messed up as it is deleting the items as per the normal order. The question is, is there a way when using context.insert(), that I can specify to insert at index 0? Or is there a better way of resolving this? Thanks in advance! The code when pasted in here appears to not be formatted correctly so I didn't include code
2
0
76
5d
Non-Optional One-To-Many Relationship Missing Object In Array
I'm encountering an issue with a one-to-many relationship between two models using SwiftData. Here's a simple example of my models: @Model final class TVSeries { var title: String @Relationship(deleteRule: .cascade, inverse: \Episode.tvSeries) var episodes: [Episode] init(title: String, episodes: [Episode] = []) { self.title = title self.episodes = episodes } } @Model final class Episode { var title: String var tvSeries: TVSeries // <- Works fine when optional init(title: String, tvSeries: TVSeries) { self.title = title self.tvSeries = tvSeries } } After creating and saving a TVSeries instance with an associated Episode, fetching the TVSeries instance shows that the episodes array remains empty whereas the back-link to the TVSeries works as expected. Here's the relevant test case: struct SwiftDataTestingTests { @Test func testFullInit() async throws { // Configure SwiftData context let config = ModelConfiguration(isStoredInMemoryOnly: true) let container = try ModelContainer(for: TVSeries.self, Episode.self, configurations: config) let context = ModelContext(container) context.autosaveEnabled = false // Create entries let tvSeries = TVSeries(title: "New Series") let episode = Episode(title: "Episode 1", tvSeries: tvSeries) context.insert(episode) try context.save() // Fetch tv series let tvSeriesDescriptor = FetchDescriptor<TVSeries>() let tvSeriesResult = try context.fetch(tvSeriesDescriptor) #expect(tvSeriesResult.count == 1) let fetchedTVSeries = try #require(tvSeriesResult.first) #expect(fetchedTVSeries.episodes.count == 1) // <- Expectation failed: (fetchedTVSeries.episodes.count → 0) == 1 // Fetch episodes let episodeDescriptor = FetchDescriptor<Episode>() let episodeResult = try context.fetch(episodeDescriptor) #expect(episodeResult.count == 1) let fetchedEpisode = try #require(episodeResult.first) #expect(fetchedEpisode.tvSeries.title == "New Series") } } Everything seems fine when I make the tvSeries attribute in the Episode model optional, but I would prefer to leave it explicit. I tested this on the latest XCode and XCode Beta versions running macOS Sonoma XCode Version: 15.4 (15F31d) XCode Beta Version: 16.0 beta 3 (16A5202i) MacOS Version: 14.5 (23F79) Any insights or suggestions on what might be causing this issue would be greatly appreciated. Thank you!
0
0
102
6d
SwiftData - error: Error: Persistent History (2) has to be truncated due to the following entities being removed: ( A )
I'm having trouble resolving the error. If I save data in view A and open view B, I get an error. If I open only view A, or only view B, there is no problem, but when I open a second view (A to B, B to A, etc.) I get an error. Attached is the code that reproduces the problem. We would appreciate your cooperation. No simulator is used. Xcode: Version 15.4 (15F31d) iPone11: 17.5.1 Thank you. Error error: Error: Persistent History (2) has to be truncated due to the following entities being removed: ( A ) warning: Warning: Dropping Indexes for Persistent History warning: Warning: Dropping Transactions prior to 2 for Persistent History warning: Warning: Dropping Changes prior to TransactionID 2 for Persistent History Code import SwiftUI import SwiftData @main struct issueApp: App { var body: some Scene { WindowGroup { ContentView() .modelContainer(for: [A.self, B.self]) } } } import SwiftUI import SwiftData struct ContentView: View { @State var showAView: Bool = false @State var showBView: Bool = false var body: some View { VStack { Button("show A"){ showAView.toggle() } .sheet(isPresented: $showAView){ AView() } Button("show B"){ showBView.toggle() } .sheet(isPresented: $showBView){ BView() } } } } struct AView: View { @Environment(\.modelContext) var context @Query private var list: [A] let query = QueryData<A>() var body: some View { VStack { if list.count > 0 { VStack { List { ForEach(list, id: \.number){ item in VStack { Text(item.number) } } } Button("clear"){ try? context.delete(model: A.self) } } } else { Button("set"){ query.set(data: A(number: "1")) } } } } } struct BView: View { @Environment(\.modelContext) var context @Query private var list: [B] let query = QueryData<B>() var body: some View { VStack { if list.count > 0 { VStack { List { ForEach(list, id: \.number){ item in VStack { Text(item.number) } } } Button("clear"){ try? context.delete(model: B.self) } } } else { Button("set"){ query.set(data: B(number: "1")) } } } } } class QueryData<T: PersistentModel> { private var container: ModelContainer? init() { self.container = try? ModelContainer(for: T.self) } func set(data: T) { guard let container = self.container else { return } let context = ModelContext(container) do { try context.delete(model: T.self) context.insert(data) try context.save() } catch { print("error: \(error)") } } } @Model final class A { var number: String init(number: String) { self.number = number } } @Model final class B { var number: String init(number: String) { self.number = number } }
2
0
144
6d
SwiftData update value
Hi I want to update item value after insert. Solution(False): I want to add "isAutosaveEnabled" prop to modelContainer, but modelContainer just have one parm. Solution2(False): There doesn't have API to update SwiftData, I just find insert, delete and save. I change the city_name and run "try? modelContext.save()". But it replace after I reopen the app and maybe it doesn't work even before I close app. How can I update the city_name? /// --------- App --------- import SwiftUI import SwiftData @main struct MyApp: App { var container: ModelContainer init(){ let schema = Schema([ SD_City.self, ]) let modelConfiguration = ModelConfiguration(schema: schema) do { container = try ModelContainer(for: schema, configurations: [modelConfiguration]) let context = ModelContext(container) var city : SD_City city = SD_City(city_id: 1, city_name: "city1") context.insert(city) city = SD_City(city_id: 2, city_name: "city2") context.insert(city) } catch { fatalError("Could not create ModelContainer: ") } } var body: some Scene { WindowGroup { ContentView() } .modelContainer(container) } } /// --------- ContentView --------- import SwiftUI import SwiftData struct ContentView: View { @Environment(\.modelContext) private var modelContext @State var sortOrder_city = SortDescriptor(\SD_City.city_id) @Query(sort: [SortDescriptor(\SD_City.city_id)]) var citylist: [SD_City] @State var city_id = 0 @State var city_name = "city_name" var body: some View { HStack { Button("Change \ncity name", action: { // Update the city_name of city1 to city3 // SD_City(city_id: 1, city_name: "city1") --> SD_City(city_id: 1, city_name: "city3") }) Text(city_name) } } } /// ---------swiftdata--------- import SwiftUI import SwiftData @Model final class SD_City{ @Attribute(.unique) let city_id: Int = 1 var city_name: String = "" init(city_id: Int , city_name: String) { self.city_id = city_id self.city_name = city_name } } Thanks~
0
0
101
6d
Public and Private CK sync failure, macOS mistake = add data before indexing
I have a Multiplatform app for iOS and macOS targets. I am using CloudKit with CoreData and have successfully established a private and public database. The app has successfully synced private and public data for months between macOS (dev machine), an iPhone 13 Pro and an iPad Pro 12.9inch 2nd gen. The public data also syncs perfectly to simulator instances running under other iCloud accounts. Recently I added a new entity in the public DB and here is where I seemed to have made a mistake. I entered data into the new public database via my developer UI built into the macOS app running on my MBP before I indexed the necessary fields. Side note - I find it necessary to index the following for each Entity to ensure iCloud sync works as expected on all devices... modifiedTimestamp - Queryable modifiedTimestamp - Sortable recordName - Queryable Realising my mistake, I indexed the above CKRecord fields for the new Entity. Since then, the macOS target has remained in some way "frozen" (for want of a better term). I can add new public or private records in the macOS app but they do not propagate to the public or private stores in iCloud. I have attempted many fixed, some summarised below: clean build folder from Xcode; remove all files from the folder /Users//Library/Containers/, place in recycle bin, empty recycle bin, then build and run; build and run on iPhone and iPad targets to ensure all apps are current dev version, then repeat above processes. I've read through the console logging when I build and run the macOS app many many times to see whether I can find any hint. The closest thing I can find is... BOOL _NSPersistentUIDeleteItemAtFileURL(NSURL *const __strong) Failed to stat item: file:///Users/<me>/Library/Containers/com.me.AppName/Data/Library/Saved%20Application%20State/com.me.AppName.savedState/restorecount.plist but my research on this and other forums suggests this is not relevant. Through this, the app still functions as expected on iOS devices and both private and public database additions and modifications propagate to iCloud stores and to other devices. I expect that removing the macOS app entirely from my dev machine would trigger a complete sync with all existing data. Imagine I bought a new macOS device and chose to install my app where before I had run this only on my iOS devices. My current problem suggests that I could not do this, but I know that this is not the intended behaviour. This scenario makes me think there is a setting file for my macOS app that I'm not aware of and that this impeding the sync of all existing app data back to the fresh install of the macOS app? But that is a wild guess. Running public releases (no betas) Xcode 15.4 (15F31d) macOS Sonoma 14.5 physical iOS devices running iOS 17.5.1 Any words of wisdom on how I might go about trying to solve this problem please?
0
0
130
6d
Tabs with SwiftData
Hello folks, absolute newbie here. I'm following a tutorial that I'm trying to adjust to my own needs. In the tutorial the app is sat up in a way that you can add a new entry to a note taking app by tapping on a button which brings in an edit view. What I'm trying to do is to bring in the edit view by adding it as a tab on the bottom instead. Here's my ContentView: import SwiftData import SwiftUI struct ContentView: View { let example = Entry(content: "Example Entry") var body: some View { TabView { LifeEventsView() .tabItem { Label("Life events", systemImage: "house") } EditEntryView(entry: example) .tabItem { Label("Add new event", systemImage: "plus") } MattersView() .tabItem { Label("What matters", systemImage: "house") } } } } #Preview { ContentView() } And here's the EditEntryView: import SwiftData struct EditEntryView: View { @Bindable var entry: Entry var body: some View { Form { TextField("Entry", text: $entry.content, axis: .vertical) } .navigationTitle("Edit entry") .navigationBarTitleDisplayMode(.inline) } } #Preview { do { let config = ModelConfiguration(isStoredInMemoryOnly: true) let container = try ModelContainer(for: Entry.self, configurations: config) let example = Entry(content: "Example Entry") return EditEntryView(entry: example) .modelContainer(container) } catch { fatalError("Failed to create model container.") } } The build succeeds but I get an error while running saying: Thread 1: Fatal error: failed to find a currently active container for Entry I'm sure this is an easy fix but I've been thinking with it for a while with no luck.
0
0
133
1w
Importing Data into SwiftData in the Background Using ModelActor and @Query
I have an app with fairly typical requirements - I need to insert some data (in my case from the network but could be anything) and I want to do it in the background to keep the UI responsive. I'm using SwiftData. I've created a ModelActor that does the importing and using the debugger I can confirm that the data is indeed being inserted. On the UI side, I'm using @Query and a SwiftUI List to display the data but what I am seeing is that @Query is not updating as the data is being inserted. I have to quit and re-launch the app in order for the data to appear, almost like the context running the UI isn't communicating with the context in the ModelActor. I've included a barebones sample project. To reproduce the issue, tap the 'Background Insert' button. You'll see logs that show items being inserted but the UI is not showing any data. I've tested on the just released iOS 18b3 seed (22A5307f). The sample project is here: https://hanchor.s3.amazonaws.com/misc/SwiftDataBackgroundV2.zip
2
1
217
1w
SwiftData: externalStorage attribute on ios18
Hello, I'm experiencing an issue with iOS 18 Beta 3 and SwiftData. I have a model with some attributes marked as externalStorage because they are pretty large. In iOS 17 I could display a list of all my Models and it would not prefetch the externalStorage attributes. Now in iOS 18 it seems the attributes are fetched even though I don't use them in my list. It's an issue because the memory use of my app goes from 100mo on iOS 17 to more than 1gb on iOS 18 because of this. My app is configured to sync with iCloud. Anyone else experiencing the issue? Thanks Gil
2
1
151
1w
Multiple relationship variables of the the same type
Want to know if it's possible to have multiple variables of the same type? This code works but when the app loads the data again on relaunch, both variables have both the new cards and discarded cards. @Model class Player { let id = UUID() let name: String @Relationship(deleteRule: .cascade, inverse: \Card.player) var cards: [Card] = [] @Relationship(deleteRule: .cascade, inverse: \Card.player) var discardedCards: [Card] = [] init(name: String, cards: [Card]) { self.name = name self.cards = cards } } @Model class Card { let id = UUID() let name: String var player: Player? init(name: String) { self.name = name } }
0
0
99
1w
SwiftData ModelConfiguration with GroupContainer shows Data Access alert on every launch
I'm building an app which has both iOS and macOS versions along with extensions on both platforms. I want the main app to be able to share data the extension using the group container and I want the app on both platforms sync data over CloudKit. CloudKit synchronization works like a dream. The data sharing between the app and extension on iOS works also exactly as intended. However on macOS every time the app is launched I get “MyApp” would like to access data from other apps. alert dialog. I tried initializing the ModelConfiguration both with an explicit and automatic app group container identifiers. Same results.
1
0
149
1w
Questions About CloudKit Security Roles and Permissions
Hi, I'm using CloudKit to create an app that backs up and records your data to iCloud. Here's what I'm unsure about: I understand that the 'CloudKit Dashboard' has 'Security Roles'. I thought these were meant to set permissions for accessing and modifying users' data, but I found there was no change even when I removed all 'Permissions' from 'Default Roles'. Can you clarify? I'd like to know what _world, _icloud, and _creator in Default Roles mean respectively. I would like to know what changes the creation, read, and write permissions make. Is it better to just use the default settings? Here's what I understand so far: Default Roles: _world: I don't know _icloud: An account that is not my device but is linked to my iCloud _creator: My Device Permissions: create: Create data read: Read data write: Update and delete data. I'm not sure if I understand this correctly. Please explain.
1
0
199
1w
CloudKit and SwiftData not syncing on MacOS
I have a simple app that uses SwiftUI and SwiftData to maintain a database. The app runs on multiple iPhones and iPads and correctly synchronises across those platforms. So I am correct setting Background Modes and Remote Notifications. I have also correctly setup my Model Configuration and ModelContainer (Otherwise I would expect syncing to fail completely). The problem arises when I run on a Mac (M1 or M3) either using Mac Designed for iPad or Mac Catalyst. This can be debugging in Xcode or running the built app. Then the app does not reflect changes made in the iPhone or iPad apps unless I follow a specific sequence. Leave the app, (e.g click on a Finder window), then come back to the app (i.e click on the app again). Now the app will show the changes made on the iPhone/iPad. It looks like the app on the Mac is not processing remote notifications when in the background - it only performs them when the app has just become active. It also looks like the Mac is not performing these sync operations when the app is active. I have tried waiting 30 minutes and still the sync doesn't happen unless I leave the app and come back to it. I am using the same development CloudKit container in all cases
1
2
113
1w