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! 😊
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
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 have not had any successful Schema Migration with CloudKit so far so I'm trying to do with with just very basic attributes, with multiple Versioned Schemas
This is the code in my App Main
var sharedModelContainer: ModelContainer = {
let schema = Schema(versionedSchema: AppSchemaV4.self)
do {
return try ModelContainer(
for: schema,
migrationPlan: AppMigrationPlan.self,
configurations: ModelConfiguration(cloudKitDatabase: .automatic))
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ItemListView()
}
.modelContainer(sharedModelContainer)
}
And this is the code for my MigrationPlan and VersionedSchemas.
typealias Item = AppSchemaV4.Item3
enum AppMigrationPlan: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[AppSchemaV1.self, AppSchemaV2.self, AppSchemaV3.self, AppSchemaV4.self]
}
static var stages: [MigrationStage] {
[migrateV1toV2, migrateV2toV3, migrateV3toV4]
}
static let migrateV1toV2 = MigrationStage.lightweight(
fromVersion: AppSchemaV1.self,
toVersion: AppSchemaV2.self
)
static let migrateV2toV3 = MigrationStage.lightweight(
fromVersion: AppSchemaV2.self,
toVersion: AppSchemaV3.self
)
static let migrateV3toV4 = MigrationStage.custom(
fromVersion: AppSchemaV3.self,
toVersion: AppSchemaV4.self,
willMigrate: nil,
didMigrate: { context in
// Fetch all Item1 instances
let item1Descriptor = FetchDescriptor<AppSchemaV3.Item1>()
let items1 = try context.fetch(item1Descriptor)
// Fetch all Item2 instances
let item2Descriptor = FetchDescriptor<AppSchemaV3.Item2>()
let items2 = try context.fetch(item2Descriptor)
// Convert Item1 to Item3
for item in items1 {
let newItem = AppSchemaV4.Item3(name: item.name, text: "Migrated from Item1 on \(item.date)")
context.insert(newItem)
}
// Convert Item2 to Item3
for item in items2 {
let newItem = AppSchemaV4.Item3(name: item.name, text: "Migrated from Item2 with value \(item.value)")
context.insert(newItem)
}
try? context.save()
}
)
}
enum AppSchemaV1: VersionedSchema {
static var versionIdentifier: Schema.Version = Schema.Version(1, 0, 0)
static var models: [any PersistentModel.Type] {
[Item1.self]
}
@Model class Item1 {
var name: String = ""
init(name: String) {
self.name = name
}
}
}
enum AppSchemaV2: VersionedSchema {
static var versionIdentifier: Schema.Version = Schema.Version(2, 0, 0)
static var models: [any PersistentModel.Type] {
[Item1.self]
}
@Model class Item1 {
var name: String = ""
var date: Date = Date()
init(name: String) {
self.name = name
self.date = Date()
}
}
}
enum AppSchemaV3: VersionedSchema {
static var versionIdentifier: Schema.Version = Schema.Version(3, 0, 0)
static var models: [any PersistentModel.Type] {
[Item1.self, Item2.self]
}
@Model class Item1 {
var name: String = ""
var date: Date = Date()
init(name: String) {
self.name = name
self.date = Date()
}
}
@Model class Item2 {
var name: String = ""
var value: Int = 0
init(name: String, value: Int) {
self.name = name
self.value = value
}
}
}
enum AppSchemaV4: VersionedSchema {
static var versionIdentifier: Schema.Version = Schema.Version(4, 0, 0)
static var models: [any PersistentModel.Type] {
[Item1.self, Item2.self, Item3.self]
}
@Model class Item1 {
var name: String = ""
var date: Date = Date()
init(name: String) {
self.name = name
self.date = Date()
}
}
@Model class Item2 {
var name: String = ""
var value: Int = 0
init(name: String, value: Int) {
self.name = name
self.value = value
}
}
@Model class Item3 {
var name: String = ""
var text: String = ""
init(name: String, text: String) {
self.name = name
self.text = text
}
}
}
My experiment was:
To create Items for every version of the schema
Updating the typealias along the way to reflect the latest Item version.
Updating the Schema in my ModelContainer to reflect the latest Schema Version.
By AppSchemaV4, I have expected all my Items to be displayed/migrated to Item3, but it does not seem to be the case.
I can only see newly created Item3 records.
My question is, is there something wrong with how I'm doing the migrations? or are migrations not really working with CloudKit right now?
I have developed an podcast app, where subscriped podcast & episodes synched with iCloud.
So its working fine with iOS & iPad with latest os version, but iCloud not synching in iPod with version 15.
Please help me to fix this.
Thanks
Devendra K.
The CloudKit Console includes a Unique Users table in the Usage section.
The numbers here are lower than what I would expect. Does this only track a certain percentage of users, e.g. users have opted in to share analytics with developers?
Is it possible to reset SwiftData to a state identical to that of a newly installed app?
I have experienced some migration issues where, when I add a new model, I need to reinstall the entire application for the ModelContainer creation to work.
Deleting all existing models does not seem to make any difference.
A potential solution I currently have, which appears to work but feels quite hacky, is as follows:
let _ = try! ModelContainer()
modelContainer = try! ModelContainer(for: Student.self, ...)
This seems to force out this error CoreData: error: Error: Persistent History (66) has to be truncated due to the following entities being removed: (...) which seems to reset SwiftData.
Any other suggestions?
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
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,
Hello,
When attempting to create a CKShare on a personal device linked to a Family iCloud plan (non-primary account holder), the operation fails with a quotaExceeded error. This occurs with the Family plan having 1.5TB available storage space.
This is also causing a data loss for the object(s) that were attempted to be shared.
Details
Account Type: Family iCloud Plan (2TB total storage)
Current Family Usage: 399GB
iCloud Account Usage: 70 GB
Steps to Reproduce:
Have an iCloud account with storage over the 5GB free space limit.
Be on a part of a iCloud Family Plan as the non-primary account holder.
Have storage space available in the Family Plan
Attempt to start a CloudKit Share/Collaboration on the device.
Observe that the CKShare creation fails with a quotaExceeded error.
Expected Behavior:
The CKShare should be successfully created, reflecting the total available storage of the Family plan.
Observed Behavior:
The CKShare fails to be created with quotaExceeded.
Additional Testing
On a test device using an iCloud account with no stored data, the CKShare was created successfully and shared without issue.
Suspected Cause
The CKShare functionality is verifying the personal storage allocation of the iCloud account and failing without checking total available storage provided by the Family plan.
Hello everyone,
I'm trying to adopt the new Staged Migrations for Core Data and I keep running into an error that I haven't been able to resolve.
The error messages are as follows:
warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass 'Movie' so +entity is unable to disambiguate.
warning: 'Movie' (0x60000350d6b0) from NSManagedObjectModel (0x60000213a8a0) claims 'Movie'.
error: +[Movie entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass
This happens for all of my entities when they are added/fetched. Movie is an abstract entity subclass, and it has the error error: +[Movie entity] Failed to find which is unique to the subclass entities, but this occurs for all entities.
The NSPersistentContainer is loaded only once, and I set the following option after it's loaded:
storeDescription.setOption(
[stages],
forKey: NSPersistentStoreStagedMigrationManagerOptionKey
)
The warnings and errors only appear after I fetch or save to context. It happens regardless of whether the database was migrated or not. In my test project, using the generic NSManagedObject with NSEntityDescription.insertNewObject(forEntityName: "MyEntity", into: context) does not cause the issue. However, using the generic NSManagedObject is not a viable option for my app.
Setting the module to "Current Project Module" doesn't change anything, except that it now prints "claims 'MyModule.Show'" in the warnings. I have verified that there are no other entities with the same name or renameIdentifier.
Has anyone else encountered this issue, or can offer any suggestions on how to resolve it?
Thanks in advance for any help!
I couldn't get iCloud root directory ("iCloud Drive not available or user domain not found."). Anybody had similar issue? I don't want to CloudKit.
Xcode: Version 16.0
I have done below:
Settings > iCloud Backup ON
Settings > iCloud > iCloud Drive ON
Settings > iCloud > MyApp ON
import Foundation
import UIKit
class BackupManager {
var isiCloudEnabled: Bool {
(FileManager.default.ubiquityIdentityToken != nil)
}
// Get the iCloud Drive folder and create a folder if needed
func createFolder() -> URL? {
let fileManager = FileManager.default
// Access the iCloud Drive root directory (user-visible folder)
guard let iCloudRootURL = fileManager.url(forUbiquityContainerIdentifier: nil)?
.appendingPathComponent("Documents") else {
print("iCloud Drive not available or user domain not found.")
return nil
}
// Check if the folder exists in iCloud Drive root, if not, create it
if !fileManager.fileExists(atPath: iCloudRootURL.path) {
do {
try fileManager.createDirectory(at: iCloudRootURL, withIntermediateDirectories: true, attributes: nil)
print("Folder created in iCloud Drive")
} catch {
print("Error creating folder: \(error.localizedDescription)")
return nil
}
}
// Return the URL of the folder in iCloud Drive
return iCloudRootURL
}
}
Topic:
App & System Services
SubTopic:
iCloud & Data
Hi everyone,
I've recently implemented CKSyncEngine in my app, and I have two questions regarding its behavior:
Duplicate FetchedRecordZoneChanges After Sending Changes:
I’ve noticed that the engine sometimes receives a FetchedRecordZoneChanges event containing modifications and deletions that were just sent by the same device a few moments earlier. This event arrives after the SentRecordZoneChanges event, and both events share the same recordChangeTag, which results in double-handling the record.
Is this expected behavior? I’d like to confirm if this is how CKSyncEngine works or if I might be overlooking something.
Handling Initial Sync with a "Sync Screen":
When a user opens the app for the first time and already has data stored in iCloud, I need to display a "Sync Screen" temporarily to prevent showing partial data or triggering abrupt, rapid UI changes.
I’ve found that canceling current operations, then awaiting sendChanges() and fetchChanges() works well to ensure data is fully synced before dismissing the sync screen:
displaySyncScreen = true
await syncEngine.cancelOperations()
try await syncEngine.sendChanges()
try await syncEngine.fetchChanges()
displaySyncScreen = false
However, I’m unsure if canceling operations like this could lead to data loss or other issues. Is this a safe approach, or would you recommend a better strategy for handling this initial sync state?
I have encountered an issue with a customer’s data access after they migrated to a different iCloud account, and I’m looking for guidance.
The Situation:
The customer was logged into their account on my app, which was associated with a specific iCloud account (iCloud A).
They had all their app data available while using iCloud A.
The customer then switched to a new iCloud account (iCloud B) on the same device, while still using the same app account.
After switching iCloud accounts, their data is no longer visible in the app or my CloudKit dashboard.
My Investigation:
I accessed the customer’s CloudKit data via the CloudKit Console, acting as their iCloud account.
I couldn’t find the private database zone or any of their records when accessing iCloud A through the console.
I don’t believe the data was deleted since actions performed under iCloud B shouldn’t affect data stored in iCloud A.
My Hypothesis:
I suspect that the customer’s old iCloud account (iCloud A) may have downgraded or stopped paying for iCloud storage.
If the iCloud subscription is inactive or expired, could that prevent me from accessing their CloudKit data?
Would renewing the iCloud subscription for iCloud A restore access to the missing data?
Questions:
Does an unpaid or expired iCloud account restrict access to CloudKit records, even if they weren’t deleted?
Would paying for iCloud storage again restore the data previously stored in CloudKit?
Is there any way to recover the customer’s CloudKit data if they are unable to access their old iCloud account?
If anyone has a simpler approach to recovering the customer’s iCloud-stored app data or has experience dealing with iCloud migrations like this, I’d appreciate your insights. Thank you in advance for any advice!
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?
The same macOS app is logged into the same iCloud account on two Macs. The apps on both devices can sync data with iCloud, but the data between them is isolated. When I was developing, I just enabled the CloudKit(SwiftData host in iCloud) capability and did not do anything special. I thought that the same app and the same iCloud account should sync the same data between different devices. Why is the cloud data on these two Macs isolated?
I get this message when trying to save my Models.
CoreData: error: SQLCore dispatchRequest: exception handling request: <NSSQLSaveChangesRequestContext: 0x303034540> , I/O error for database at /var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store. SQLite error code:1, 'no such table: ZCALENDARMODEL' with userInfo of {
NSFilePath = "/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store";
NSSQLiteErrorDomain = 1;
}
SwiftData.DefaultStore save failed with error: Error Domain=NSCocoaErrorDomain Code=256 "The file “default.store” couldn’t be opened." UserInfo={NSFilePath=/var/mobile/Containers/Data/Application/726ECA8C-6C67-4BFE-89E7-AFD8A83CAA5D/Library/Application Support/default.store, NSSQLiteErrorDomain=1}
The App has Recipes and Calendars and the user can select a Recipe for each Calendar day. The recipe should not be referenced, it should be saved by SwiftData along with the Calendar.
import SwiftUI
import SwiftData
enum CalendarSource: String, Codable {
case created
case imported
}
@Model
class CalendarModel: Identifiable, Codable {
var id: UUID = UUID()
var name: String
var startDate: Date
var endDate: Date
var recipes: [String: RecipeData] = [:]
var thumbnailData: Data?
var source: CalendarSource?
// Computed Properties
var daysBetween: Int {
let days = Calendar.current.dateComponents([.day], from: startDate.midnight, to: endDate.midnight).day ?? 0
return days + 1
}
var allDates: [Date] {
startDate.midnight.allDates(upTo: endDate.midnight)
}
var thumbnailImage: Image? {
if let data = thumbnailData, let uiImage = UIImage(data: data) {
return Image(uiImage: uiImage)
} else {
return nil
}
}
// Initializer
init(name: String, startDate: Date, endDate: Date, thumbnailData: Data? = nil, source: CalendarSource? = .created) {
self.name = name
self.startDate = startDate
self.endDate = endDate
self.thumbnailData = thumbnailData
self.source = source
}
// Convenience initializer to create a copy of an existing calendar
static func copy(from calendar: CalendarModel) -> CalendarModel {
let copiedCalendar = CalendarModel(
name: calendar.name,
startDate: calendar.startDate,
endDate: calendar.endDate,
thumbnailData: calendar.thumbnailData,
source: calendar.source
)
// Copy recipes
copiedCalendar.recipes = calendar.recipes.mapValues { $0 }
return copiedCalendar
}
// Codable Conformance
private enum CodingKeys: String, CodingKey {
case id, name, startDate, endDate, recipes, thumbnailData, source
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
startDate = try container.decode(Date.self, forKey: .startDate)
endDate = try container.decode(Date.self, forKey: .endDate)
recipes = try container.decode([String: RecipeData].self, forKey: .recipes)
thumbnailData = try container.decodeIfPresent(Data.self, forKey: .thumbnailData)
source = try container.decodeIfPresent(CalendarSource.self, forKey: .source)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(startDate, forKey: .startDate)
try container.encode(endDate, forKey: .endDate)
try container.encode(recipes, forKey: .recipes)
try container.encode(thumbnailData, forKey: .thumbnailData)
try container.encode(source, forKey: .source)
}
}
import SwiftUI
struct RecipeData: Codable, Identifiable {
var id: UUID = UUID()
var name: String
var ingredients: String
var steps: String
var thumbnailData: Data?
// Computed property to convert thumbnail data to a SwiftUI Image
var thumbnailImage: Image? {
if let data = thumbnailData, let uiImage = UIImage(data: data) {
return Image(uiImage: uiImage)
} else {
return nil // No image
}
}
init(recipe: RecipeModel) {
self.name = recipe.name
self.ingredients = recipe.ingredients
self.steps = recipe.steps
self.thumbnailData = recipe.thumbnailData
}
}
import SwiftUI
import SwiftData
@Model
class RecipeModel: Identifiable, Codable {
var id: UUID = UUID()
var name: String
var ingredients: String
var steps: String
var thumbnailData: Data? // Store the image data for the thumbnail
static let fallbackSymbols = ["book.pages.fill", "carrot.fill", "fork.knife", "stove.fill"]
// Computed property to convert thumbnail data to a SwiftUI Image
var thumbnailImage: Image? {
if let data = thumbnailData, let uiImage = UIImage(data: data) {
return Image(uiImage: uiImage)
} else {
return nil // No image
}
}
// MARK: - Initializer
init(name: String, ingredients: String = "", steps: String = "", thumbnailData: Data? = nil) {
self.name = name
self.ingredients = ingredients
self.steps = steps
self.thumbnailData = thumbnailData
}
// MARK: - Copy Function
func copy() -> RecipeModel {
RecipeModel(
name: self.name,
ingredients: self.ingredients,
steps: self.steps,
thumbnailData: self.thumbnailData
)
}
// MARK: - Codable Conformance
private enum CodingKeys: String, CodingKey {
case id, name, ingredients, steps, thumbnailData
}
required init(from decoder: Decoder) throws {
...
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(name, forKey: .name)
try container.encode(ingredients, forKey: .ingredients)
try container.encode(steps, forKey: .steps)
try container.encode(thumbnailData, forKey: .thumbnailData)
}
}
I'm trying to build a custom FetchRequest that I can use outside a View. I've built the following ObservableFetchRequest class based on this article: https://augmentedcode.io/2023/04/03/nsfetchedresultscontroller-wrapper-for-swiftui-view-models
@Observable @MainActor class ObservableFetchRequest<Result: Storable>: NSObject, @preconcurrency NSFetchedResultsControllerDelegate {
private let controller: NSFetchedResultsController<Result.E>
private var results: [Result] = []
init(context: NSManagedObjectContext = .default, predicate: NSPredicate? = Result.E.defaultPredicate(), sortDescriptors: [NSSortDescriptor] = Result.E.sortDescripors) {
guard let request = Result.E.fetchRequest() as? NSFetchRequest<Result.E> else {
fatalError("Failed to create fetch request for \(Result.self)")
}
request.predicate = predicate
request.sortDescriptors = sortDescriptors
controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil)
super.init()
controller.delegate = self
fetch()
}
private func fetch() {
do {
try controller.performFetch()
refresh()
}
catch {
fatalError("Failed to fetch results for \(Result.self)")
}
}
private func refresh() {
results = controller.fetchedObjects?.map { Result($0) } ?? []
}
var predicate: NSPredicate? {
get {
controller.fetchRequest.predicate
}
set {
controller.fetchRequest.predicate = newValue
fetch()
}
}
var sortDescriptors: [NSSortDescriptor] {
get {
controller.fetchRequest.sortDescriptors ?? []
}
set {
controller.fetchRequest.sortDescriptors = newValue.isEmpty ? nil : newValue
fetch()
}
}
internal func controllerDidChangeContent(_ controller: NSFetchedResultsController<any NSFetchRequestResult>) {
refresh()
}
}
Till this point, everything works fine.
Then, I conformed my class to RandomAccessCollection, so I could use in a ForEach loop without having to access the results property.
extension ObservableFetchRequest: @preconcurrency RandomAccessCollection, @preconcurrency MutableCollection {
subscript(position: Index) -> Result {
get {
results[position]
}
set {
results[position] = newValue
}
}
public var endIndex: Index { results.endIndex }
public var indices: Indices { results.indices }
public var startIndex: Index { results.startIndex }
public func distance(from start: Index, to end: Index) -> Int {
results.distance(from: start, to: end)
}
public func index(_ i: Index, offsetBy distance: Int) -> Index {
results.index(i, offsetBy: distance)
}
public func index(_ i: Index, offsetBy distance: Int, limitedBy limit: Index) -> Index? {
results.index(i, offsetBy: distance, limitedBy: limit)
}
public func index(after i: Index) -> Index {
results.index(after: i)
}
public func index(before i: Index) -> Index {
results.index(before: i)
}
public typealias Element = Result
public typealias Index = Int
}
The issue is, when I update the ObservableFetchRequest predicate while searching, it causes a Index out of range error in the Collection subscript because the ForEach loop (or a List loop) access a old version of the array when the item property is optional.
List(request, selection: $selection) { item in
VStack(alignment: .leading) {
Text(item.content)
if let information = item.information { // here's the issue, if I leave this out, everything works
Text(information)
.font(.callout)
.foregroundStyle(.secondary)
}
}
.tag(item.id)
.contextMenu {
if Item.self is Client.Type {
Button("Editar") {
openWindow(ClientView(client: item as! Client), id: item.id!)
}
}
}
}
Is it some RandomAccessCollection issue or a SwiftUI bug?
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).
why not working?
Topic:
App & System Services
SubTopic:
iCloud & Data
When I try to promote schema to production, I get following error:
Cannot promote schema with empty type 'workspace', please delete the record type before attempting to migrate the schema again
However, in hierarchical root record sharing, I think it should be completely legit use case where there is empty root record (in my case workspace) to which other records reference through ->parent reference.
Am I missing something? Is this weird constraint imposed on CloudKit?