Greetings i have an app that uses three different SwiftData models and i want to know what is the best way to use the them accross the app. I though a centralized behaviour and i want to know if it a correct approach.First let's suppose that the first view of the app will load the three models using the @Enviroment that work with @Observation. Then to other views that add data to the swiftModels again with the @Environment. Another View that will use the swiftData models with graph and datas for average and min and max.Is this a corrent way? or i should use @Query in every view that i want and ModelContext when i add the data.
@Observable
class CentralizedDataModels {
var firstDataModel: [FirstDataModel] = []
var secondDataModel: [SecondDataModel] = []
var thirdDataModel: [ThirdDataModel] = []
let context: ModelContext
init(context:ModelContext) {
self.context = context
}
}
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
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?
Hi all,
In my SwiftUI / SwiftData / Cloudkit app which is a series of lists, I have a model object called Project which contains an array of model objects called subprojects:
final class Project1
{
var name: String = ""
@Relationship(deleteRule: .cascade, inverse: \Subproject.project) var subprojects : [Subproject]?
init(name: String)
{
self.name = name
self.subprojects = []
}
}
The user will select a project from a list, which will generate a list of subprojects in another list, and if they select a subproject, it will generate a list categories and if the user selects a category it will generate another list of child objects owned by category and on and on.
This is the pattern in my app, I'm constantly passing arrays of model objects that are the children of other model objects throughout the program, and I need the user to be able to add and remove things from them.
My initial approach was to pass these arrays as bindings so that I'd be able to mutate them. This worked for the most part but there were two problems: it was a lot of custom binding code and when I had to unwrap these bindings using init?(_ base: Binding<Value?>), my program would crash if one of these arrays became nil (it's some weird quirk of that init that I don't understand at al).
As I'm still learning the framework, I had not realized that the @model macro had automatically made my model objects observable, so I decided to remove the bindings and simply pass the arrays by reference, and while it seems these references will carry the most up to date version of the array, you cannot mutate them unless you have access to the parent and mutate it like such:
project.subcategories?.removeAll { $0 == subcategory }
project.subcategories?.append(subcategory)
This is weirding me out because you can't unwrap subcategories before you try to mutate the array, it has to be done like above. In my code, I like to unwrap all optionals at the moment that I need the values stored in them and if not, I like to post an error to the user. Isn't that the point of optionals? So I don't understand why it's like this and ultimately am wondering if I'm using the correct design pattern for what I'm trying to accomplish or if I'm missing something? Any input would be much appreciated!
Also, I do have a small MRE project if the explanation above wasn't clear enough, but I was unable to paste in here (too long), attach the zip or paste a link to Google Drive. Open to sharing it if anyone can tell me the best way to do so. Thanks!
Hello everyone,
I am experiencing a persistent authentication error when querying a custom user profile record, and the error message seems to be a red herring.
My Setup:
I have a custom CKRecord type called ColaboradorProfile.
When a new user signs up, I create this record and store their hashed password, salt, nickname, and a custom field called loginIdentifier (which is just their lowercase username).
In the CloudKit Dashboard, I have manually added an index for loginIdentifier and set it to Queryable and Searchable. I have deployed this schema to Production.
The Problem:
During login, I run an async function to find the user's profile using this indexed loginIdentifier.
Here is the relevant authentication code:
func autenticar() async {
// ... setup code (isLoading, etc.)
let lowercasedUsername = username.lowercased()
// My predicate ONLY filters on 'loginIdentifier'
let predicate = NSPredicate(format: "loginIdentifier == %@", lowercasedUsername)
let query = CKQuery(recordType: "ColaboradorProfile", predicate: predicate)
// I only need these specific keys
let desiredKeys = ["password", "passwordSalt", "nickname", "isAdmin", "isSubAdmin", "username"]
let database = CKContainer.default().publicCloudDatabase
do {
// This is the line that throws the error
let result = try await database.records(matching: query, desiredKeys: desiredKeys, resultsLimit: 1)
// ... (rest of the password verification logic)
} catch {
// The error always lands here
logDebug("Error authenticating with CloudKit: \(error.localizedDescription)")
await MainActor.run {
self.errorMessage = "Connection Error: \(error.localizedDescription)"
self.isLoading = false
self.showAlert = true
}
}
}
The Error:
Even though my query predicate only references loginIdentifier, the catch block consistently reports this error:
Error authenticating with CloudKit: Field 'createdBy' is not marked queryable.
I know createdBy (the system creatorUserRecordID) is not queryable by default, but my query isn't touching that field. I already tried indexing createdBy just in case, but the error persists. It seems CloudKit cannot find or use my index for loginIdentifier and is incorrectly reporting a fallback error related to a system field.
Has anyone seen this behavior? Why would CloudKit report an error about createdBy when the query is explicitly on an indexed, custom field?
I'm new to Swift and I'm struggling quite a bit.
Thank you,
When attempting to deploy schema changes in the iCloudKit Database by clicking the Deploy Schema Changes button, a Confirm Deployment dialog appears, showing an error: “Internal error”. The following error details were observed in the JavaScript console:
• description: “The request has failed due to an error.”
• headers: undefined
• message: “Known response error: The request has failed due to an error.”
• result:
• code: 400
• detailedMessage: undefined
• message: “bad-request”
• reason: “Internal error”
• redirectUrl: undefined
• requestUuid: “0c5b4af2-15c9-425f-87ea-************”
• retryAfterSeconds: undefined
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
CloudKit
CloudKit Dashboard
CloudKit Console
My client is using iCloud Mail with his custom domain and he communicated with many govt organizations which seem to all be using Barracuda Email Protection for their spam prevention. I have properly configured his SPF, DKIM & DMARC DNS records however his emails were still being rejected. (Email header below)
I contacted Barracuda support with the email header and they replied saying that the emails were rejected becuase Apple Mail has missing PTR records.
I have sent dozens of emails for testing and looking at all their headers I can see (ms-asmtp-me-k8s.p00.prod.me.com [17.57.154.37]) which does not have a PTR record.
----FULL EMAIL HEADER WITH 3RD PARTY DOMAINS REMOVED-----
<recipient_email_address>: host
d329469a.ess.barracudanetworks.com[209.222.82.255] said: 550 permanent
failure for one or more recipients (recipient_email_address:blocked)
(in reply to end of DATA command)
Reporting-MTA: dns; p00-icloudmta-asmtp-us-west-3a-100-percent-10.p00-icloudmta-asmtp-vip.icloud-mail-production.svc.kube.us-west-3a.k8s.cloud.apple.com
X-Postfix-Queue-ID: 8979C18013F8
X-Postfix-Sender: rfc822; sender_email_address
Arrival-Date: Thu, 20 Mar 2025 12:30:05 +0000 (UTC)
Final-Recipient: rfc822; @******
Original-Recipient: rfc822;recipient_email_address
Action: failed
Status: 5.0.0
Remote-MTA: dns; d329469a.ess.barracudanetworks.com
Diagnostic-Code: smtp; 550 permanent failure for one or more recipients
(recipient_email_address:blocked)
Return-Path: <sender_email_address>
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sender_domain;
s=sig1; bh=CyUt/U7mIHwXB5OQctPjRH/OxLH7GsLR54JjGuRkj9Y=;
h=From:Message-Id:Content-Type:Mime-Version:Subject:Date:To:x-icloud-hme;
b=hwEbggsctiCRlMlEgovBTjB/0sPRCb2k+1wzHRZ2dZNrZdOqvFSNWU+Aki9Bl8nfv
eEOoXz5qWxO2b2rEBl08lmRQ3hCyroayIn4keBRrgkxL1uu4zMTaDUHyau2vVnzC3h
ZmwQtQxiu7QvTS/Sp8jjJ/niOPSzlfhphqMxnQAZi/jmJGcZPadT8K+7+PhRllVnI+
TElJarN1ORQu+CaPGhEs9/F7AIcjJNemnVg1cude7EUuO9va8ou49oFExWTLt7YSMl
s+88hxxGu3GugD3eBnitzVo7s7/O9qkIbDUjk3w04/p/VOJ+35Mvi+v/zB9brpYwC1
B4dZP+AhwJDYA==
Received: from smtpclient.apple (ms-asmtp-me-k8s.p00.prod.me.com [17.57.154.37])
by p00-icloudmta-asmtp-us-west-3a-100-percent-10.p00-icloudmta-asmtp-vip.icloud-mail-production.svc.kube.us-west-3a.k8s.cloud.apple.com (Postfix) with ESMTPSA id 8979C18013F8;
Thu, 20 Mar 2025 12:30:05 +0000 (UTC)
From: Marcel Brunel <sender_email_address>
Message-Id: <2E8D69EA-FCA6-4F5D-9D42-22A955C073F6@sender_domain>
Content-Type: multipart/alternative;
boundary="Apple-Mail=_F9AC7D29-8520-4B25-9362-950CB20ADEC5"
Mime-Version: 1.0 (Mac OS X Mail 16.0 (3826.400.131.1.6))
Subject: Re: [EXTERNAL] - Re: Brunel - 2024 taxes
Date: Thu, 20 Mar 2025 07:29:27 -0500
In-Reply-To: <SA0PR18MB350300DE7274C018F66EEA24F2D82@SA0PR18MB3503_namprd18_prod_outlook_com>
To: Troy Womack <recipient_email_address>
References: <SA0PR18MB350314D0B88E283C5C8E1BB6F2DE2@SA0PR18MB3503_namprd18_prod_outlook_com>
<9B337A3E-D373-48C5-816F-C1884BDA6F42@sender_domain>
<SA0PR18MB350341A7172E8632D018A910F2D82@SA0PR18MB3503_namprd18_prod_outlook_com>
<SA0PR18MB350300DE7274C018F66EEA24F2D82@SA0PR18MB3503_namprd18_prod_outlook_com>
X-Mailer: Apple Mail (2.3826.400.131.1.6)
X-Proofpoint-ORIG-GUID: uqebp2OIbPqBr3dYsAxdFVkCNbM5Cxyl
X-Proofpoint-GUID: uqebp2OIbPqBr3dYsAxdFVkCNbM5Cxyl
X-Proofpoint-Virus-Version: vendor=baseguard
engine=ICAP:2.0.293,Aquarius:18.0.1093,Hydra:6.0.680,FMLib:17.12.68.34
definitions=2025-03-20_03,2025-03-19_01,2024-11-22_01
X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 bulkscore=0 clxscore=1030
suspectscore=0 mlxlogscore=999 mlxscore=0 phishscore=0 malwarescore=0
spamscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1
engine=8.19.0-2411120000 definitions=main-2503200077
Topic:
App & System Services
SubTopic:
iCloud & Data
I'm experiencing a crash during a lightweight Core Data migration when a widget that accesses the same database is installed. The migration fails with the following error:
CoreData: error: addPersistentStoreWithType:configuration:URL:options:error: returned error NSCocoaErrorDomain (134100)
error: userInfo:
CoreData: error: userInfo:
error: metadata : {
NSPersistenceFrameworkVersion = 1414;
NSStoreModelVersionChecksumKey = "dY78fBnnOm7gYtb+QT14GVGuEmVlvFSYrb9lWAOMCTs=";
NSStoreModelVersionHashes = {
Entity1 = { ... };
Entity2 = { ... };
Entity3 = { ... };
Entity4 = { ... };
Entity5 = { ... };
};
NSStoreModelVersionHashesDigest = "aOalpc6zSzr/VpduXuWLT8MLQFxSY4kHlBo/nuX0TVQ/EZ+MJ8ye76KYeSfmZStM38VkyeyiIPf4XHQTMZiH5g==";
NSStoreModelVersionHashesVersion = 3;
NSStoreModelVersionIdentifiers = (
""
);
NSStoreType = SQLite;
NSStoreUUID = "9AAA7AB7-18D4-4DE4-9B54-893D08FA7FC4";
"_NSAutoVacuumLevel" = 2;
}
The issue occurs only when the widget is installed. If I remove the widget’s access to the Core Data store, the migration completes successfully. The crash happens only once—after the app is restarted, everything works fine.
This occurs even though I'm using lightweight migration, which should not require manual intervention. My suspicion is that simultaneous access to the Core Data store by both the main app and the widget during migration might be causing the issue.
Has anyone encountered a similar issue? Is there a recommended way to ensure safe migration while still allowing the widget to access Core Data?
Any insights or recommendations would be greatly appreciated.
The app works on a local db but when I try to make it work with iCloud I get errors that I don't understand.
CoreData+CloudKit: -[NSCloudKitMirroringDelegate _performSetupRequest:]_block_invoke(1247): <NSCloudKitMirroringDelegate: 0x10664c200>: Failed to set up CloudKit integration for store: <NSSQLCore: 0x106688140> (URL: file:///var/mobile/Containers/Data/Application/20EF350F-F0FA-4132-97DA-61B60AADB101/Library/Application%20Support/default.store)
<CKError 0x109430e40: "Partial Failure" (2/1011); "Failed to modify some record zones"; uuid = 82ED152A-D015-414D-BB79-AF36E5AF4A8B; container ID = "iCloud.se.Grindegard.MinaRecept"; partial errors: {
com.apple.coredata.cloudkit.zone:defaultOwner = <CKError 0x109431230: "Permission Failure" (10/2007); server message = "Invalid bundle ID for container"; op = E56A3CDA393641F8; uuid = 82ED152A-D015-414D-BB79-AF36E5AF4A8B>
}>
what can be wrong?
Topic:
App & System Services
SubTopic:
iCloud & Data
Hello, thank you Apple for supporting custom store with SwiftData and the Schema type is superb to work with. I have successfully set one up with SQL and have some feedback and issues regarding its APIs.
There’s a highlighted message in the documentation about not using internal restricted symbols directly, but they contradict with the given protocols and I am concerned about breaking any App Store rules. Are we allowed to use these? If not, they should be opened up as they’re useful.
BackingData is required to set up custom snapshots, initialization, and getting/setting values. And I want to use it with createBackingData() to directly initialize instances from snapshots when transferring them between server and client or concurrency.
RelationshipCollection for casting to-many relationships from backing data or checking if an array contains a PersistentModel.
SchemaProperty for type erasure in a collection.
Schema.Relationship has KeyPath properties, but it is missing for Schema.Attribute and Schema.CompositeAttribute. Which means you can’t purely depend on the schema to map data. I am unable to access the properties of a custom struct type in a predicate unless I use Mirror with schemaMetadata() or CustomStringConvertible on the KeyPath directly to extract it.
Trivial, but… the KeyPath property name is inconsistent (it’s all lowercase).
It would be nice to retrieve property names from custom struct types, since you are unable access CodingKeys that are auto synthesized by Codable for structs. But I recently realized they’re a part Schema.CompositeAttribute, however I don’t know how to match these without the KeyPath…
I currently map my entities using CodingKeys to their PredicateCodableKeyPathProviding.… but I wish for a simpler alternative!
It’s unclear how to provide the schema to the snapshot before new models are created.
I currently use a static property, but I want to make it flexible if more schemas and configurations are added later on.
I considered saving and loading the schema in a temporary location, but doubtful that the KeyPath values will be available as they are not Codable.
I suspect schemaMetadata() has the information I need to map the backing data without a schema for snapshots, but as mentioned previously, properties are inaccessible…
Allow access to entity metatypes, like value types from SchemaProperty. They’re useful for getting data out of snapshots and casting them to CodingKeys and PredicateCodableKeyPathProviding. They do not carry over when you provide them in the Schema.
I am unable to retrieve the primary key from PersistentIdentifier.
It seems like once you create one, you can’t get it out, like the DataStoreConfiguration in ModelContainer is not the one you used to set it up. I cannot cast it, it is an entirely different struct?
I have to use JSONSerialization to extract it, but I want to get it directly since it is not a column in my database. It is transformed when it goes to/from my tables.
It’s unknown how to support some schema options, such as Spotlight and CloudKit.
Allow for extending macro options, such as adding options to set as primary key, whether to auto increment, etc…
You can create a schema for super and sub entities, but it doesn’t appear you can actually set them up from the @Model macro or use inheritance on these models…
SwiftData history tracking seems incomplete for HistoryDelete, because that protocol requires HistoryTombstone, but this type cannot be instantiated, nor does it contain anything useful to infer from.
As an aside, I want to create my own custom ModelActor that is a global actor. However, I’m unable to replicate the executor that Apple provides where the executor has a ModelContext, because this type does not conform to Sendable. So how did Apple do this? The documentation doesn’t mention unchecked Sendable, but I figure if the protocol is available then we would be able to set up our own.
And please add concurrency features!
Anyway, I hope for more continued support in the future and I am looking forward to what’s new this WWDC! 😊
Using SwiftData and this is the simplest example I could boil down:
@Model
final class Item {
var timestamp: Date
var tag: Tag?
init(timestamp: Date) {
self.timestamp = timestamp
}
}
@Model
final class Tag {
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
Notice Tag has no reference to Item.
So if I create a bunch of items and set their Tag. Later on I add the ability to delete a Tag. Since I haven't added inverse relationship Item now references a tag that no longer exists so so I get these types of errors:
SwiftData/BackingData.swift:875: Fatal error: This model instance was invalidated because its backing data could no longer be found the store. PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://EEC1D410-F87E-4F1F-B82D-8F2153A0B23C/Tag/p1), implementation: SwiftData.PersistentIdentifierImplementation)
I think I understand now that I just need to add the item reference to Tag and SwiftData will nullify all Item references to that tag when a Tag is deleted.
But, the damage is already done. How can I iterate through all Items that referenced a deleted tag and set them to nil or to a placeholder Tag? Or how can I catch that error and fix it when it comes up?
The crash doesn't occur when loading an Item, only when accessing item.tag?.timestamp, in fact, item.tag?.id is still ok and doesn't crash since it doesn't have to load the backing data.
I've tried things like just looping through all items and setting tag to nil, but saving the model context fails because somewhere in there it still tries to validate the old value.
Thanks!
Hello,
I tried to validate if my app was properly syncing to the cloud. To test this, I created some data in the app, and then deleted the app, and reinstalled. I was expecting the data to still exist but it isn't. Is this a valid test or is the data expected to be deleted when app is deleted?
Topic:
App & System Services
SubTopic:
iCloud & Data
I am trying out the new AttributedString binding with SwiftUI’s TextEditor in iOS26. I need to save this to a Core Data database. Core Data has no AttributedString type, so I set the type of the field to “Transformable”, give it a custom class of NSAttributedString, and set the transformer to NSSecureUnarchiveFromData
When I try to save, I first convert the Swift AttributedString to NSAttributedString, and then save the context. Unfortunately I get this error when saving the context, and the save isn't persisted:
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x600003721140> , <shared NSSecureUnarchiveFromData transformer> threw while encoding a value. with userInfo of (null)
Here's the code that tries to save the attributed string:
struct AttributedDetailView: View {
@ObservedObject var item: Item
@State private var notesText = AttributedString()
var body: some View {
VStack {
TextEditor(text: $notesText)
.padding()
.onChange(of: notesText) {
item.attributedString = NSAttributedString(notesText)
}
}
.onAppear {
if let nsattributed = item.attributedString {
notesText = AttributedString(nsattributed)
} else {
notesText = ""
}
}
.task {
item.attributedString = NSAttributedString(notesText)
do {
try item.managedObjectContext?.save()
} catch {
print("core data save error = \(error)")
}
}
}
}
This is the attribute setup in the Core Data model editor:
Is there a workaround for this?
I filed FB17943846 if someone can take a look.
Thanks.
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?
I am trying to save to cloud kit shared database. The shared database does not allow zones to be set up.
How do I save to sharedCloudDatabase without a zone?
private func addItem(recordType: String, name: String) {
let record = CKRecord(recordType: recordType)
record[Constances.field.name] = name as CKRecordValue
record[Constances.field.done] = false as CKRecordValue
record[Constances.field.priority] = 0 as CKRecordValue
CKContainer.default().sharedCloudDatabase.save(record) { [weak self] returnRecord, error in
if let error = error {
print("Error saving record: \(record[Constances.field.name] as? String ?? "No Name"): \n \(error)")
return
}
}
}
The following error message prints out:
Error saving record: Milk:
<CKError 0x15af87900: "Server Rejected Request" (15/2027); server message = "Default zone is not accessible in shared DB"; op = B085F7BA703D4A08; uuid = 87AEFB09-4386-4E43-81D7-971AAE8BA9E0; container ID = "iCloud.com.sfw-consulting.Family-List">
For a CRM application, I want users to be able to switch between accounts and have their saved contacts stored locally. Whenever a user logs in, the app should fetch data from their specific database location.
What’s the best practice to achieve this?
Should I create a separate database for each user?
Should I store all the data in one database and filter it by user?
Or is there a better approach I should consider?
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?
Hi there, I got two models here:
Two Models, with Many-To-Many Relationship
@Model
final class PresetParams: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
var positionX: Float = 0.0
var positionY: Float = 0.0
var positionZ: Float = 0.0
var volume: Float = 1.0
@Relationship(deleteRule: .nullify, inverse: \Preset.presetAudioParams)
var preset = [Preset]()
init(position: SIMD3<Float>, volume: Float) {
self.positionX = position.x
self.positionY = position.y
self.positionZ = position.z
self.volume = volume
self.preset = []
}
var position: SIMD3<Float> {
get {
return SIMD3<Float>(x: positionX, y: positionY, z: positionZ)
}
set {
positionX = newValue.x
positionY = newValue.y
positionZ = newValue.z
}
}
}
@Model
final class Preset: Identifiable {
@Attribute(.unique) var id: UUID = UUID()
var presetName: String
var presetDesc: String?
var presetAudioParams = [PresetParams]() // Many-To-Many Relationship.
init(presetName: String, presetDesc: String? = nil) {
self.presetName = presetName
self.presetDesc = presetDesc
self.presetAudioParams = []
}
}
To be honest, I don't fully understand how the @Relationship thing works properly in a Many-To-Many relationship situation. Some tutorials suggest that it's required on the "One" side of an One-To-Many Relationship, while the "Many" side doesn't need it.
And then there is an ObservableObject called "ModelActors" to manage all ModelActors, ModelContainer, etc.
ModelActors, ModelContainer...
class ModelActors: ObservableObject {
static let shared: ModelActors = ModelActors()
let sharedModelContainer: ModelContainer
private init() {
var schema = Schema([
// ...
Preset.self,
PresetParams.self,
// ...
])
do {
sharedModelContainer = try ModelContainer(for: schema, migrationPlan: MigrationPlan.self)
} catch {
fatalError("Could not create ModelContainer: \(error.localizedDescription)")
}
}
}
And there is a migrationPlan:
MigrationPlan
// MARK: V102
// typealias ...
// MARK: V101
typealias Preset = AppSchemaV101.Preset
typealias PresetParams = AppSchemaV101.PresetParams
// MARK: V100
// typealias ...
enum MigrationPlan: SchemaMigrationPlan {
static var schemas: [VersionedSchema.Type] {
[
AppSchemaV100.self,
AppSchemaV101.self,
AppSchemaV102.self,
]
}
static var stages: [MigrationStage] {
[AppMigrateV100toV101, AppMigrateV101toV102]
}
static let AppMigrateV100toV101 = MigrationStage.lightweight(fromVersion: AppSchemaV100.self, toVersion: AppSchemaV101.self)
static let AppMigrateV101toV102 = MigrationStage.lightweight(fromVersion: AppSchemaV101.self, toVersion: AppSchemaV102.self)
}
// MARK: Here is the AppSchemaV101
enum AppSchemaV101: VersionedSchema {
static var versionIdentifier: Schema.Version = Schema.Version(1, 0, 1)
static var models: [any PersistentModel.Type] {
return [ // ...
Preset.self,
PresetParams.self
]
}
}
Fails on iOS 18.3.x: "Failed to fulfill link PendingRelationshipLink"
So I expected the SwiftData subsystem to work correctly with version control. A good news is that on iOS 18.1 it does work. But it fails on iOS 18.3.x with a fatal Error:
"SwiftData/SchemaCoreData.swift:581: Fatal error: Failed to fulfill link PendingRelationshipLink(relationshipDescription: (<NSRelationshipDescription: 0x30377fe80>), name preset, isOptional 0, isTransient 0, entity PresetParams, renamingIdentifier preset, validation predicates (), warnings (), versionHashModifier (null)userInfo {}, destination entity Preset, inverseRelationship (null), minCount 0, maxCount 0, isOrdered 0, deleteRule 1, destinationEntityName: "Preset", inverseRelationshipName: Optional("presetAudioParams")), couldn't find inverse relationship 'Preset.presetAudioParams' in model"
Fails on iOS 17.5: Another Error
I tested it on iOS 17.5 and found another issue: Accessing or mutating the "PresetAudioParams" property causes the SwiftData Macro Codes to crash, affecting both Getter and Setter. It fails with an error:
"EXC_BREAKPOINT (code=1, subcode=0x1cc1698ec)"
Tweaking the @Relationship marker and ModelContainer settings didn't fix the problem.
I have a CoreData model with two configuration - but several problems. Notably the viewContext only shows data from the .private configuration. Here is the setup:
The private configuration holds entities, for example, User and Course and the shared one holds entities, for example, Player and League. I setup the NSPersistentStoreDescriptions to use the same container but with a databaseScope of .private/.shared and with the configuration of "Private"/"Shared". loadPersistentStores() does not report an error.
If I try container.initializeCloudKitSchema() only the .private configuration produces CKRecord types. If I create a companion app using one configuration (w/ all entities) the schema initialization creates all CKRecord types AND I can populate some data in the .private and a created CKShare. I see that data in the CloudKit dashboard.
If I axe the companion app and run the real thing w/ two configurations, the viewContext only has the .private data. Why?
If when querying history I use NSPersistentHistoryTransaction.fetchRequest I get a nil return when using two configurations (but non-nil when using one).
Definitely one of the stranger quirks of SwiftData I've come across.
I have a ScriptView that shows Line entities related to a Production, and a TextEnterScriptView that’s presented in a sheet to input text.
I’m noticing that every time I type in the TextEditor within TextEnterScriptView, a new Line shows up in ScriptView — even though I haven’t explicitly inserted it into the modelContext.
I'm quite confused because even though I’m only assigning a new Line to a local @State array in TextEnterScriptView, every keystroke in the TextEditor causes a duplicate Line to appear in ScriptView.
In other words, Why is SwiftData creating new Line entities every time I type in the TextEditor, even though I’m only assigning to a local @State array and not explicitly inserting them into the modelContext?
Here is my minimal reproducible example:
import SwiftData
import SwiftUI
@main
struct testApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: Line.self, isAutosaveEnabled: false)
}
}
}
struct ContentView: View {
@Environment(\.modelContext) var modelContext
@Query(sort: \Production.title) var productions: [Production]
var body: some View {
NavigationStack {
List(productions) { production in
NavigationLink(value: production) {
Text(production.title)
}
}
.navigationDestination(for: Production.self) { production in
ScriptView(production: production)
}
.toolbar {
Button("Add", systemImage: "plus") {
let production = Production(title: "Test \(productions.count + 1)")
modelContext.insert(production)
do {
try modelContext.save()
} catch {
print(error)
}
}
}
.navigationTitle("Productions")
}
}
}
struct ScriptView: View {
@Query private var lines: [Line]
let production: Production
@State private var isShowingSheet: Bool = false
var body: some View {
List {
ForEach(lines) { line in
Text(line.content)
}
}
.toolbar {
Button("Show Sheet") {
isShowingSheet.toggle()
}
}
.sheet(isPresented: $isShowingSheet) {
TextEnterScriptView(production: production)
}
}
}
struct TextEnterScriptView: View {
@Environment(\.dismiss) var dismiss
@State private var text = ""
@State private var lines: [Line] = []
let production: Production
var body: some View {
NavigationStack {
TextEditor(text: $text)
.onChange(of: text, initial: false) {
lines = [Line(content: "test line", production: production)]
}
.toolbar {
Button("Done") {
dismiss()
}
}
}
}
}
@Model
class Production {
@Attribute(.unique) var title: String
@Relationship(deleteRule: .cascade, inverse: \Line.production)
var lines: [Line] = []
init(title: String) {
self.title = title
}
}
@Model
class Line {
var content: String
var production: Production?
init(content: String, production: Production?) {
self.content = content
self.production = production
}
}
CloudKit CKRecordZone Deletion Issue
Problem: CloudKit record zones deleted via CKDatabase.modifyRecordZones(deleting:) or CKModifyRecordZonesOperation are successfully
removed but then reappear. I suspect they are automatically reinstated by CloudKit sync, despite successful deletion confirmation.
Environment:
SwiftData with CloudKit integration
Custom CloudKit zones created for legacy zone-based sharing
Observed Behavior:
Create custom zone (e.g., "TestZone1") via CKDatabase.modifyRecordZones(saving:)
Copy records to zone for sharing purposes
Delete zone using any CloudKit deletion API - returns success, no errors
Immediate verification: Zone is gone from database.allRecordZones()
After SwiftData/CloudKit sync or app restart: Zone reappears
Reproduction:
Tested with three different deletion methods - all exhibit same behaviour:
modifyRecordZones(deleting:) async API
CKModifyRecordZonesOperation (fire-and-forget)
CKModifyRecordZonesOperation with result callbacks
Zone deletion succeeds, change tokens (used to track updates to shared records) cleaned up
But zones are restored presumably by CloudKit background sync
Expected: Deleted zones should remain deleted
Actual: Zones are reinstated, creating orphaned zones