I've developed an app that allow me to send messages to all users by entering a record in Public database. On each users device, the app takes that Public record and creates a record in their Private database to track the Read and Deleted status.
It works flawlessly on the simulator and about 1/3 of the devices enrolled in my TestFlight. The other 2/3 are unable to write to their Private database. In fact, the CKContainer.default().privateCloudDatabase.save() operation causes the app to crash if I let it run.
I've looked at every Google link and I just can't figure out why this works for 1/3 of the users, but not the other 2/3. Incidentally, the 2/3 of the group that can't save to their Private database are also unable to save their Notification Subscription.
All devices are running iOS 18.0 or better iPhone 11 through 16 in testing pool. Each portion of the working/not working group are a mix of iPhone versions.
if !found && checkIcloudStatus() {
CKContainer.default().privateCloudDatabase.save(loadPrivateData(n: processedMessages[index].recordName)) { returnedRecord, returnedError in
print("Adding new message to Private and error is: \(String(describing: returnedError))")
DispatchQueue.main.async {
self.processedMessages[index].record = returnedRecord ?? CKRecord(recordType: "messages_private")
self.processedMessages[index].unRead = true
self.processedMessages[index].deleted = false
}
}
}
func loadPrivateData(n: String) -> CKRecord {
let record = CKRecord(recordType: "messages_private")
record["deleted"] = "false"
record["messageId"] = n
record["unRead"] = "true"
return record
}
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
Hi everyone,
We’re currently using CKSyncEngine to sync all our locally persisted data across user devices (iOS and macOS) via iCloud.
We’ve noticed something strange and reproducible:
On iOS, when the CKSyncEngine is initialized with manual sync behavior, both manual calls to fetchChanges() and sendChanges() happen nearly instantly (usually within seconds). Automatic syncing is also very fast.
On macOS, when the CKSyncEngine is initialized with manual sync behavior, fetchChanges() and sendChanges() are also fast and responsive.
However, once CKSyncEngine is initialized with automatic syncing enabled on macOS:
sendChanges() still appears to transmit changes immediately.
But automatic fetching becomes significantly slower — often taking minutes to pick up changes from the cloud, even when new data is already available.
Even manual calls to fetchChanges() behave as if they’re throttled or delayed, rather than performing an immediate fetch.
Our questions:
Is this delay in automatic (and post-automatic manual) fetch behavior on macOS expected, or possibly a bug?
Are there specific macOS constraints that impact CKSyncEngine differently than on iOS?
Once CKSyncEngine has been initialized in automatic mode, is fetchChanges() no longer treated as a truly manual trigger?
Is there a recommended workaround to enable fast sync behavior on macOS — for example, by sticking to manual sync configuration and triggering sync using a CKSubscription-based mechanism when remote changes occur?
Any guidance, clarification, or experiences from other developers (or Apple engineers) would be greatly appreciated — especially regarding maintaining parity between iOS and macOS sync performance.
Thanks in advance!
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?
Hello. I am re-writing our way of storing data into Core Data in our app, so it can be done concurrently.
The solution I opted for is to have a singleton actor that takes an API model, and maps it to a Core Data object and saves it.
For example, to store an API order model, I have something like this:
func store(
order apiOrder: APIOrder,
currentContext: NSManagedObjectContext?
) -> NSManagedObjectID? {
let context = currentContext ?? self.persistentContainer.newBackgroundContext()
// …
}
In the arguments, there is a context you can pass, in case you need to create additional models and relate them to each other. I am not sure this is how you're supposed to do it, but it seemed to work.
From what I've understood of Core Data and using multiple contexts, the appropriate way use them is with context.perform or context.performAndWait.
However, since my storage helper is an actor, @globalActor actor Storage2 { … }, my storage's methods are actor-isolated.
This gives me warnings / errors in Swift 6 when I try to pass the context for to another of my actor's methods.
let context = …
return context.performAndWait {
// …
if let apiBooking = apiOrder.booking {
self.store(booking: apiBooking, context: context)
/* causes warning:
Sending 'context' risks causing data races; this is an error in the Swift 6 language mode
'self'-isolated 'context' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
Access can happen concurrently
*/
}
// …
}
From what I understand this is because my methods are actor-isolated, but the closure of performAndWait does not execute in a thread safe environment.
With all this, what are my options? I've extracted the store(departure:context:) into its own method to avoid duplicated code, but since I can't call it from within performAndWait I am not sure what to do.
Can I ditch the performAndWait? Removing that makes the warning "go away", but I don't feel confident enough with Core Data to know the answer.
I would love to get any feedback on this, hoping to learn!
Hi everyone,
Im trying to set up CloudKit for my Unreal Engine 5.4 project but seem to be hitting some roadblocks on how to set up the Record Types.
From my understanding I need to set up a "file" record type with a "contents" asset field - but even with this it doesn't seem to work :(
Any unreal engine devs with some experience on this who could help me out?
Thanks!
Let's say I have a CloudKit database schema where I have records of type Author that are referenced by multiple records of type Article.
I want to delete an Author record if no Article is referencing it. Now consider the following conflict:
device A deleted the last Article referencing Author #42
device B uploads a new Article referencing Author #42 at the same time
The result should be that Author #42 is not deleted after both operations are finished. But both device don't know from each other changes. So either device B could miss that device A deleted the author. Or device A could have missed that a new Article was uploaded and therefore the Author #42 was deleted right after the upload of device B.
I though about using a reference count first. But this won't work if the ref count is part of the Author record. This is because deletions do not use the changeTag to detect lost updates: If device A found a reference count 0 and decides to delete the Author, it might miss that device B incremented the count meanwhile.
I currently see two alternatives:
Using a second record that outlives the Author to keep the reference count and using an atomic operation to update and delete it. So if the update fails, the delete would fail either.
Always adding a new child record to the Author whenever a reference is made. We could call it ReferenceToken. Since child records may not become dangling, CloudKit would stop a deletion, if a new ReferenceToken sets the parent reference to the Author.
Are there any better ways doing this?
Hello the documentation for message filtering has been offline for a few days now, is it possible to get it back, or is there somewhere else it can be viewed in the meanwhile?
https://developer.apple.com/documentation/sms_and_call_reporting/sms_and_mms_message_filtering
(I just chose topic/tags at random, there aren't any relevant for this)
Testing Environment: iOS 18.4.1 / macOS 15.4.1
I am working on an iOS project that aims to utilize the user's iCloud Drive documents directory to save a specific directory-based file structure. Essentially, the app would create a root directory where the user chooses in iCloud Drive, then it would populate user generated files in various levels of nested directories.
I have been attempting to use NSMetadataQuery with various predicates and search scopes but haven't been able to get it to directly monitor changes to files or directories that are not in the root directory.
Instead, it only monitors files or directories in the root directory, and any changes in a subdirectory are considered an update to the direct children of the root directory.
Example
iCloud Drive Documents (Not app's ubiquity container)
User Created Root Directory (Being monitored)
File A
Directory A
File B
An insertion or deletion within Directory A would only return a notification with userInfo containing data for NSMetadataQueryUpdateChangedItemsKey relating to Directory A, and not the file or directory itself that was inserted or deleted. (Query results array also only contain the direct children.)
I have tried all combinations of these search scopes and predicates with no luck:
query.searchScopes = [
rootDirectoryURL,
NSMetadataQueryUbiquitousDocumentsScope,
NSMetadataQueryAccessibleUbiquitousExternalDocumentsScope,
]
NSPredicate(value: true)
NSPredicate(format: "%K LIKE '*.md'", NSMetadataItemFSNameKey)
NSPredicate(format: "%K BEGINSWITH %@", NSMetadataItemPathKey, url.path(percentEncoded: false))
I do see these warnings in the console upon starting my query:
[CRIT] UNREACHABLE: failed to get container URL for com.apple.CloudDocs
[ERROR] couldn't fetch remote operation IDs: NSError: Cocoa 257 "The file couldn’t be opened because you don’t have permission to view it."
"Error returned from daemon: Error Domain=com.apple.accounts Code=7 "(null)""
But I am not sure what to make of that, since it does act normally for finding updates in the root directory.
Hopefully this isn't a limitation of the API, as the only alternative I could think of would be to have multiple queries running for each nested directory that I needed updates for.
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Files and Storage
iCloud Drive
Foundation
I'm trying to use the new (in tvOS 26) video streaming service automatic login API from the VideoSubscriberAccount framework:
https://developer.apple.com/documentation/videosubscriberaccount/vsuseraccountmanager/autosignintoken-swift.property
It seems that this API requires an entitlement. This document suggests that the com.apple.smoot.subscriptionservice entitlement is required.
https://developer.apple.com/documentation/videosubscriberaccount/signing-people-in-to-media-apps-automatically
However, it seems more likely that com.apple.developer.video-subscriber-single-sign-on is the correct entitlement.
https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.video-subscriber-single-sign-on
Which is the correct entitlement and how do I obtain it?
I don't want to fully comply with the video partner program.
https://developer.apple.com/programs/video-partner/
I just want to use this one new automatic login feature.
I'm developing an app that uses CloudKit synchronization with SwiftData and on visionOS I added an App Settings bundle. I have noticed that sometimes, when the app is open and the user changes a setting from the App Settings bundle, the following fatal error occurs:
SwiftData/BackingData.swift:831: Fatal error: This model instance was destroyed by calling ModelContext.reset and is no longer usable.
The setting is read within the App struct in the visionOS app target using @AppStorage and this value is in turn used to set the passthrough video dimming via the .preferredSurroundingsEffect modifier. The setting allows the user to specify the dimming level as dark, semi dark, or ultra dark.
The fatal error appears to occur intermittently although the first time it was observed was after adding the settings bundle. As such, I suspect there is some connection between those code changes and this fatal error even though they do not directly relate to SwiftData.
In core-data I have a contact and location entity. I have one-to-many relationship from contact to locations and one-to-one from location to contact. I create contact in a seperate view and save it. Later I create a location, fetch the created contact, and save it while specifying the relationship between location and contact contact and test if it actually did it and it works.
viewContext.perform {
do {
// Set relationship using the generated accessor method
currentContact.addToLocations(location)
try viewContext.save()
print("Saved successfully. Locations count:", currentContact.locations?.count ?? 0)
if let locs = currentContact.locations {
print("📍 Contact has \(locs.count) locations.")
for loc in locs {
print("➡️ Location: \(String(describing: (loc as AnyObject).locationName ?? "Unnamed"))")
}
}
} catch {
print("Failed to save location: \(error.localizedDescription)")
}
}
In my NSManagedObject class properties I have this : for Contact:
@NSManaged public var locations: NSSet?
for Location:
@NSManaged public var contact: Contact?
in my persistenceController I have:
for desc in [publicStore, privateStore] {
desc.setOption(true as NSNumber, forKey:
NSPersistentStoreRemoteChangeNotificationPostOptionKey)
desc.setOption(true as NSNumber, forKey: NSPersistentHistoryTrackingKey)
desc.setOption(true as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
desc.setOption(true as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
desc.setOption(true as NSNumber, forKey: "CKSyncCoreDataDebug") // Optional: Debug sync
// Add these critical options for relationship sync
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitEnforceRecordExistsKey")
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreCloudKitMaintainReferentialIntegrityKey")
// Add this specific option to force schema update
desc.setOption(true as NSNumber, forKey: "NSPersistentStoreRemoteStoreUseCloudKitSchemaKey")
}
When synchronization happens on CloudKit side, it creates CKRecords: CD_Contact and CD_Location. However for CD_Location it creates the relationship CD_contact as a string and references the CD_Contact. This I thought should have come as REFERENCE On the CD_Contact there is no CD_locations field at all. I do see the relationships being printed on coredata side but it does not come as REFERENCE on cloudkit. Spent over a day on this. Is this normal, what am I doing wrong here? Can someone advise?
I'm using SwiftData to persist my items in storage. I used .modelContext to pass in my shared context, and on iOS 18 (both on a physical device and a simulator), I discovered a bug where SwiftData doesn't automatically save my data. For example, I could add a new item, go to the next screen, change something that reloads a previous screen, and SwiftData just forgets the item that I added. Please find the fully working code attached.
While writing this post, I realized that if I use .modelContainer instead of .modelContext, the issue is solved. So I have two questions:
It seems like .modelContainer is the go-to option when working with SwiftData, but why did an issue occur when I used .modelContext and passed in a shared container? When should we use .modelContext over .modelContainer?
What was the bug? It's working fine in iOS 17, but not in iOS 18. Or is this expected?
Here's the fully working code so you can copy and paste:
import SwiftUI
import SwiftData
typealias NamedColor = (color: Color, name: String)
extension Color {
init(r: Double, g: Double, b: Double) {
self.init(red: r/255, green: g/255, blue: b/255)
}
static let namedColors: [NamedColor] = [
(.blue, "Blue"),
(.red, "Red"),
(.green, "Green"),
(.orange, "Orange"),
(.yellow, "Yellow"),
(.pink, "Pink"),
(.purple, "Purple"),
(.teal, "Teal"),
(.indigo, "Indigo"),
(.brown, "Brown"),
(.cyan, "Cyan"),
(.gray, "Gray")
]
static func name(for color: Color) -> String {
return namedColors.first(where: { $0.color == color })?.name ?? "Blue"
}
static func color(for name: String) -> Color {
return namedColors.first(where: { $0.name == name })?.color ?? .blue
}
}
@main
struct SwiftDataTestApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
@AppStorage("accentColor") private var accentColorName: String = "Blue"
var body: some Scene {
WindowGroup {
NavigationStack {
HomeView()
}
.tint(Color.color(for: accentColorName))
}
.modelContainer(sharedModelContainer) // This works
// .modelContext(ModelContext(sharedModelContainer)) // This doesn't work
}
}
@Model
final class Item {
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
struct HomeView: View {
@State private var showSettings = false
@Environment(\.modelContext) var modelContext
@AppStorage("accentColor") private var accentColorName: String = "Blue"
@Query private var items: [Item]
var body: some View {
List {
ForEach(items) { item in
NavigationLink {
Text("Item at \(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))")
} label: {
Text(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))
}
}
Button {
withAnimation {
let newItem = Item(timestamp: Date())
modelContext.insert(newItem)
}
} label: {
Image(systemName: "plus")
.frame(maxWidth: .infinity)
.frame(maxHeight: .infinity)
}
}
.navigationTitle("Habits")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: { showSettings = true }) {
Label("", systemImage: "gearshape.fill")
}
}
}
.navigationDestination(isPresented: $showSettings) {
colorPickerView
}
}
private var colorPickerView: some View {
Form {
Section(header: Text("Accent Color")) {
Picker("Accent Color", selection: $accentColorName) {
ForEach(Color.namedColors, id: \.name) { namedColor in
Text(namedColor.name)
.tag(namedColor.name)
.foregroundColor(namedColor.color)
}
}
.pickerStyle(.wheel)
}
}
.navigationTitle("Settings")
}
}
why not working?
Topic:
App & System Services
SubTopic:
iCloud & Data
Using this Apple repository as a basis
https://github.com/apple/sample-cloudkit-zonesharing
I created and verified the shared zone and the same zone is private for the person who shared it and shared for the person who received it, so aren't they the same zones?
[same zone but different id?]
I can make the person who shared the zone (owner) access the zone as a .shared scope just like the person who was shared.
I am trying to extend my PersistedModels like so:
@Versioned(3)
@Model
class MyType {
var name: String
init() { name = "hello" }
}
but it seems that SwiftData's@Model macro is unable to read the properties added by my @Versioned macro. I have tried changing the order and it ignores them regardless. version is not added to schemaMetadata and version needs to be persisted. I was planning on using this approach to add multiple capabilities to my model types. Is this possible to do with macros?
VersionedMacro
/// A macro that automatically implements VersionedModel protocol
public struct VersionedMacro: MemberMacro, ExtensionMacro {
// Member macro to add the stored property directly to the type
public static func expansion(
of node: AttributeSyntax,
providingMembersOf declaration: some DeclGroupSyntax,
in context: some MacroExpansionContext
) throws -> [DeclSyntax] {
guard let argumentList = node.arguments?.as(LabeledExprListSyntax.self),
let firstArgument = argumentList.first?.expression else {
throw MacroExpansionErrorMessage("@Versioned requires a version number, e.g. @Versioned(3)")
}
let versionValue = firstArgument.description.trimmingCharacters(in: .whitespaces)
// Add the stored property with the version value
return [
"public private(set) var version: Int = \(raw: versionValue)"
]
}
// Extension macro to add static property
public static func expansion(
of node: SwiftSyntax.AttributeSyntax,
attachedTo declaration: some SwiftSyntax.DeclGroupSyntax,
providingExtensionsOf type: some SwiftSyntax.TypeSyntaxProtocol,
conformingTo protocols: [SwiftSyntax.TypeSyntax],
in context: some SwiftSyntaxMacros.MacroExpansionContext
) throws -> [SwiftSyntax.ExtensionDeclSyntax] {
guard let argumentList = node.arguments?.as(LabeledExprListSyntax.self),
let firstArgument = argumentList.first?.expression else {
throw MacroExpansionErrorMessage("@Versioned requires a version number, e.g. @Versioned(3)")
}
let versionValue = firstArgument.description.trimmingCharacters(in: .whitespaces)
// We need to explicitly add the conformance in the extension
let ext = try ExtensionDeclSyntax("extension \(type): VersionedModel {}")
.with(\.memberBlock.members, MemberBlockItemListSyntax {
MemberBlockItemSyntax(decl: DeclSyntax(
"public static var version: Int { \(raw: versionValue) }"
))
})
return [ext]
}
}
VersionedModel
public protocol VersionedModel: PersistentModel {
/// The version of this particular instance
var version: Int { get }
/// The type's current version
static var version: Int { get }
}
Macro Expansion:
Hello,
I'm planning to had an onboarding to one of my apps. I am thinking about a way for a user to not see the onboarding again if he installs the app on another device. So for example, the user completes the onboarding on its iPhone, then downloads the app on its iPad and launch it, he doesn't see the onboarding a second time.
I thought about using iCloud NSUbiquitousKeyValueStored to store the onboarding completion state.
But I'm not sure when the data is synced to the other device logged into the same Apple account:
Immediately even if the app is not installed on the other device (independent from the app, only iCloud thing)?
At the same time as the app install on the other device?
After the app is first launched on the other device?
Of course synchronisation will depend on the Internet connection, speed, etc. so the app should handle the case where the data is not here but what would be the best case scenario?
Thank you,
Axel
Topic:
App & System Services
SubTopic:
iCloud & Data
There's some logic in my app that first checks to see if a specific CloudKit record zone exists. If it doesn't, it creates the zone, and then my application continues on with its work.
The way I've implemented this right now is by catching the zoneNotFound error when I call CKDatabase#recordZone(for:) (docs) and creating the zone when that happens:
do {
try await db.recordZone(for: zoneID)
} catch let ckError as CKError
where [.zoneNotFound, .userDeletedZone].contains(ckError.code)
{
// createZone is a helper function
try await createZone(zoneID: zoneID, context: context)
}
This works great, but every time I do this, an error is logged in CloudKit Console, which creates a lot of noise and makes it harder to see real errors.
Is there a way to do this without explicitly triggering a CloudKit error?
I just found CKDatabase#recordZones(for:) (docs), which seems like it returns an empty array instead of throwing an error if the zone doesn't exist.
Will calling that and looking for a non-empty array work just as well, but without logging lots of errors in the console?
I have a Live Activity with a button that updates a SwiftData model. This used to work in iOS 17, but not on iOS 18. The reason is that in iOS 17, when you run an AppIntent from a Live Activity, the perform() method would run in the main app's process, meaning it had access to the app's ModelContainer/ModelContext. However, in iOS 18, this is no longer the case, and the perform() method of an AppIntent now runs in the extension's process.
While I can still construct a new ModelContainer & ModelContext in the AppIntent's perform() method, the main app's container and context will not see these changes until the app is relaunched.
How can I make this work in iOS 18 now that an AppIntent executed from an extension runs in a different process from the main app?
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
Extensions
ActivityKit
App Intents
SwiftData
In the CloudKit logs I see logs that suggest users getting QUOTA_EXCEEDED error for RecordDelete operations.
{
"time":"21/07/2025, 7:57:46 UTC"
"database":"PRIVATE"
"zone":"***"
"userId":"***"
"operationId":"***"
"operationGroupName":"2.3.3(185)"
"operationType":"RecordDelete"
"platform":"iPhone"
"clientOS":"iOS;18.5"
"overallStatus":"USER_ERROR"
"error":"QUOTA_EXCEEDED"
"requestId":"***"
"executionTimeMs":"177"
"interfaceType":"NATIVE"
"recordInsertBytes":54352
"recordInsertCount":40
"returnedRecordTypes":"_pcs_data"
}
I'm confused as to what this means? Why would a RecordDelete operation have recordInsertBytes? I'd expect a RecordDelete operation to never fail on quotaExceeded and how would I handle that in the app?
I'm calling a method with the context as parameter, within the context's perform block – is this really not legal in Swift 6?
actor MyActor {
func bar(context: NSManagedObjectContext) { /* some code */ }
func foo(context: NSManagedObjectContext) {
context.performAndWait {
self.bar(context: context)
// WARN: Sending 'context' risks causing data races; this is an error in the Swift 6 language mode
// 'self'-isolated 'context' is captured by a actor-isolated closure. actor-isolated uses in closure may race against later nonisolated uses
// Access can happen concurrently
}
}
}
The warning appears when I call a method with a context parameter, within the performAndWait-block.
Background: In my app I have methods that takes in API data, and I need to call the same methods from multiple places with the same context to store it, and I do not want to copy paste the code and have hundreds of lines of duplicate code.
Is there a well-known "this is how you should do it" for situations like this?
This is related to a previous post I made, but it's a bit flimsy and got no response: https://developer.apple.com/forums/thread/770605