Hi
I’m having real problems trying to get a simple “to do” type app working with cloudkit. It works fine with SwiftData but as soon as I add CloudKit I get lots of “container not found errors “ which I think relates to the relationships between my classes. If I strip out the group sort order class it works fine.
I’ve stripped the app back to basics to test - I want to be able to add a “task” (task data) to a “group list “ (group data) also also store the position of the task in that list (group sort order) as there may be lots of tasks in a list. The same task could also be in a different group list with a different position in the list (so another entry and value in group sort order) .. why does the following not work??
any help appreciated!
// TaskData.swift
// TaskOutApp
//
import Foundation
import SwiftData
`@Model
class TaskData: Identifiable, Equatable {
var id = UUID()
var title: String = "No Title"
var isDone: Bool = false
var isToday: Bool = false
var creationDate: Date = Date()
var doneDate: Date = Date()
var todayDate: Date = Date()
// Use an array of GroupSortOrder to maintain both group and sort order
var groupSortOrders: [GroupSortOrder]? = nil
init(id: UUID = UUID(), title: String = "No Title", isDone: Bool = false, isToday: Bool = false, creationDate: Date = Date(), doneDate: Date = Date(), todayDate: Date = Date(), groupSortOrders: [GroupSortOrder]? = nil) {
self.id = id
self.title = title
self.isDone = isDone
self.isToday = isToday
self.creationDate = creationDate
self.doneDate = doneDate
self.todayDate = todayDate
self.groupSortOrders = groupSortOrders
}
static func currentDateString() -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
return formatter.string(from: Date())
}
static func == (lhs: TaskData, rhs: TaskData) -> Bool {
lhs.id == rhs.id
}
}
@Model
class GroupData: Identifiable {
var id = UUID()
var title: String = "no title"
var icon: String = "no icon"
var creationDate: Date = Date()
var task: [TaskData]? = []
init(id: UUID = UUID(), title: String, icon: String, creationDate: Date = Date(), task: [TaskData] = []) {
self.id = id
self.title = title
self.icon = icon
self.creationDate = creationDate
self.task = task
}
static func currentDateString() -> String {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
return formatter.string(from: Date())
}
}
@Model
class GroupSortOrder: Identifiable {
var id = UUID()
var group: GroupData? = nil
var sortOrder: Int = 0
init(id: UUID = UUID(), group: GroupData? = nil, sortOrder: Int = 0) {
self.id = id
self.group = group
self.sortOrder = sortOrder
}
}`
CloudKit
RSS for tagStore structured app and user data in iCloud containers that can be shared by all users of your app using CloudKit.
Posts under CloudKit tag
200 Posts
Sort by:
Post
Replies
Boosts
Views
Activity
Hi, I have a swift data class that has an enum PromptFont as a property.
the data class is hosted in CloudKit.
upon launch, I started getting this error:
below is the property and the enum
when I released this app, there was no error, and it was building and running flawlessly.
when I build this on a simulator (iPadOS 17.5), it runs just fine
I have an app which uses SwiftData and CloudKit all works fine and well. Now I wanted to implement a feature which lets the user know that there are data incoming from the cloud and they have to wait a little bit for the data to show up. Furthermore my app needs to do some data sanitation when it starts up. This sanitation should only be done after the CloudKit updates are processed.
So is there a way that my app can know when CloudKit is doing updates and when it is finished? I was looking for some kind of notification but couldn’t find any info on that.
I don’t need to know which data are updated or process the data. I just want to get notified when a sync starts and when it has ended. Actually it would suffice to know when a sync is finished.
Users will receive a unique ID, if a user enters another user's ID they will go to a view where both have access to the information, being able to change, add, delete...
(Paired, available on App Store)
Public container is not secure, private with ckshare doesn't seem to work for what I would like, plus the content is very confusing
I need something that uses native Apple technologies to build this system.
Hi guys. Can someone please confirm this bug so I report it? The issue is that SwiftData relationships don't update the views in some specific situations on devices running iOS 18 Beta. One clear example is with CloudKit. I created a small example for testing. The following code creates two @models, one to store bands and another to store their records. The following code works with no issues. (You need to connect to a CloudKit container and test it on two devices)
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var records: [Record]
var body: some View {
NavigationStack {
List(records) { record in
VStack(alignment: .leading) {
Text(record.title)
Text(record.band?.name ?? "Undefined")
}
}
.toolbar {
ToolbarItem {
Button("Add Record") {
let randomNumber = Int.random(in: 1...100)
let newBand = Band(name: "New Band \(randomNumber)", records: nil)
modelContext.insert(newBand)
let newRecord = Record(title: "New Record \(randomNumber)", band: newBand)
modelContext.insert(newRecord)
}
}
}
}
}
}
@Model
final class Record {
var title: String = ""
var band: Band?
init(title: String, band: Band?) {
self.title = title
self.band = band
}
}
@Model
final class Band {
var name: String = ""
var records: [Record]?
init(name: String, records: [Record]?) {
self.name = name
self.records = records
}
}
This view includes a button at the top to add a new record associated with a new band. The data appears on both devices, but if you include more views inside the List, the views on the second device are not updated to show the values of the relationships. For example, if you extract the row to a separate view, the second device shows the relationships as "Undefined". You can try the following code.
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var records: [Record]
var body: some View {
NavigationStack {
List {
ForEach(records) { record in
RecordRow(record: record)
}
}
.toolbar {
ToolbarItem {
Button("Add Record") {
let randomNumber = Int.random(in: 1...100)
let newBand = Band(name: "New Band \(randomNumber)", records: nil)
modelContext.insert(newBand)
let newRecord = Record(title: "New Record \(randomNumber)", band: newBand)
modelContext.insert(newRecord)
}
}
}
}
}
}
struct RecordRow: View {
let record: Record
var body: some View {
VStack(alignment: .leading) {
Text(record.title)
Text(record.band?.name ?? "Undefined")
}
}
}
Here I use a ForEach loop and move the row to a separate view. Now on the second device the relationships are nil, so the row shows the text "Undefined" instead of the name of the band.
I attached an image from my iPad. I inserted all the information on my iPhone. The first three rows were inserted with the first view. But the last two rows were inserted after I extracted the rows to a separate view. Here you can see that the relationships are nil and therefore shown as "Undefined". The views are not updated to show the real value of the relationship.
This example shows the issue with CloudKit, but this also happens locally in some situations. The system doesn't detect updates in relationships and therefore doesn't refresh the views.
Please, let me know if you can reproduce the issue. I'm using Mac Sequoia 15.1, and two devices with iOS 18.0.
I am seeking guidance on handling field-level schema changes in CKSyncEngine, specifically when introducing new fields in a subsequent app version.
The current CKSyncEngine documentation (https://developer.apple.com/documentation/cloudkit/cksyncengine) and the GitHub sample (https://github.com/apple/sample-cloudkit-sync-engine) provide clear instructions for managing changes to existing CKRecords. However, I am uncertain about the best approach for handling newly added fields in a new version of my app.
For example:
In version 1 of my app, I have a CKRecord named User with a field called name.
In version 2, I plan to add a new field, phone_number, to the User record. When version 1 (e.g., installed on a user's iPad) and version 2 (e.g., installed on the same user's iPhone) sync using CKSyncEngine, version 1 is unaware of the phone_number field.
My concern is how to ensure version 1 handles this scenario gracefully without blocking other sync events.
Additionally, when version 1 on the iPad is later updated to version 2, there will be no new sync events unless the "phone_number" field is modified again. This could result in the "phone_number" field never being synced to the iPad.
Could you please advise on the best practices for handling such cases to ensure seamless synchronization across different app versions?
Thank you for your assistance.
I have some questions about Apple privacy manifest.
I have a visionOS app called Project Graveyard. I'm getting ready for the visionOS 2 release. Since my last update Apple has started requiring privacy manifest files, but the documentation is extremely vague and I can't tell if I actually need one or not.
My app stores data two types of data for the user.
User Defaults - App settings: lights, rain, window placement etc.
SwiftData + CloudKit - User generated data: a list of project names and some optional text. User customization options for each item.
The data is stored on device or in CloudKit. I do not "collect" this data, it is simply there for the app to function. Do I need a privacy manifest for this type of data? If so, what do I "declare".
Is it ok to have latency about 4 sec? The amount of downloaded data is less than 1 MB. Maybe I need to setup an index for every field requested?
Hi All,
I used to be able to query all my records in dev for years. It seems today i have a bug where I can't query any of my records prior to 7/25/2024. I am able to query the older records using the record name but using the createdtimestamp or any other field i cannot access my records before 7/25/2024.
Anyone else having a similar issue?
I was on vacation last week and when I returned, I discovered that a sizable number of records in my iCloud database had been deleted! I am at a complete loss on this could have occurred. I have several questions:
Is there anyway to contact Apple iCloud support teams?
Does iCould have backups of data?
I have been using the basic NSPersistentContainer with 100k+ records for a while now with no issues. The database size can fluctuate a bit but on average it takes up about 22mb on device.
When I switch the container to NSPersistentCloudKitContainer, I see a massive increase in size to ~150mb initially. As the sync engine uploads records to iCloud it has ballooned to over 600mb on device. On top of that, the user's iCloud usage in settings reports that it takes up 1.7gb in the cloud.
I understand new tables are added and history tracking is enabled but the size increase seems a bit drastic. I'm not sure how we got from 22mb to 1.7gb with the exact same data.
A few other things that are important to note:
I import all the 100k+ records at once when testing the different containers. At the time of the initial import there is only 1 relation (an import group record) that all the records are attached to.
I save the background context only once after all the records and the import group have been made and added to the context.
After the initial import, some of these records may have a few new relations added to them over time. I suppose this could be causing some of the size increase, but its only about 20,000 records that are updated.
None of the records include files/ large binary data.
Most of the attributes are encrypted.
I'm syncing to the dev iCloud environment.
When I do make a change to a single attribute in a record, CloudKit reports that every attribute has been modified (not sure if this is normal or not )
Also, When syncing to a new device, the sync can take hours - days. I'm guessing it's having to sync both the new records and the changes, but it exponentially gets slower as more records are downloaded. The console will show syncing activity, but new records are being added at a slower rate as more records are added. After about 50k records, it grinds to a halt and while the console still shows sync activity, only about 100 records are added every hour.
All this to say i'm very confused where these issues are coming from. I'm sure its a combination of how i've setup my code and the vast record count, record history, etc.
If anyone has any ideas it would be much appreciated.
I am getting the following error:
Failed to save diary entry to CloudKit: <CKError 0x6000035adec0: "Server Rejected Request" (15/2000); op = 10F3CACEA9EC09B6; uuid = 22DDB0B8-9F1B-4BE6-A51B-2ADD08B469B1; container ID = "iCloud.com.domainname.productname">
What should I do to prevent this from happening?
I put the right container name and still this happens.
I was expecting the data model object to save in the iCloud.
I am converting my subscriptions to shouldBadge=NO; and adding silent notifications to implement my own badge count incrementation now that setApplicationIconBadgeNumber: doesn't work. (see [https://stackoverflow.com/questions/47542005/ckmodifybadgeoperation-is-deprecated-in-ios-11-anyone-know-an-alternative-appro]
The problem is that I still have subscriptions with shouldBadge=YES; triggering. I cannot delete those subscriptions because they do not appear in a fetchAllSubscriptionsWithCompletionHandler: . I think I may have deleted the subscriptions from the Development and Production environments on the dashboard and that is why they do not appear in the fetch. But they still exist and are firing over and over again - and setting the badge.
Does anyone know how to delete a subscription that can't be fetched?
We're using Swift Data and syncing it to iCloud.
For the most part this is working.
Our problem is when we delete the App, and install the latest version from TestFlight all our Swift Data Meeting tables are gone. If we create a Meeting on this instance it works, and we can display multiple meetings, but all our prior meetings are gone.
Now if we just build the App in Xcode and overwrite the install, all the prior Swift Data meetings show up, in addition the above created meetings also show up.
If we don't delete the App, and just install the TestFlight build over the Xcode build it also works, all the meetings show up.
So it's as if there are 2 Containers, one for Development and one for Production (the TestFlight build), and they are not sync'd with each other.
I have developed the following two App: app1 and app2. Both App have the function of using iCloud. The iCloud container id of app1 and app2 is different. I use the CloudKit storage function of iCloud.
In the storage management of "Settings", iCloud, the display name of app1 is app2's name, not app1's name. This causes many of our users to delete the iCloud data of the application by mistake, resulting in losses.
Now my question is: What caused the name in app1's iCloud storage management list to be displayed as the name of app2? How should it be solved?
I am receiving:
<CKError 0x30201abb0: "Service Unavailable" (6/2009); "Request failed with http status code 503"; uuid = B6454A02-15FF-4FC1-B124-E5478A9C8BA7; Retry after 28.0 seconds>
This seems to be an issue with all users. What is happening? Seems like CloudKit is down.
I’m working on a project where I’m using CKSyncEngine to sync different types of SwiftData models, specifically User and Organization, to CloudKit. Here’s how I schedule these models to be synced:
For the User model:
let pendingSaves: [CKSyncEngine.PendingRecordZoneChange] = [.saveRecord(user.recordID)]
syncEngine.state.add(pendingRecordZoneChanges: pendingSaves)
For the Organization model:
let pendingSaves: [CKSyncEngine.PendingRecordZoneChange] = [.saveRecord(organization.recordID)]
syncEngine.state.add(pendingRecordZoneChanges: pendingSaves)
The problem arises in my CKSyncEngineDelegate's nextRecordZoneChangeBatch method where from CKRecord.ID alone I need to create the actual CKRecord that will be synced to CloudKit. This recordID alone doesn’t provide enough information to determine 1) in which local model table I need to fetch actual data to build whole CKRecord; and 2) what to put in CKRecord.recordType - whether it’s a User or an Organization.
Question:
What is the best practice for passing or determining the model type (e.g., User or Organization) in nextRecordZoneChangeBatch? How should I handle this in a way that effectively differentiates between the different model types being synced?
Any advice or examples would be greatly appreciated!
Few ideas:
embed the Model type in RecordID.recordName string, but this makes my recordNames longer (like resource_29af3932).
fetch data by recordID in all local persistent storage, but this seems slow and there is constraint that User and Organization IDs should never be the same.
introduce lookup table where from CKRecordID I can look up model type.
Somehow extend CKRecordID to add model type field?
I have a new app I’m working on. I just ran the app on my own phone and noting was put in CloudKit. I get the following error:
com.apple.coredata.cloudkit.zone:__defaultOwner__ = <CKError 0x30305d530: "Permission Failure" (10/2007); server message = "Invalid bundle ID for container"; op = 5D9EC664D6A5C463; uuid = 990B1892-07E6-45C9-B718-0B1BD8DED75A>
}>
So none of my SwiftData models are being transferred up the CloudKit. My bundle ID is: com.tazmancoder.MyHomeInventory. I know that everything is setup correctly cause I have another app using SwiftData and I followed the same setup.
I have done the following and nothing has worked:
Go to https://developer.apple.com and sign in
Select Certificates, Identifiers & Profiles
Select Identifiers (App IDs)
Edit App ID for the app
Uncheck iCloud
Save
Check iCloud
Quit Xcode and Clear DerivedData
Run app
I am not sure why this is happening? Can someone please tell me why this happening and how to fix it?
Thanks,
-Mark/*
My iOS app uses Core Data for local data management and NSPersistentCloudKitContainer to sync data with user’s private iCloud storage. The app has been functioning correctly for several years, but recently, some users have started encountering a CKError.partialFailure error when the app attempts to export data to iCloud. Due to the critical nature of export errors, several features in the app have been disabled to prevent potential data duplication.
Core Data Setup:
lazy var container: NSPersistentContainer = {
let container: NSPersistentContainer
if storeType == .inMemory {
// Used by unit tests
container = NSPersistentContainer(name: "models")
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
} else {
container = NSPersistentCloudKitContainer(name: "models")
}
container.loadPersistentStores { [weak self] _, error in
if let error = error {
self?.logger.error("Failed to load persistent store: \(error)")
fatalError()
}
}
return container
}()
lazy var context: NSManagedObjectContext = {
container.viewContext.name = "main"
container.viewContext.automaticallyMergesChangesFromParent = true
container.viewContext.undoManager = nil
container.viewContext.mergePolicy = NSMergePolicy(merge: .mergeByPropertyObjectTrumpMergePolicyType)
return container.viewContext
}()
Error Handling:
Following the API documentation on partial failures, I have attempted to log the error’s userInfo property for the CKPartialErrorsByItemIDKey. However, the userInfo object appears to be empty:
guard let ckError = SyncMonitor.shared.lastError as? CKError else {
return
}
logger.error("ckError: \(ckError)")
if ckError.code == CKError.partialFailure {
if let dictionary = ckError.userInfo[CKPartialErrorsByItemIDKey] as? NSDictionary {
for (recordID, error) in dictionary {
logger.error("\(recordID): \(error)")
}
}
}
}
This code results in the following log:
ckError: Error Domain=CKErrorDomain Code=2 "(null)"
CloudKit Logs:
Upon reviewing the CloudKit Console logs, I observed two types of errors: QUOTA_EXCEEDED and BAD_REQUEST, both associated with the “returnedRecordTypes” field showing _pcs_data.
Log 1:
{
"time":"17/08/2024, 19:02:14 UTC",
"database":"PRIVATE",
"zone":"com.apple.coredata.cloudkit.zone",
"userId":<redacted>,
"operationId":"14F4FAE7F4B75973",
"operationType":"RecordSave",
"platform":"iPhone",
"clientOS":"iOS;17.5.x",
"overallStatus":"USER_ERROR",
"error":"QUOTA_EXCEEDED",
"requestId":"12EB47C3-08A9-439B-9560-E38C32EE4643",
"executionTimeMs":"259",
"interfaceType":"NATIVE",
"recordInsertBytes":300418,
"recordInsertCount":25,
"returnedRecordTypes":"_pcs_data"
}
Log 2:
{
"time":"17/08/2024, 18:41:31 UTC",
"database":"PRIVATE",
"zone":"com.apple.coredata.cloudkit.zone",
"userId":<redacted>,
"operationId":"8AC0CDC966F6E903",
"operationType":"RecordSave",
"platform":"iPhone",
"clientOS":"iOS;17.5.x",
"overallStatus":"USER_ERROR",
"error":"BAD_REQUEST",
"requestId":"75AC88E2-BFB7-4A41-977D-8E4067A0F40A",
"executionTimeMs":"283",
"interfaceType":"NATIVE",
"returnedRecordTypes":"_pcs_data"
}
It is worth noting that all RecordSave logs containing my app’s data models in the returnedRecordTypes field have been successful. Additionally, I can confirm that users experiencing this error have ample unused iCloud storage.
Despite extensive research on this topic, I have been unable to find relevant information to resolve this issue. It’s unclear whether this _pcs_data error can be ignored, what kind of quota this error refers to, or where I can find more information about how much space my app has consumed. I would greatly appreciate any help in pointing me in the right direction.
I use Core Data and CloudKit in my iOS app, and everything has worked flawlessly so far. I just got a new Mac with an M-chip and now have to run my app in a Rosetta Simulator. iOS17 Rosetta Simulator works great. But iOS16 Rosetta Simulator crashes as soon as I run any CloudKit code, console prints:
[CK] BUG IN CLIENT OF CLOUDKIT: Not entitled to listen to push notifications. Please add the 'aps-connection-initiate' entitlement.
Although I have "Push Notifications" capability enabled in "Signing and Capabilities" of the project.
OK, I open the .entitlements file as a source code and add:
<key>aps-connection-initiate</key>
<true/>
Can confirm, that it started working in the iOS16 Rosetta Simulator. But now I have an error in the Signing and Capabilities:
Provisioning profile "iOS Team Provisioning Profile: com.###" doesn't include the aps-connection-initiate entitlement.
What exactly is this aps-connection-initiate entitlement? And why haven't I needed it ever before? Should I upload it to App Store ASAP or remove it (since my current version works on iOS16 without this entitlement)
Tried searching the web, couldn't find anything about this 'aps-connection-initiate' :'(