Use CloudKit's ckqueryoperation's recordmatchedblock in Swift 6.0, which always crashes, but works fine in Swift 5: func fetchAllRecords() async throws { let predicate = NSPredicate(format: "Topics = %@", "Integrations") let query = CKQuery(recordType: "PureMList", predicate: predicate)
let operation = CKQueryOperation(query: query)
operation.recordMatchedBlock = { recordID, result in
switch result {
case .success(let record):
DispatchQueue.main.async {
// Ensure UI updates happen here
print("Fetched record: \(record)")
// Update your UI elements here
}
case .failure(let error):
// Handle the error
print("Error fetching record with ID \(recordID): \(error)")
}
}
// Ensure you're using the correct database
publicDatabase.add(operation)
}
If it is a crash that occurs at _dispatch_assert_queue_fail
in libdispatch.dylic
when you adopt Swift 6, please see here for an in-depth analysis.
Basically, Swift 6 inserted a runtime check to detect concurrency issues, and if indeed detecting an issue, it (intentionally) triggers a crash. The Swift compiler doesn't spot the issue at compile time because of "a Swift / Objective-C impedance mismatch."
To solve the issue, I'd consider two options:
-
Use async APIs. Concretely in your case, you can replace
CKQueryOperation
with records(matching:inZoneWith:desiredKeys:resultsLimit:) and records(continuingMatchFrom:desiredKeys:resultsLimit:). -
Make the closure passed to
CKQueryOperation
sendable:
operation.recordMatchedBlock = { @Sendable (recordID, result) in
...
}
You can give it a try and follow up here if this doesn't help.
Best,
——
Ziqiao Chen
Worldwide Developer Relations.