I've defined 'public' and 'private' configurations in CoreData and configured the 'public' configuration to be handled in the CloudKit publicDB. The two configurations contain disjoint sets of NSManagedObject entity types. Some of the entities in the private configuration reference an entity in the public configuration using a UUID; thereby avoiding cross-configuration CoreData relationships. Avoiding this is a requirement of CoreData + CloudKit.
Now, since the UUID is meant to point to an object, in the awakeFromFetch() method I decided to fill in a transient property. No go. Even for a transient property there cannot be a cross-configuration relationship.
Why? Is there a way to avoid this constraint?
The cost of not having a transient property is to do a fetch of the object based on the UUID every time it is needed. Is this something that one should never worry about?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
Hello Developers,
So I am having a problem, I wanted to do some computation after I have called context.save() but looking at context.changedModelsArray i see that there is no models that have changed. I have also checked insertedModelsArray and deletedModelsArray and all they're empty. Does swiftdata not support or keep track of the models being deleted/changed/inserted?
I'm using SwiftData to save title and images.
When I delete the model, the fetched model count reduces. But in the settings - storage - myApp, the document and data does not change. I deleted all the models but its like 9GB left.
I followed the SwiftData example app provided by Apple as much as possible.
I tried isAutosaveEnabled = false and put save() to every delete functions.
deleteRule = .cascade
In my recent endeavor, I aimed to introduce new Fetch Index Elements to the Core Data model of my iOS application. To achieve this, I followed a process of lightweight migration, detailed as follows:
Navigate to Editor > Add Model Version to create a new version of the data model.
Name the new version with a sequential identifier (e.g., MyAppModelV3.xcdatamodel) based on the naming convention of previous models.
Select the newly created version, MyAppModelV3.xcdatamodel, as the active model.
Mark this new version as the "Current" model in the Xcode properties panel on the right.
In the new version of the model, MyAppModelV3.xcdatamodel, and add the new Fetch Index Elements there. Also, insert "v3" in the Versioning Hash Modifier field of affected entity, to indicate this modification.
Upon reflection, I realized that creating a new version of the xcdatamodel might not have been necessary for this particular case. However, it appears to have caused no adverse effects on the application's functionality.
During testing, I executed the application in a simulated environment, initially running an older version of the app to inspect the database content with SQLite DB Browser. I then upgraded to the latest app version to verify that the migration was successfully completed without causing any crashes. Throughout this testing phase, I employed the -com.apple.CoreData.MigrationDebug 1 flag to monitor all SQL operations, ensuring that indexes were appropriately dropped and recreated for the affected entity.
Following thorough testing, I deployed the update to production. The majority of users were able to upgrade to the new app version seamlessly. However, a small fraction reported crashes at startup, indicated by the following error message:
Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={NSUnderlyingError=0x2820ad3e0 {Error Domain=NSCocoaErrorDomain Code=134100 "The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store." UserInfo={metadata={ NSPersistenceFrameworkVersion = 1338; NSStoreModelVersionChecksumKey = "qcPf6+DfpsPrDQ3j1EVXcBIrFe1O0R6IKd30sJf4IrI="; NSStoreModelVersionHashes = { NSAttachment = {length = 32, ...
Strangely, the only way I could replicate this issue in the simulator was by running the latest version of the app followed by reverting to an older version, a scenario unlikely to occur in a real-world setting. This raises the question: How could this situation arise with actual users, considering they would typically move from an old to a new version rather than the reverse?
I am reaching out to the community for insights or advice on this matter. Has anyone else encountered a similar problem during the Core Data migration process? How did you resolve it?
According to my experiments SwiftData does not work with model attributes of primitive type UInt64. More precisely, it crashes in the getter of a UInt64 attribute invoked on an object fetched from the data store.
With Core Data persistent UInt64 attributes are not a problem. Does anyone know whether SwiftData will ever support UInt64?
Hi,
I am developing a multi platform application using SwiftData.
Since a few days, when running the macOS version I cannot create the model container while on iOS everything is fine.
On macOS I receive the error :
Thread 1: Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer)
The code that initialize the model container is :
let schema = Schema([
Account.self,
ABCategory.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
let container = try ModelContainer(for: schema, configurations: [modelConfiguration])
container.mainContext.undoManager = UndoManager()
return container
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}
var body: some Scene {
WindowGroup {
ContentView(accuBalanceModel: accuBalanceModel)
}
.modelContainer(sharedModelContainer)
//.modelContainer(for: [Account.self, ABCategory.self], inMemory: false, isUndoEnabled: true)
#if os (OSX)
//
// Remove the 'New Window' menu item.
//
.commands {
CommandGroup(replacing: CommandGroupPlacement.newItem) {
}
}
#endif
}
I can say that the model has no issues as it works when running on IOS and also, if I
change inMemory: false with inMemory: true the macOS version succeed in creating the model.
I can also say this is not a network issue as the Mac and the iOS device running the application are connected to the same box.
The Xcode version is Version 15.2 (15C500b)
Ios device where everything is fine : iPhone 11 OS 17.3.1 (21D61)
the Mac device : Mac mini M1 2020 : 14.0 (23A344)
Any advice to resolve that problem would be greatly appreciated.
Best Regards
Christophe Lestrade
Hi
I have an App that uses SwiftData + CloudKit as a private database to sync across users' devices. Is there a way to enable sharing and collaboration capabilities with other users by using Cloudkit + CoreData?
To reduce the complexity of the Shared Object Graph, I envision treating the object graph as an Object (Document). In this way, the cloud's implementation of the sharing DB is simplified. The App will take care of the integrity and security of the document.
Ideally, I would love this capability in a multi-peer-to-peer local network, but I have yet to find a Conflict-free Replicated Document library to use.
In summary, I want to use SwiftData for the CRUD operations in the data objects running in the local App and treat the Object Graph as a document for sharing with other users.
Any guidance or any code examples will be appreciated.
Thank you!
My app does not knowingly capture or retain user data in any form, but I wanted to make sure that I don’t inadvertently have access to user data via CloudKit. I’m reaching out to confirm this.
Here’s the basic structure of the app - users can enter information which is then retained for their own use. I’m using SwiftData. @Model data is saved in a CloudKit container, which permits data syncing across iOS devices. I’ve never written any code to make the container database I’m using for the app (or zones within it) anything other than private, so I think (??) by default the data is stored privately. Data syncing does work and my icloud.developer.apple.com dashboard shows a private database subscription but no public or shared subscriptions.
My understanding of this design has been that the user’s data was stored in their personal, private iCloud account and I did NOT have access to it (nor do I need or want to have access).
However, then I watched this WWDC 2018 video on GDPR compliance (https://developer.apple.com/videos/play/tech-talks/703/), in which Michael Ford presents code that provides “visibility into the data that is saved in CloudKit for the user”, including the private database. This sounds to me like a developer might actually be able to access a user’s private database data.
Have I misinterpreted this WWDC 2018 presentation, has a developer’s access to user data in a private database changed since 2018, or do I actually have access to user data via my reliance on CloudKit to save user data?
Many thanks for your help!
I need to edit a persisted SwiftData item in an isolated context, because if not is isolated with every change triggers expensive computations.
When the edit is done and I save the temporal context. I expect that the temporal context is merged with the main context and all the views that have a dependency with the edited Item.id be invalidated and updated. But not. When I dismiss the ItemEditView and temporalContext.save() is triggered the ItemNormalView that has a dependency with the Item.id remains not updated so I have to do a non elegant either reliable workaround.
What is the canonical way of do it?
The most relevant code:
struct ItemEditView: View {
@Bindable var temporalItem: Item
let temporalContext: ModelContext
@Binding var updater: Bool
init(itemID: PersistentIdentifier,
container: ModelContainer,
updater: Binding<Bool>){
temporalContext = ModelContext(container)
temporalContext.autosaveEnabled = false
self.temporalItem = temporalContext.model(for: itemID) as! Item
self._updater = updater
}
var body: some View {
TextField("", text: $temporalItem.name)
.onDisappear{
Task{
try! temporalContext.save()
//Workaround:
try? await Task.sleep(nanoseconds:1_000_000_000)
updater.toggle()
}
}
}
}
struct ItemView: View {
let item: Item
@Environment(\.modelContext) private var modelContext
@State private var itemEditorViewModalViewIsPresented = false
@State private var updater = false
var body: some View {
VStack{
ItemNornalView(item: item, updater: $updater)
Button("Show Edit Modal View"){
itemEditorViewModalViewIsPresented.toggle()
}
}
.sheet(isPresented: $itemEditorViewModalViewIsPresented){
ItemEditView(itemID: item.id, container: modelContext.container, updater: $updater)
}
}
}
struct ItemNornalView: View {
let item: Item
@Binding var updater: Bool
var body: some View {
let _ = Self._printChanges()
Text("\(updater)").scaleEffect(0).hidden()
Text("Item name: \(item.name)")
}
}
The rest of the code:
struct ContentView: View {
var body: some View {
TabView{
MetricsView().tabItem { Text("Metrics") }
ItemsList().tabItem { Text("List") }
}
}
}
struct MetricsView: View {
@Query private var items: [Item]
var body: some View {
Chart{
BarMark(x: .value("x", "Average"), y: .value("y", expensiveComputation(items: items)))
}
}
private func expensiveComputation(items: [Item])->Double{
//...
return 1.0
}
}
struct ItemsList: View {
@Query private var items: [Item]
@Environment(\.modelContext) private var modelContext
@State var path = NavigationPath()
var body: some View {
let _ = Self._printChanges()
NavigationStack(path: $path) {
List {
ForEach(items) { item in
Button{
path.append(item)
}label:{
ItemRowView(item: item)
}
}
.onDelete(perform: deleteItems)
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
EditButton()
}
ToolbarItem {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
.navigationDestination(for: Item.self){ item in
ItemView(item:item)
}
}
}
private func addItem() {
withAnimation {
let newItem = Item()
modelContext.insert(newItem)
try! modelContext.save()
}
}
private func deleteItems(offsets: IndexSet) {
withAnimation {
for index in offsets {
modelContext.delete(items[index])
}
}
}
}
Hi Forum,
I am wondering how I can filter the data retrieving from CloudKit with SwiftData and using the #Predicate macro?
I always get this error message:
Thread 1: Fatal error: Predicate does not support keypaths with multiple components
Does someone know how to add two predicates and use CloudKit?
Thanks in advance 😊
I am developing a SwiftData application using CloudKit.
I have been playing with the app for a while and populated the database.
Recently I decided to change the type of an attribute. So I reseted the scheme in CloudKit Console to the production one, which actually is empty for now, so I restart from scratch.
Unfortunatly, the locally cached database on my Mac is using the old schema and my app fails loading the ModelContainer because of the type incompatibilities.
I would like to delete the locally cached database but I haven't a clue of where it can be. I do not want to go through a migration plan for that.
Does someone know where can I find it ?
Chris tof
I have set a core data structure based on two entities :
first entity is "Mot" (Word) with one String attribute "graphie" (spelling) and one ToOne relationship "definition" with the second entity.
second entity is "Definition" that has one String attribute "enonce" (statement) and on ToMany relationship "mot" with entity Mot.
One word can have several spellings but only one definition.
I managed to load several rows of data and now, when I select one word I want to display all the spellings, which means all "Mot" that have the same definition.
In my content view I have a List based on a request:
@Environment(\.managedObjectContext) private var viewContext
@FetchRequest(entity: Mot.entity(), sortDescriptors: [NSSortDescriptor(key: "graphie", ascending: true)])
private var mots: FetchedResults<Mot>
var body: some View {
NavigationView {
...
List {
ForEach(mots) { (mot: Mot) in
HStack {
Text(mot.graphie ?? "Non trouvé")
Spacer()
Text(mot.definition!.enonce ?? "Non trouvé")
}
.onTapGesture {
motAffiche = mot
tfMot = mot.graphie ?? "Not found"
tfDefinition = mot.definition?.enonce ?? "Not found"
tfOrthoAlt = returnOrthoAlt(mot: mot)
}
}
}
the function returnOrthoAlt(mot) is supposed to return a string with all spellings separated by commas.
What is working for the moment, but not satisfactory is the following code for that function
private func returnOrthoAlt(mot: Mot) -> String {
var ort = [String]()
for elem in mots {
if elem.definition!.enonce! == mot.definition!.enonce! && elem != mot {
ort.append(elem.graphie!)
let defObjId = elem.definition!.objectID.uriRepresentation()
print("\(elem.graphie!) def: \(elem.definition!.enonce!) \(defObjId) ")
}
}
return if !ort.isEmpty { ort.joined(separator: ", ")} else {""}
}:
I am going through the table 'mots' of FetchedResults to find those that have the same definition as the current 'mot'. What I find not satisfactory is that I am comparing the 'mot.definition.enonce' of each Mot, which is not supposed to be unique, instead of 'mot.definition' directly, which did not work. The objects 'mot.definition' are obviously not equal (neither with == nor with === operators).
Also I tried to retrieve the reverse relation directly with 'mot.definition.mot' but that returned a NSSet which, converted to a set seems to contain only one object, that is 'mot' itself.
One possibility of course, would be to ad a unique Id to the entity 'Definition' but I seem to understand that this is not the recommended practice, the more so as swift does not provide system generated id.
What do I miss in the core data concept ? Can someone help me out ?
In WWDC2020, the video "Synchronizing a local store to the cloud" shows some data fields in the left column of the dashboard, such as CD_post, CD_Tag, where did these come from? need to create it manually on dababoard, or it synchronize from the demo app ?
why do I run this corresponding demo, but there are no data fields in the dashboard of my account
and my dashboard of my account is almost empty?
Hello,
I want to build an app that will allow the user to entry some health related records and be synced with the HealthKit. Any record that is in the HealthKit is stored locally on the device.
I find it conceptually unsure if I should be storing the HealthKit records in the SwiftData to make the user records available across the iCloud synced devices.
Should I read the HealthKit record, make a copy of it (including it's ID) on my app's data in SwiftData?
How the syncing should be done the right way?
thanks.
I am writing code to be able to perform Undo&Redo with SwiftUI and SwiftData. Unlike usual, there is a Model of SwiftData in the Observable class,
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true, isUndoEnabled: true)
@Environment(\.undoManager) var undoManager
but it doesn't work. How should I describe the isUndoEnabled: true option and the undoManager?
import SwiftData
@Observable class SwiftDataCoordinator {
static let shared = SwiftDataCoordinator()
var items = [Item]()
let modelContainer: ModelContainer = {
let schema = Schema([
Item.self
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
}
Dear members,
The lack of enumeration during the documentation has lead to the situation where the sizing of the text can no longer be used to categorise anything. Take an example of the Apple Technologies (where the standardised way to read and type both are missing).
Also, suggestions from the members related to running the numbers (about updating the real-time prices of the underlying assets) in the iOS application and the usage of the most relevant frameworks will be highly appreciated.
Waiting to hear back soon.
I created an app using SwiftData to store titles, dates, and images. When creating and deleting data models repeatedly, the app's storage space should remains the same, but for me it steadily accumulate so now its 5GB in the app's settings despite having no data. Why is this happening?
Here is my basic problem. The app itself builds without issue, but when I simulate I get the following log in the debug console. The app also force closes when the Save function is selected with these errors. The app is a simple form which saves the data to a CoreData entity. It will do more (full CRUD) later after I deal with this particular issue. I have checked and rechecked class names, spelling issues, case sensitivities. I can not seem to find the issue.
error: No NSEntityDescription in any model claim the NSManagedObject subclass 'App.Entity' so +entity is confused. Have you loaded your NSManagedObject Model yet?
Also:
error: +[App.Entity entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
Hello everyone,
I followed this (https://www.hackingwithswift.com/quick-start/swiftdata/how-to-sync-swiftdata-with-icloud) guide from Paul Hudson on how to sync swiftdata with iCloud. I tried it on my device directly and it worked exactly as I would expect. Now I tried the same version of the app on another device installed through TestFlight external tester group. It no longer works.
When deleting the app, the alert reads "Deleting this app will also delete its data, but any documents or data will be stored in iCloud will not be deleted" so the app should have said something in iCloud. When looking in Settings -> Your Name -> iCloud -> Manage Account Storage, on the working device I can see around 300 KB saved in iCloud, on the other device my app is not listed. Both have a fast and working internet connection, almost fully charged, low data mode turned off, running 17.4.1, Background modes enabled, Mobile data enabled, more than enough unused iCloud storage and plenty of time to sync.
I was streaming the logs from the Cloudkit dashboard but nothing appeared there. The data saved neither syncs to another device of the same apple id nor reappears after app removal.
The model Container is initialized without any configuration, the only relationship is optional and every value has a default value. There is A LOT of log noise when launching the app and I am unable to get any meaningful information from that. I can only get the log from the device where it is working as expected.
I have triple checked that it is exactly the same app version and no debug conditions anywhere. I have absolutely no idea what is causing this.
Thanks for any help :)
I have a model class with various status values that I would like to store in a single 64 bits integer. For example one bit for this boolean flag, 4 bits for that lookup value and so on.
I tried to create a predicate containing bitwise operators to query specific bits but that seems impossible, no way no compile it.
is there a way I am no aware of to accomplish this, some workaround or is that planned to be possible in future Swiftdata release?
that would be a shame having to store a few status values of 4 or less bits each in different 64bits values, what a waste!
Thanks
Tof