Hi,
I did cloudkit synchronization using swiftdata.
However, synchronization does not occur automatically, and synchronization occurs intermittently only when the device is closed and opened.
For confirmation, after changing the data in Device 1 (saving), when the data is fetched from Device 2, there is no change.
I've heard that there's still an issue with swiftdata sync and Apple is currently troubleshooting it, is the phenomenon I'm experiencing in the current version normal?
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
Created
Hello,
SwiftData is not working correctly with Swift Concurrency. And it’s sad after all this time.
I personally found a regression. The attached code works perfectly fine on iOS 17.5 but doesn’t work correctly on iOS 18 or iOS 18.1.
A model can be updated from the background (Task, Task.detached or ModelActor) and refreshes the UI, but as soon as the same item is updated from the View (fetched via a Query), the next background updates are not reflected anymore in the UI, the UI is not refreshed, the updates are not merged into the main.
How to reproduce:
Launch the app
Tap the plus button in the navigation bar to create a new item
Tap on the “Update from Task”, “Update from Detached Task”, “Update from ModelActor” many times
Notice the time is updated
Tap on the “Update from View” (once or many times)
Notice the time is updated
Tap again on “Update from Task”, “Update from Detached Task”, “Update from ModelActor” many times
Notice that the time is not update anymore
Am I doing something wrong? Or is this a bug in iOS 18/18.1?
Many other posts talk about issues where updates from background thread are not merged into the main thread. I don’t know if they all are related but it would be nice to have
1/ bug fixed, meaning that if I update an item from a background, it’s reflected in the UI, and
2/ proper documentation on how to use SwiftData with Swift Concurrency (ModelActor). I don’t know if what I’m doing in my buttons is correct or not.
Thanks,
Axel
import SwiftData
import SwiftUI
@main
struct FB_SwiftData_BackgroundApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.modelContainer(for: Item.self)
}
}
}
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@State private var simpleModelActor: SimpleModelActor!
@Query private var items: [Item]
var body: some View {
NavigationView {
VStack {
if let firstItem: Item = items.first {
Text(firstItem.timestamp, format: Date.FormatStyle(date: .omitted, time: .standard))
.font(.largeTitle)
.fontWeight(.heavy)
Button("Update from Task") {
let modelContainer: ModelContainer = modelContext.container
let itemID: Item.ID = firstItem.persistentModelID
Task {
let context: ModelContext = ModelContext(modelContainer)
guard let itemInContext: Item = context.model(for: itemID) as? Item else { return }
itemInContext.timestamp = Date.now.addingTimeInterval(.random(in: 0...2000))
try context.save()
}
}
.buttonStyle(.bordered)
Button("Update from Detached Task") {
let container: ModelContainer = modelContext.container
let itemID: Item.ID = firstItem.persistentModelID
Task.detached {
let context: ModelContext = ModelContext(container)
guard let itemInContext: Item = context.model(for: itemID) as? Item else { return }
itemInContext.timestamp = Date.now.addingTimeInterval(.random(in: 0...2000))
try context.save()
}
}
.buttonStyle(.bordered)
Button("Update from ModelActor") {
let container: ModelContainer = modelContext.container
let persistentModelID: Item.ID = firstItem.persistentModelID
Task.detached {
let actor: SimpleModelActor = SimpleModelActor(modelContainer: container)
await actor.updateItem(identifier: persistentModelID)
}
}
.buttonStyle(.bordered)
Button("Update from ModelActor in State") {
let container: ModelContainer = modelContext.container
let persistentModelID: Item.ID = firstItem.persistentModelID
Task.detached {
let actor: SimpleModelActor = SimpleModelActor(modelContainer: container)
await MainActor.run {
simpleModelActor = actor
}
await actor.updateItem(identifier: persistentModelID)
}
}
.buttonStyle(.bordered)
Divider()
.padding(.vertical)
Button("Update from View") {
firstItem.timestamp = Date.now.addingTimeInterval(.random(in: 0...2000))
}
.buttonStyle(.bordered)
} else {
ContentUnavailableView(
"No Data",
systemImage: "slash.circle", //
description: Text("Tap the plus button in the toolbar")
)
}
}
.toolbar {
ToolbarItem(placement: .primaryAction) {
Button(action: addItem) {
Label("Add Item", systemImage: "plus")
}
}
}
}
}
private func addItem() {
modelContext.insert(Item(timestamp: Date.now))
try? modelContext.save()
}
}
@ModelActor
final actor SimpleModelActor {
var context: String = ""
func updateItem(identifier: Item.ID) {
guard let item = self[identifier, as: Item.self] else {
return
}
item.timestamp = Date.now.addingTimeInterval(.random(in: 0...2000))
try! modelContext.save()
}
}
@Model
final class Item: Identifiable {
var timestamp: Date
init(timestamp: Date) {
self.timestamp = timestamp
}
}
I'm using NSPersistentCloudKitContainer to save, edit, and delete items, but it only works half of the time. When I delete an item and terminate the app and repoen, sometimes the item is still there and sometimes it isn't. The operations are simple enough:
moc.delete(thing)
try? moc.save()
Here is my DataController. I'm happy to provide more info as needed
class DataController: ObservableObject {
let container: NSPersistentCloudKitContainer
@Published var moc: NSManagedObjectContext
init() {
container = NSPersistentCloudKitContainer(name: "AppName")
container.loadPersistentStores { description, error in
if let error = error {
print("Core Data failed to load: \(error.localizedDescription)")
}
}
#if DEBUG
do {
try container.initializeCloudKitSchema(options: [])
} catch {
print("Error initializing CloudKit schema: \(error.localizedDescription)")
}
#endif
moc = container.viewContext
}
}
I'm getting a crash in SwiftData but only on one specific device (iPhone 16 pro running 18.2 22C5131e) and not on an ipad or simulator
I cant troubleshoot this crash and its quite frustrating, all I am getting is
@Query(sort: \Todo.timestamp, order: .reverse) private var todos: [Todo]
ForEach(todos.filter { !$0.completed }) { item in // <---crash
TodoListView()
}
and the error is
Thread 1: signal SIGABRT
An abort signal terminated the process. Such crashes often happen because of an uncaught exception or unrecoverable error or calling the abort() function.
and
_SwiftData_SwiftUI.Query.wrappedValue.getter : τ_0_1
-> 0x105b98b58 <+160>: ldur x8, [x29, #-0x40]
0x105b98b5c <+164>: ldur x0, [x29, #-0x38]
0x105b98b60 <+168>: ldur x1, [x29, #-0x30]
0x105b98b64 <+172>: ldur x9, [x29, #-0x20]
0x105b98b68 <+176>: stur x9, [x29, #-0x28]
0x105b98b6c <+180>: ldr x8, [x8, #0x8]
0x105b98b70 <+184>: blr x8
0x105b98b74 <+188>: ldur x0, [x29, #-0x28]
0x105b98b78 <+192>: sub sp, x29, #0x10
0x105b98b7c <+196>: ldp x29, x30, [sp, #0x10]
0x105b98b80 <+200>: ldp x20, x19, [sp], #0x20
0x105b98b84 <+204>: ret
How do I fix this?
I've been trying to setup a successful migration, but it keeps failing with this error:
NSCloudKitMirroringDelegate are not reusable and should have a lifecycle tied to a given instance of NSPersistentStore.
I can't find any information about this online. I added breakpoints throughout the code in willMigrate, and it originally failed on this line:
try? context.save()
I removed that, and it still failed. After I reload the app, it doesn't run the migration again and the app loads successfully. I figured since it crashed, it would keep trying, but I guess not. Here's how my migration is setup.
enum MigrationV1ToV2: SchemaMigrationPlan {
static var schemas: [any VersionedSchema.Type] {
[SchemaV1.self, SchemaV2.self]
}
static var stages: [MigrationStage] {
[stage]
}
static let stage = MigrationStage.custom(
fromVersion: SchemaV1.self,
toVersion: SchemaV2.self,
willMigrate: { context in
// Get cycles
let cycles = try? context.fetch(FetchDescriptor<SchemaV1.Cycle>())
if let cycles {
for cycle in cycles {
// Create new recurring objects based on what's in the cycle
for income in cycle.income {
let recurring = SchemaV2.Recurring(name: income.name, frequency: income.frequency, kind: .income)
recurring.addAmount(.init(date: cycle.startDate, amount: income.amount))
context.insert(recurring)
}
for expense in cycle.expenses {
let recurring = SchemaV2.Recurring(name: expense.name, frequency: expense.frequency, kind: .expense)
recurring.addAmount(.init(date: cycle.startDate, amount: expense.amount))
context.insert(recurring)
}
for savings in cycle.savings {
let recurring = SchemaV2.Recurring(name: savings.name, frequency: savings.frequency, kind: .savings)
recurring.addAmount(.init(date: cycle.startDate, amount: savings.amount))
context.insert(recurring)
}
for investment in cycle.investments {
let recurring = SchemaV2.Recurring(name: investment.name, frequency: investment.frequency, kind: .investment)
recurring.addAmount(.init(date: cycle.startDate, amount: investment.amount))
context.insert(recurring)
}
}
//try? context.save()
} else {
print("The cycles were not able to be fetched.")
}
},
didMigrate: { context in
// Get new recurring objects
let newRecurring = try? context.fetch(FetchDescriptor<SchemaV2.Recurring>())
if let newRecurring {
for recurring in newRecurring {
// Get all recurring with the same name and kind
let sameName = newRecurring.filter({ $0.name == recurring.name && $0.kind == recurring.kind })
// Add amount history to recurring object, and then remove matching
for match in sameName {
recurring.amountHistory.append(contentsOf: match.amountHistory)
context.delete(match)
}
}
//try? context.save()
} else {
print("The new recurring objects could not be fetched.")
}
}
)
}
Here's is my modelContainer in the app file. There is a fatal error occurring here that's crashing the app.
var sharedModelContainer: ModelContainer = {
let schema = Schema(versionedSchema: SchemaV2.self)
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: false)
do {
return try ModelContainer(
for: schema,
migrationPlan: MigrationV1ToV2.self,
configurations: [modelConfiguration]
)
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
Does anyone have any suggestions for this?
EDIT:
I found this error in the console that may be relevant.
BUG IN CLIENT OF CLOUDKIT: Registering a handler for a CKScheduler activity identifier that has already been registered (com.apple.coredata.cloudkit.activity.export.8F7A1261-4324-40B4-B041-886DF36FBF0A).
CloudKit setup failed because it couldn't register a handler for the export activity. There is another instance of this persistent store actively syncing with CloudKit in this process.
And here is the fatal error
Fatal error: Could not create ModelContainer: SwiftDataError(_error: SwiftData.SwiftDataError._Error.loadIssueModelContainer, _explanation: nil)
I am trying to add a custom JSON DataStore and DataStoreConfiguration for SwiftData. Apple kindly provided some sample code in the WWDC24 session, "Create a custom data store with SwiftData", and (once updated for API changes since WWDC) that works fine.
However, when I try to add a relationship between two classes, it fails. Has anyone successfully made a JSONDataStore with a relationship?
Here's my code; firstly the cleaned up code from the WWDC session:
import SwiftData
final class JSONStoreConfiguration: DataStoreConfiguration {
typealias Store = JSONStore
var name: String
var schema: Schema?
var fileURL: URL
init(name: String, schema: Schema? = nil, fileURL: URL) {
self.name = name
self.schema = schema
self.fileURL = fileURL
}
static func == (lhs: JSONStoreConfiguration, rhs: JSONStoreConfiguration) -> Bool {
return lhs.name == rhs.name
}
func hash(into hasher: inout Hasher) {
hasher.combine(name)
}
}
final class JSONStore: DataStore {
typealias Configuration = JSONStoreConfiguration
typealias Snapshot = DefaultSnapshot
var configuration: JSONStoreConfiguration
var name: String
var schema: Schema
var identifier: String
init(_ configuration: JSONStoreConfiguration, migrationPlan: (any SchemaMigrationPlan.Type)?) throws {
self.configuration = configuration
self.name = configuration.name
self.schema = configuration.schema!
self.identifier = configuration.fileURL.lastPathComponent
}
func save(_ request: DataStoreSaveChangesRequest<DefaultSnapshot>) throws -> DataStoreSaveChangesResult<DefaultSnapshot> {
var remappedIdentifiers = [PersistentIdentifier: PersistentIdentifier]()
var serializedData = try read()
for snapshot in request.inserted {
let permanentIdentifier = try PersistentIdentifier.identifier(for: identifier,
entityName: snapshot.persistentIdentifier.entityName,
primaryKey: UUID())
let permanentSnapshot = snapshot.copy(persistentIdentifier: permanentIdentifier)
serializedData[permanentIdentifier] = permanentSnapshot
remappedIdentifiers[snapshot.persistentIdentifier] = permanentIdentifier
}
for snapshot in request.updated {
serializedData[snapshot.persistentIdentifier] = snapshot
}
for snapshot in request.deleted {
serializedData[snapshot.persistentIdentifier] = nil
}
try write(serializedData)
return DataStoreSaveChangesResult<DefaultSnapshot>(for: self.identifier, remappedIdentifiers: remappedIdentifiers)
}
func fetch<T>(_ request: DataStoreFetchRequest<T>) throws -> DataStoreFetchResult<T, DefaultSnapshot> where T : PersistentModel {
if request.descriptor.predicate != nil {
throw DataStoreError.preferInMemoryFilter
} else if request.descriptor.sortBy.count > 0 {
throw DataStoreError.preferInMemorySort
}
let objs = try read()
let snapshots = objs.values.map({ $0 })
return DataStoreFetchResult(descriptor: request.descriptor, fetchedSnapshots: snapshots, relatedSnapshots: objs)
}
func read() throws -> [PersistentIdentifier : DefaultSnapshot] {
if FileManager.default.fileExists(atPath: configuration.fileURL.path(percentEncoded: false)) {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let data = try decoder.decode([DefaultSnapshot].self, from: try Data(contentsOf: configuration.fileURL))
var result = [PersistentIdentifier: DefaultSnapshot]()
data.forEach { s in
result[s.persistentIdentifier] = s
}
return result
} else {
return [:]
}
}
func write(_ data: [PersistentIdentifier : DefaultSnapshot]) throws {
let encoder = JSONEncoder()
encoder.dateEncodingStrategy = .iso8601
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
let jsonData = try encoder.encode(data.values.map({ $0 }))
try jsonData.write(to: configuration.fileURL)
}
}
The data model classes:
import SwiftData
@Model
class Settings {
private(set) var version = 1
@Relationship(deleteRule: .cascade) var hack: Hack? = Hack()
init() {
}
}
@Model
class Hack {
var foo = "Foo"
var bar = 42
init() {
}
}
Container:
lazy var mainContainer: ModelContainer = {
do {
let url = // URL to file
let configuration = JSONStoreConfiguration(name: "Settings", schema: Schema([Settings.self, Hack.self]), fileURL: url)
return try ModelContainer(for: Settings.self, Hack.self, configurations: configuration)
}
catch {
fatalError("Container error: \(error.localizedDescription)")
}
}()
Load function, that saves a new Settings JSON file if there isn't an existing one:
@MainActor func loadSettings() {
let mainContext = mainContainer.mainContext
let descriptor = FetchDescriptor<Settings>()
let settingsArray = try? mainContext.fetch(descriptor)
print("\(settingsArray?.count ?? 0) settings found")
if let settingsArray, let settings = settingsArray.last {
print("Loaded")
} else {
let settings = Settings()
mainContext.insert(settings)
do {
try mainContext.save()
} catch {
print("Error saving settings: \(error)")
}
}
}
The save operation creates a JSON file, which while it isn't a format I would choose, is acceptable, though I notice that the "hack" property (the relationship) doesn't have the correct identifier.
When I run the app again to load the data, I get an error (that there wasn't room to include in this post).
Even if I change Apple's code to not assign a new identifier, so the relationship property and its pointee have the same identifier, it still doesn't load.
Am I doing something obviously wrong, or are relationships not supported in custom data stores?
When a user first downloads my application they are prompted to sign into their apple account via a pop up.
I have not had this pop up previously, I believe the change occurred after iOS18.
I have functions that do a few things:
Retrieves userRecordID
Retrieves a userprofile(via userrecordid) from cloudkit.
Hello, on every Apple device, iPhone/Watch I have the option to install developer beta versions. I would like to unsubscribe from developer beta versions - not have them in settings. I know that developer beta is assigned to my iCloud account. After logging out and restoring factory settings, the tab disappears. When I log into my iCloud account, it appears everywhere. I did not sign up for developer beta. How do I remove it?
Topic:
App & System Services
SubTopic:
iCloud & Data
Hi everyone,
I am a beginner in iOS/Swift programming. I'm trying to develop a mobile application that allows to mount a network drive in the iphone Files application via the WebDav protocol.
I saw on the internet that WebDav is no longer implemented in iOS because considered deprecated by apple.
To accomplish this task, I decided to separate responsibilities as follows:
Framework:
WebDav (responsible for communication with the WebDav server)
FileProviderExtension:
FileBridge (Responsible for bridging the gap between the WebDav Framework and the iOS Files app)
Main App
I also have an AppGroup that includes the main application and the fileproviderextension
Initially, to measure the feasibility and complexity of this task, I'd like to make a simplistic version that simply displays the files on my drive in the Files app, without necessarily being able to interact with them.
FileProviderExtension.swift:
import FileProvider
import WebDav
class FileProviderExtension: NSObject, NSFileProviderReplicatedExtension {
private var webDavService: WebDavService?
required init(domain: NSFileProviderDomain)
{
super.init()
self.webDavService = WebDavService(baseURL: URL(string: "https://www.mydrive.com/drive")!)
}
func invalidate() {
// TODO: cleanup any resources
}
func item(for identifier: NSFileProviderItemIdentifier,
request: NSFileProviderRequest,
completionHandler: @escaping (NSFileProviderItem?, Error?) -> Void) -> Progress {
let progress = Progress(totalUnitCount: 1)
Task {
do {
if let items = try await webDavService?.propfind(path: identifier.rawValue, depth: 1),
let item = items.first(where: { $0.itemIdentifier == identifier }) {
completionHandler(item, nil)
} else {
completionHandler(nil, NSError(domain: NSCocoaErrorDomain,
code: NSFileNoSuchFileError,
userInfo: nil))
}
} catch {
completionHandler(nil, error)
}
}
return progress
}
func fetchContents(for itemIdentifier: NSFileProviderItemIdentifier,
version requestedVersion: NSFileProviderItemVersion?,
request: NSFileProviderRequest,
completionHandler: @escaping (URL?, NSFileProviderItem?, Error?) -> Void) -> Progress {
let progress = Progress(totalUnitCount: 1)
Task {
do {
guard let service = webDavService else {
throw WebDavError.invalidResponse
}
let data = try await service.get(fileAt: itemIdentifier.rawValue)
let tempURL = FileManager.default.temporaryDirectory.appendingPathComponent(itemIdentifier.rawValue)
try data.write(to: tempURL)
completionHandler(tempURL, nil, nil)
} catch {
completionHandler(nil, nil, error)
}
}
return progress
}
func createItem(basedOn itemTemplate: NSFileProviderItem, fields: NSFileProviderItemFields, contents url: URL?, options: NSFileProviderCreateItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress {
// TODO: a new item was created on disk, process the item's creation
completionHandler(itemTemplate, [], false, nil)
return Progress()
}
func modifyItem(_ item: NSFileProviderItem, baseVersion version: NSFileProviderItemVersion, changedFields: NSFileProviderItemFields, contents newContents: URL?, options: NSFileProviderModifyItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (NSFileProviderItem?, NSFileProviderItemFields, Bool, Error?) -> Void) -> Progress {
// TODO: an item was modified on disk, process the item's modification
completionHandler(nil, [], false, NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:]))
return Progress()
}
func deleteItem(identifier: NSFileProviderItemIdentifier, baseVersion version: NSFileProviderItemVersion, options: NSFileProviderDeleteItemOptions = [], request: NSFileProviderRequest, completionHandler: @escaping (Error?) -> Void) -> Progress {
// TODO: an item was deleted on disk, process the item's deletion
completionHandler(NSError(domain: NSCocoaErrorDomain, code: NSFeatureUnsupportedError, userInfo:[:]))
return Progress()
}
func enumerator(for containerItemIdentifier: NSFileProviderItemIdentifier,
request: NSFileProviderRequest) throws -> NSFileProviderEnumerator {
return FileProviderEnumerator(enumeratedItemIdentifier: containerItemIdentifier,
service: webDavService)
}
}
Here's the code I use to initialize my domain in the main app files:
fileprivate func registerFileProviderDomain() {
let domainIdentifier = NSFileProviderDomainIdentifier("FileProviderExtension Bundle Identifier")
let domain = NSFileProviderDomain(identifier: domainIdentifier,
displayName: "My Drive")
NSFileProviderManager.add(domain) { error in
NSFileProviderManager.add(domain) { error in
if let error = error {
print("Error cannot add domain file provider : \(error.localizedDescription)")
} else {
print("Success domain file provider added")
}
}
}
I can't get rid of the Error :
Error cannot add domain file provider : The operation couldn’t be completed. Invalid argument.
I don't know what I'm missing
Please help me understand
Hello,
I recently published an app that uses Swift Data as its primary data storage. The app uses concurrency, background threads, async await, and BLE communication.
Sadly, I see my app incurs many fringe crashes, involving EXC_BAD_ACCESS, KERN_INVALID_ADDRESS, EXC_BREAKPOINT, etc.
I followed these guidelines:
One ModelContainer that is stored as a global variable and used throughout.
ModelContexts are created separately for each task, changes are saved manually, and models are not passed around.
Threads with different ModelContexts might manipulate and/or read the same data simultaneously.
I was under the impression this meets the usage requirements.
I suspect perhaps the issue lies in my usage of contexts in a single await function, that might be paused and resumed on a different thread (although same execution path). Is that the case? If so, how should SwiftData be used in async scopes?
Is there anything else particularly wrong in my approach?
I am using NSPersistentCloudKitContainer and I decided to add a property to an entity. I accidentally ran try! container.initializeCloudKitSchema(options: []) while using the production container in Xcode (com.apple.developer.icloud-container-environment) which throw a couple of errors and created some FAKE_ records in my production container.
So I changed to my development container and ran the try! container.initializeCloudKitSchema(options: []) and now it succeeded.
After that I cleaned up the FAKE_ records scattered in production container but in Xcode when I'm running I now get these logs in the console (and I can't seem to get rid of them):
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _importFinishedWithResult:importer:](1398): <PFCloudKitImporter: 0x300cc72c0>: Import failed with error:
Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate recoverFromError:](2310): <NSCloudKitMirroringDelegate: 0x302695770> - Attempting recovery from error: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _recoverFromError:withZoneIDs:forStore:inMonitor:](2620): <NSCloudKitMirroringDelegate: 0x302695770> - Failed to recover from error: NSCocoaErrorDomain:4864
Recovery encountered the following error: (null):0
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate resetAfterError:andKeepContainer:](610): <NSCloudKitMirroringDelegate: 0x302695770> - resetting internal state after error: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _requestAbortedNotInitialized:](2198): <NSCloudKitMirroringDelegate: 0x302695770> - Never successfully initialized and cannot execute request '<NSCloudKitMirroringExportRequest: 0x303a52d00> 548CB420-E378-42E5-9607-D23E7A2A364D' due to error: Error Domain=NSCocoaErrorDomain Code=4864 "*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)" UserInfo={NSDebugDescription=*** -[NSKeyedUnarchiver _initForReadingFromData:error:throwLegacyExceptions:]: incomprehensible archive (0x53, 0x6f, 0x6d, 0x65, 0x20, 0x73, 0x61, 0x6d)}
I have integrated CloudKit into a CoreData application and am ready to deploy the schema to production but keep getting an "internal error" when trying to deploy to production or reset my CloudKit environment. I have attached images of what I am seeing including one of the console error. Is there any way to resolve this?
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
CloudKit
Cloud and Local Storage
CloudKit Dashboard
CloudKit Console
Hello,
I apologize if this post could be slightly out of forum topic but I have one issue that I cannot solve.
I tried a few times to call Apple support but the only indication that have given to me is to try with this forum.
The issue I have is simple. Sometimes the modifications performed on iCloud Drive on one computer are not properly synced between the local folder /Users/[username]/Library/Mobile Documents/... and the cloud and therefore are not shared across all devices that use the same iCloud Drive.
This is very disturbing as it may lead to a data loss.
I would like to write a simple software that activates the iCloud Drive sync between the local iCloud folder /Users/[username]/Library/Mobile Documents/... and the Cloud.
A simple macOS bash script would be fine but also other pieces of software are welcome.
Can anyone please help me?
Thanks!
Daniele
Topic:
App & System Services
SubTopic:
iCloud & Data
I have a widely-used app that lets users keep track of personal data. This data is persisted with SwiftData, and synced with CloudKit.
I understand that if the user's iCloud account changes on a device (for example, user logs out or toggles off an app's access to iCloud), then NSPersistentCloudKitContainer will erase the local data records on app launch. This is intentional behavior, intended as a privacy feature.
However, we are receiving regular reports from users for whom the system has incorrectly indicated that the app's access to iCloud is unavailable, even when the user hasn't logged out or toggled off permission to access iCloud. This triggers the behavior to clear the local records, and even though the data is still available in iCloud, to the user, it looks like their data has disappeared for no reason. Helping the user find and troubleshoot their iCloud app data settings can be very difficult, since in many cases the user has no idea what iCloud is, and we can't link them directly to the correct settings screen.
We seem to get these reports most frequently from users whose iCloud storage is full (which feels like punishment for not paying for additional storage), but we've also received reports from users who have enough storage space available (and are logged in and have the app's iCloud data permissions toggled on). It appears to happen randomly, as far as we can tell.
I found a blog post from two years ago from another app developer who encountered the same issue: https://crunchybagel.com/nspersistentcloudkitcontainer/#:~:text=The%20problem%20we%20were%20experiencing
To work around this and improve the user experience, we want to use CKContainer.accountStatus to check if the user has an available iCloud account, and if not, disable the CloudKit sync before it erases the local data.
I've found steps to accomplish this workaround using CoreData, but I'm not sure how to best modify the ModelContainer's configuration after receiving the CKAccountStatus when using SwiftData. I've put together this approach so far; is this the right way to handle disabling/enabling sync based on account status?
import SwiftUI
import SwiftData
import CloudKit
@main
struct AccountStatusTestApp: App {
@State private var modelContainer: ModelContainer?
var body: some Scene {
WindowGroup {
if let modelContainer {
ContentView()
.modelContainer(modelContainer)
} else {
ProgressView("Loading...")
.task {
await initializeModelContainer()
}
}
}
}
func initializeModelContainer() async {
let schema = Schema([
Item.self,
])
do {
let accountStatus = try await CKContainer.default().accountStatus()
let modelConfiguration = ModelConfiguration(
schema: schema,
cloudKitDatabase: accountStatus == .available ? .private("iCloud.com.AccountStatusTestApp") : .none
)
do {
let container = try ModelContainer(for: schema, configurations: [modelConfiguration])
modelContainer = container
} catch {
print("Could not create ModelContainer: \(error)")
}
} catch {
print("Could not determine iCloud account status: \(error)")
}
}
}
I understand that bypassing the clearing of local data when the iCloud account is "unavailable" introduces possible issues with data being mingled on shared devices, but I plan to mitigate that with warning messages when users are in this state. This would be a far more preferable user experience than what's happening now.
Topic:
App & System Services
SubTopic:
iCloud & Data
Something I want to know
and all users of CKSyncEngine care about
I'm going to build a full stack solution using CKSyncEngine, but what's the near future and the support and maintenance priorities inside Apple for CKSyncEngine?
There is only one short video for CKSyncEngine, in 2023, no updates after that, no future plans mentioned. I'm worried that this technology be deprecated or not activity maintained. This is a complex technology, without being activity maintained (or open-sourced) there will be fatal production issues we the developer cannot solve.
The CK developer in the video stated that "many apps" were using the framework, but he did not list any. The only named is NSUbiquitousKeyValueStore, but NSUbiquitousKeyValueStore is too simple a use case. I wonder is apple's Notes.app using it, or going to use it? Is SwiftData using it?
API Problems
The API design seems a little bit tricky, not designed from a user's perspective.
handleEvent doesn't contain any context information about which batch. How do I react the event properly? Let's say our sync code and CKSyncEngine, and callbacks are all on a dedicated thread.
Consider this:
in nextRecordZoneChangeBatch you provided a batch of changes, let's call this BATCH 1, including an item in database with uuid "xxx" and name "yyy"
before the changes are uploaded, there are new changes from many OTHER BACKGROUND THREADS made to the database. item "xxx"'s name is now "zzz"
handle SentRecordZoneChanges event: I get records that uploaded or failed, but I don't know which BATCH the records belong to.
How do I decide if i have to mark "xxx" as finished uploading or not? If I mark xxx as finished that's wrong, the new name "zzz" is not uploaded.
I have to compare every field of xxx with the savedRecord to decide if I finished uploading or not? That is not acceptable as the performance and memory will be bad.
Other questions
I have to add recordIDs to state, and wait for the engine to react. I don't think this is a good idea, because recordID is a CloudKit concept, and what I want to sync is a local database. I don't see any rational for this, or not documented. If the engine is going to ask for a batch of records, you can get all record ids from the batch?
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
iOS
CloudKit
Cloud and Local Storage
Core Data
After updating to 15.2 I am seeing frequent crashes in my in-development app related to SwiftData.
For instance, I have a 100% reproducible crash when I make the app lose and regain focus.
There is also a crash that seem to be triggered by a modelContext.save() call in one of my ModelActors.
With both of these crashes, the issue seems to be around keeping SwiftData models up to date. The first item in the stacktrace that is not machinecode is always some getter on a SwiftData collection or object.
In the console, these crashes are accompanied by output along the lines of:
=== AttributeGraph: cycle detected through attribute 820680 ===
precondition failure: setting value during update: 930592
error: the replacement path doesn't exist: "/var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swift"
error: the replacement path doesn't exist: "/var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swift"
Can't show file for stack frame : <DBGLLDBStackFrame: 0x35a57c4e0> - stackNumber:27 - name:TodoList.todos.getter. The file path does not exist on the file system: /var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swiftCan't show file for stack frame : <DBGLLDBStackFrame: 0x35a57c4e0> - stackNumber:27 - name:TodoList.todos.getter. The file path does not exist on the file system: /var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swiftCan't show file for stack frame : <DBGLLDBStackFrame: 0x35a5a82f0> - stackNumber:62 - name:TodoList.todos.getter. The file path does not exist on the file system: /var/folders/b7/0dw7ztp13fgfxlj19by851tw0000gn/T/swift-generated-sources/@__swiftmacro_10SpaceDebug8TodoListV5todos33_5575DE008494C519BB9FA49C405133E1LL5QueryfMa_.swift
Has anyone run into something similar? I'm looking for suggestions on how to debug this.
Cheers,
Bastiaan
Hi, I'm working on a macOS app that utilizes SwiftData to save some user generated content to their private databases.
It is not clear to me at which point the app I made starts using the production database. I assumed that if I produce a Release build that it will be using the prod db, but that doesn't seem to be the case.
I made the mistake of distributing my app to users before "going to prod" with CloudKit. So after I realized what I had done, I inspected my CloudKit dashboard and records and I found the following:
For my personal developer account the data is saved in the Developer database correctly and I can inspect it.
When I use the "Act as iCloud account" feature and use one of my other accounts to inspect the data, I notice that for the other user, the data is neither in the Development environment nor the Production environment. Which leads me to believe it is only stored locally on that user's machine, since the app does in fact work, it's just not syncing with other devices of the same user.
So, my question is: how do I "deploy to production"?
I know that there is a Deploy Schema Changes button in the CloudKit dashboard. At which point should I press that? If I press it now, before distributing a new version of my app, will that somehow "signal" the already running apps on user's machines to start using the Production database?
Is there a setting in Xcode that I need to check for my Release build, so that the app does in fact start using the production db?
Is there a way to detect in the code whether the app is using the Production database or not? It would be useful so I can write appropriate migration logic, since I don't want to loose existing data users already have saved locally.
Topic:
App & System Services
SubTopic:
iCloud & Data
Tags:
CloudKit
CloudKit Dashboard
CloudKit Console
SwiftData
I've got an application built on top of SwiftData (+ CloudKit) which is published to App Store.
I've got a problem where on each app update, the data saved in the database is duplicated to the end user.
Obviously this isn't wanted behaviour, and I'm really looking forward to fixing it. However, given the restrictions of SwiftData, I haven't found a single fix for this.
The data duplication happens automatically on the first initial sync after the update. My guess is that it's because it doesn't detect the data already in the device, so it pulls all data from iCloud and appends it to the database where data in reality exists.
I am trying to port my application over to CloudKit. My app worked fine before, but then I made scheme of changes and am trying to deploy to a new container. For some reason, the database is not being created after I create the container through Xcode.
I think I have configured the app correctly and a container was created, but no records were deployed. My app current stores data locally on individual devices just fine but they don't sync with each other. That's why I would like to use CloudKit.
See screenshot from Xcode of where I have configured the container. I also have background notifications enabled. Also see screenshot from console where the container has been created, but no records have been.
Any suggestions would be greatly appreciated.
Thank you
I just released an App update the didn't touch ANYTHING to do with Core Data (nothing changed in our Coredata code for at least 8 months). The update uses SDK for iOS 18 and Xcode 16.2 and the app now requires iOS 18 and was a minor bug patch and UI improvements for recent iOS changes.
Since the update we are getting a steady trickle of users on iOS 18, some who allow the App to store data in iCloud (Cloudkit) and others who do not, all reporting that after the update to our recent release ALL their data is gone?!
I had not seen this on ANY device until today when I asked a friend who uses the App if they had the issue and it turned out they did, so I hooked their device up to Xcode and ALL the data in the CoreData database was gone?! They are NOT using iCloud. There were no errors or exceptions on Xcode console but a below code returned NO records at all?!
Chart is custom entity and is defined as:
@interface Chart : NSManagedObject {}
let moc = pc.viewContext
let chartsFetch = NSFetchRequest<NSFetchRequestResult>(entityName:"Charts") // Fetch all Charts
do {
let fetchedCharts = try moc.fetch(chartsFetch) as! [Chart]
for chart in fetchedCharts {
....
}
}
A break point inside the do on fetchedCharts show there are NO objects returned.
This is a serious issue and seems like an iOS 18 thing. I saw some people talking in here about NSFetchRequest issues with iOS 18. I need some guidance here from someone Apple engineer here who knows what the status of these NSFetchrequest bugs are and what possible workarounds are. Becasue this problem will grow for me as more users update to iOS 18.