I'm managing the database with SQLite in Flutter.
I want to enable iCloud backup and restore on the Swift side when called from Flutter.
I am using the following source code, but it is not working.
What could be the cause?
Could you provide a method and countermeasure?
private func saveFileToICloud(fileName: String, localDatabasePath: String, result: @escaping FlutterResult) {
guard let containerName = Bundle.main.object(forInfoDictionaryKey: "ICLOUD_CONTAINER_NAME") as? String else {
result(FlutterError(code: "NO_ICLOUD_CONTAINER", message: "iCloud container is not available", details: nil))
return
}
guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: containerName) else {
result(FlutterError(code: "NO_ICLOUD_CONTAINER", message: "iCloud container is not available", details: nil))
return
}
let fileURL = containerURL.appendingPathComponent(fileName)
let sourceURL = URL(fileURLWithPath: localDatabasePath)
do {
if FileManager.default.fileExists(atPath: fileURL.path) {
try FileManager.default.removeItem(at: fileURL)
}
try FileManager.default.copyItem(at: sourceURL, to: fileURL)
result("File saved successfully to iCloud: \(fileURL.path)")
} catch {
result(FlutterError(code: "WRITE_ERROR", message: "Failed to write file to iCloud", details: error.localizedDescription))
}
}
private func readFileFromICloud(fileName: String, localDatabasePath: String, result: @escaping FlutterResult) {
let containerName = ProcessInfo.processInfo.environment["ICLOUD_CONTAINER_NAME"]
guard let containerURL = FileManager.default.url(forUbiquityContainerIdentifier: containerName) else {
result(FlutterError(code: "NO_ICLOUD_CONTAINER", message: "iCloud container is not available", details: nil))
return
}
let fileURL = containerURL.appendingPathComponent(fileName)
let sourceURL = URL(fileURLWithPath: localDatabasePath)
do {
if FileManager.default.fileExists(atPath: sourceURL.path) {
try FileManager.default.removeItem(at: sourceURL)
}
try FileManager.default.copyItem(at: fileURL, to: sourceURL)
result("File restored successfully to sqlite: \(sourceURL.path)") } catch {
result(FlutterError(code: "READ_ERROR", message: "Failed to read file from iCloud", details: error.localizedDescription))
}
}
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
Am developing an iOS App, which uses a ZipFoundation wrapper around Compression. In XCode, have exported a document type with extension '.MU' in the Info.plist.
On iPhone, when attempting to open archive called: 'Snapshot-test.mu'
can OPEN as a mobile email attachment
but FAILED via Files App referring to "iCloud Drive/Desktop"
Here are the respective URLS
"file:///private/var/mobile/Containers/Data/Application/<UniqueID>/Documents/Inbox/Snapshot-test.mu"
"file:///private/var/mobile/Library/Mobile%20Documents/com~apple~CloudDocs/Desktop/Snapshot-test1.mu"
Two questions:
Is it possible to grant access to files residing remotely in iCloud?
Is "iCloud Drive/Desktop" unique, whereas other iCloud locations would be OK?
We worked with SwiftData, and once CloudKit was integrated, the synchronization worked well. Even if I rerun the app, it works just as well.
However, when I delete the app and reinstall it, I get a Token Expired error and CloudKit doesn't work properly.
My code is organized like this
public lazy var modelContext: ModelContext = { ModelContext(modelContainer) }()
private lazy var modelContainer: ModelContainer = {
let schema = Schema([
Entity1.self,
Entity2.self,
Entity3.self,
])
let modelConfiguration = ModelConfiguration(
schema: schema,
groupContainer: .identifier("myGroupContainer"),
cloudKitDatabase: .automatic
)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
The error content is as follows
error: CoreData+CloudKit: -[PFCloudKitImportRecordsWorkItem fetchOperationFinishedWithError:completion:]_block_invoke(707): <PFCloudKitImporterZoneChangedWorkItem: 0x3022c0000 - <NSCloudKitMirroringImportRequest: 0x3036e7ac0> 1A7E53D4-E95B-423F-8887-66360F6D8865> {
(
"<CKRecordZoneID: 0x301bb1bf0; zoneName=com.apple.coredata.cloudkit.zone, ownerName=__defaultOwner__>"
)
} - Fetch finished with error:
<CKError 0x301bb5650: "Partial Failure" (2/1011); "Couldn't fetch some items when fetching changes"; uuid = 3F346302-C3EE-4F72-820C-988287C92C0A; container ID = "MyContainerID"; partial errors: {
com.apple.coredata.cloudkit.zone:__defaultOwner__ = <CKError 0x301bb1830: "Change Token Expired" (21/2026); server message = "client knowledge differs from server knowledge"; op = 515034AC3ADC4348; uuid = 3F346302-C3EE-4F72-820C-988287C92C0A>
}>
error: CoreData+CloudKit: -[NSCloudKitMirroringDelegate _importFinishedWithResult:importer:](1390): <PFCloudKitImporter: 0x3000a1240>: Import failed with error:
<CKError 0x301bb5650: "Partial Failure" (2/1011); "Couldn't fetch some items when fetching changes"; uuid = 3F346302-C3EE-4F72-820C-988287C92C0A; container ID = "MyContainerID"; partial errors: {
com.apple.coredata.cloudkit.zone:__defaultOwner__ = <CKError 0x301bb1830: "Change Token Expired" (21/2026); server message = "client knowledge differs from server knowledge"; op = 515034AC3ADC4348; uuid = 3F346302-C3EE-4F72-820C-988287C92C0A>
}>
Forcing the ModelContainer to be reinitialized fixes the problem,
it's a problem to get this error in the first place,
the error doesn't even go to fatal for me, so I don't even know how to verify that it's happening.
Is there something I'm doing wrong, or do you have any good ideas for solving the same problem?
I'm working on an app that is using Core Data. I have a custom big number class that boils down to a double and an integer, plus math functions. I've been using transformable types to store these big numbers, but its forcing me to do a lot of ugly casts since the number class is used throughout the application. I figure I can either have my stored values be named differently (e.g. prefix with underscore) and have a computed variable to cast it to the correct type, or find some way to move the big number class to being an NSManagedObject. The issue with this is that the inverse relationships would be massive for the big number class, since multiple entities use the class in multiple properties already. Would it be recommended that I just keep using Transformable types and casts to handle this, or is there some standard way to handle a case like this in Core Data relationships?
Anyone getting "Failed to execute query" when querying a Record type in CloudKit Console?
just a simple query on all records
Hello,
I'm currently developing an app using SwiftData.
I want the app to use CloudKit to sync data, so I made sure all my model properties are optional.
I've defined a Codable enum as follows:
enum Size: Int, Codable {
case small
case medium
case large
}
I've defined a Drink SwiftData model as follows:
@Model
class Drink {
var name: String?
var size: Size?
init(
name: String? = nil,
size: Size? = nil
) {
self.name = name
self.size = size
}
}
In one of my Views, I want to use a @Query to fetch the data, and use a Predicate to filter the data. The Predicate uses the size enumeration of the Drink model. Here is the code:
struct DrinksView: View {
@Query var drinks: [Drink]
init() {
let smallRawValue: Int = Size.small.rawValue
let filter: Predicate<Drink> = #Predicate<Drink> { drink in
if let size: Size = drink.size {
return size.rawValue == smallRawValue
} else {
return false
}
}
_drinks = Query(filter: filter)
}
var body: some View {
List {
ForEach(drinks) { drink in
Text(drink.name ?? "Unknown Drink")
}
}
}
}
The code compiles, but when I run the app, it crashes with the following error:
Thread 1: Fatal error: Couldn't find \Drink.size!.rawValue on Drink with fields [SwiftData.Schema.PropertyMetadata(name: "name", keypath: \Drink.name, defaultValue: nil, metadata: nil), SwiftData.Schema.PropertyMetadata(name: "size", keypath: \Drink.size, defaultValue: nil, metadata: nil)]
How can I filter my data using this optional variable on the Drink model?
Thanks,
Axel
In Xcode 15.0.1, I created a new project to start working with SwiftData. I did this by creating a default App project and checking the Use SwiftData checkbox. The resulting project contains just three files: an app entry point file, a ContentView SwiftUI view file, and an Item model file.
The only change I made was to annotate the default Item timestamp property with a .transformable attribute.
Here is the resulting model:
@Model
final class Item {
@Attribute(.transformable(by: TestVT.self)) var timestamp: Date // Only updated this line
init(timestamp: Date) {
self.timestamp = timestamp
}
}
And here is the definition of TestVT. It is a basic ValueTransformer that simply tries to store the Date as a NSNumber:
// Added this
class TestVT: ValueTransformer {
static let name = NSValueTransformerName("TestVT")
override class func transformedValueClass() -> AnyClass {
NSNumber.self
}
override class func allowsReverseTransformation() -> Bool {
true
}
override func transformedValue(_ value: Any?) -> Any? {
guard let date = value as? Date else {
return nil
}
let ti = date.timeIntervalSince1970
return NSNumber(value: ti)
}
override func reverseTransformedValue(_ value: Any?) -> Any? {
guard let num = value as? NSNumber else {
return nil
}
let ti = num.doubleValue as TimeInterval
return Date(timeIntervalSince1970: ti)
}
}
And finally, I made sure to register my ValueTransformer but updating the sharedModelContainer definition in the App:
var sharedModelContainer: ModelContainer = {
ValueTransformer.setValueTransformer(TestVT(), forName: TestVT.name) // Only added this line
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)")
}
}()
Prior to Xcode 15.1, this was working fine. However, now when I try to create an item when running the app I get the following error:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "timestamp"; desired type = NSNumber; given type = __NSTaggedDate; value = 2023-12-14 01:47:11 +0000.'
I'm unsure of why this stopped working. The error seems to be complaining about the input being of type Date when NSNumber was expected, but I thought that's what the ValueTransformer was supposed to be doing.
Important note: prior to Xcode 15.1, I did not originally override the transformedValueClass() and everything was working but in the new Xcode when launching the app I was getting a Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) on the return try ModelContainer(...) line. Removing the .transformable property from my model fixed the issue. That's why I added the override here, because I think the docs indicate overriding it as well and I missed that the first time. This being said, I think the code I have is what a correct ValueTransformer would look like.
If anyone has experienced this issue, or has a working ValueTransformer for SwiftData in Xcode 15.1, please let me know. Appreciate any help with this issue. Thanks so much!
I have a background thread that is updating a swift data model Item using a ModelActor. The background thread runs processing an Item and updates the Item's status field. I notice that if I have a view like
struct ItemListView: View {
@Query private var items: [Items]
var body: some View {
VStack {
ForEach(items) { item in
ItemDetailView(item)
}
}
}
}
struct ItemDetailView: View {
var item: Item
var body: some View {
// expected: item.status automatically updates when the background thread updates the `Item`'s `status`.
Text(item.status)
// actual: This text never changes
}
}
Then background updates to the Item's status in SwiftData does not reflect in the ItemDetailView. However, if I inline ItemDetailView in ItemListView like this:
struct ItemListView: View {
@Query private var items: [Items]
var body: some View {
VStack {
ForEach(items) { item in
// Put the contents of ItemDetailView directly in ItemListView
Text(item.status)
// result: item.status correctly updates when the background thread updates the item.
}
}
}
}
Then the item's status text updates in the UI as expected. I suspect ItemDetailView does not properly update the UI because it just takes an Item as an input. ItemDetailView would need additional understanding of SwiftData, such as a ModelContext.
Is there a way I can use ItemDetailView to show the Item's status and have the UI show the status as updated in the background thread?
In case details about my background thread helps solve the problem, my thread is invoked from another view's controller like
@Observable
class ItemCreateController {
func queueProcessingTask() {
Task {
let itemActor = ItemActor(modelContainer: modelContainer)
await itemActor.setItem(item)
await itemActor.process()
}
}
}
@ModelActor
actor ItemActor {
var item: Item?
func setItem(_ item: Item) {
self.item = modelContext.model(for: item.id) as? Item
}
func process() async {
// task that runs processing on the Item and updates the Item's status as it goes.
}
All of a sudden my app started getting this CloudKit error, and it happens to a lot of users. I had no changes to cloud sync for months and really surprised by seeing this. What confuses me even more, is that there is no information on the web about this kind of error. I have no idea what causes it and how to solve it. Would love to get any feedback from the CloudKit engineer.
Client went away before operation 27761871408C460A could be validated; failing
{
"NSUnderlyingError": "<CKUnderlyingError 0x600002573f30: \"ClientInternalError\" (2005); \"Client went away before operation 27761871408C460A could be validated; failing\">",
"CKErrorDescription": "Client went away before operation 27761871408C460A could be validated; failing",
"NSDebugDescription": "CKInternalErrorDomain: 2005",
"NSLocalizedDescription": "Client went away before operation 27761871408C460A could be validated; failing"
}
Seems to happen only on macOS.
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.
When using Core Data I would override willSave on NSManagedObject to compute alastModified value on a model object. This allowed one simple method to check changed values and set a date if necessary.
It is possible set lastModified in SwiftData, but the approaches I have found all have drawbacks when compared to the previous approach.
Hide saved model properties behind transient versions
private var textSaved: String = ""
var text: String {
get { textSaved }
set {
textSaved = newValue
lastModified = .now
}
}
I could hide every property that should update the lastModified behind a computed value, but this requires additional code for each new property and obfuscates the model definition.
Update all properties through an update function
func update<T>(keyPath: ReferenceWritableKeyPath<Player, T>, to value: T)
Paul Hudson notes a workaround where any changes are made to the model through an update function that takes a keyPath. This will add complexity to every view that wants to modify model properties, and also leaves those properties open to change through other approaches.
Use ModelContext.willSave
ModelContext sends a notification when it is about to save. This could be caught in .onReceive in a view or perhaps in some model-holding singleton, then ALL changes queried and dealt with accordingly. Perhaps the best approach here would to add willSave to all model objects so code external to the model isn't doing the lastModified logic.
This last solution feels like the best way forward that I know (ideally avoiding any .onReceive code in views). Should I prefer another solution or are there better ones I have missed?
Hello!
I deleted an index on the cloudkit console of a container in development environment and re-added it again with the same name. After that my app has been giving error on development environment.
The live version is working fine as I haven't deployed the changes.. Any idea what can cause this issue and is there a way to fix it? Thank you
I have a requirement to get all records changed after a certain date.
I have set modifiedTimestamp as Queryable, but when I attempt to do any query at all using the following operators: > < >= <= no results are returned. I have confirmed there are records that should be returned. The only operator that works is == and !=.
I have tried the following:
NSPredicate(format: "modificationDate > %@", lastFetched as NSDate)
NSPredicate(format: "___modTime > %@", lastFetched as NSDate)
I'm using NSPersistentCloudKitContainer to sync CoreData to CloudKit public database but I have the problem that canUpdateRecord always returns true even for objects created by another iCloud user with _icloud role permission set to Read only. Am I missing something?
I am working on an app that will have about 200mb of data in a database. I would like to install a json file with 3-4 mb on the phone with the app, and then be able to send small packets of information to the user of the app as they are using the app, for example if they hit a button with a link to an instagram page, I can have the database/server send the link to the app, it will be just small bits of information going from the database to the phone, and perhaps I would do some location tracking and getting some information from the user to build up personal preferences etc. I'm wondering if coredata would be the best way to get smaller packets of information to the phone quickly or if I should think about firebase or something else. I was interested in using Cloudkit, but it seems like Cloudkit is really more about syncing devices and for getting information from the user to the database. It's been really hard to find any information about the strengths versus weaknesses of different databases and how they interact with mobile apps.
I am encountering an issue with the UndoManager functionality in a SwiftUI application that integrates SwiftData for persistence. This issue occurs specifically in macOS 14 (Sonoma) but works as expected on macOS 15 (Sequoia).
The focused test app I have prepared for demonstration allows users to create ParentItem objects, and for each ParentItem, users can add multiple ChildItem objects. The undo functionality (via Cmd+Z) is not working as expected in Sonoma. When I try to undo a ChildItem addition, the UndoManager does not revert just the last ChildItem added, but instead removes all ChildItems that were added in that session.
Expected Behavior
On macOS 14 (Sonoma), I expect the UndoManager to undo only the most recent transaction (in this case, a single ChildItem insert), similar to how it functions on macOS 15 (Sequoia). Each ChildItem insertion should be treated as a separate undoable action.
Current Behavior
In macOS Sonoma, pressing Cmd+Z undoes the entire list of ChildItems added to a ParentItem in the current session, rather than just the most recent ChildItem. This appears to be an issue with undo grouping, but I’ve confirmed that no explicit grouping is being used.
Question
Is this an issue with UndoManager in macOS Sonoma, particularly in how it interacts with SwiftData persistence? What changes should I make to ensure that each ChildItem insert is treated as an individual undo action in macOS Sonoma, just as it works in Sequoia?
Any guidance on isolating the issue or recommended workarounds would be appreciated. I would expect that undo actions for each child addition would be treated as separate transactions, not grouped.
Steps Taken to Solve the Problem
I attempted to manually save the model context (modelContext.save()) after each ChildItem insert to ensure proper persistence.
I also verified that UndoManager was not grouping operations explicitly by calling beginUndoGrouping() or endUndoGrouping() myself.
This issue seems to be tied specifically to macOS Sonoma, as it does not occur on macOS Sequoia, where undoing behaves as expected.
Conditions
macOS 14 Sonoma: The issue occurs consistently.
macOS 15 Sequoia: The issue does not occur.
This issue appears to be independent of hardware, as I’ve tested it on multiple machines.
APIs/Features Potentially Involved
UndoManager in a SwiftUI application
SwiftData for persistence (using modelContext.save())
macOS version-specific behavior
Steps to reproduce
Clone test project (https://github.com/Maschina/SwiftDataUndoManagerExample), compile and run
Create a new ParentItem in the app (via plus toolbar button in the sidebar).
Add multiple ChildItems to the ParentItem (via plus toolbar button in the content / middle column of the navigation split view).
Press Cmd+Z to undo the last addition.
I have enabled an App Group in my App and the Widget Extension. I use it to share my UserDefaults. Every time the app starts I now get the following error message in the Xcode console:
Couldn't read values in CFPrefsPlistSource<0x303034510> (Domain: group.XX.XXXX.XXXX, User: kCFPreferencesAnyUser, ByHost: Yes, Container: (null), Contents Need Refresh: Yes): Using kCFPreferencesAnyUser with a container is only allowed for System Containers, detaching from cfprefsd
The shared UserDefaults itself works without problems.
Any ideas how I could get rid of this warning?
Juust before I initiate an App Transfer...
We have sandboxed 'mobile' versions of our app (iOS and mac App) to transfer to the surviving company.
However, we also have a 'full' non-sandboxed legacy desktop dmg version of the app, available for Mac (and Win). This has access to the same iCloud folder
So the question is, what happens to the iCloud app folder on the Mac if they are only using this desktop version, once the transfer takes place? Will it remain visible on the Mac, and will it remain accessible by the desktop version if so?
I expect that although the iCloud entitlement is transferred, as it is another Team ID, the legacy app will not be able to read/write without user prompted permission. What I hope at the least, is that the folder doesn't become invisible on that machine...
I'm seeing a lot of these in my logs:
PersistentIdentifier PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-swiftdata://Course/BC9CF99A-DE6A-46F1-A18D-8034255A56D8), implementation: SwiftData.PersistentIdentifierImplementation) was remapped to a temporary identifier during save: PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata:///Course/t58C849CD-D895-4773-BF53-3F63CF48935B210), implementation: SwiftData.PersistentIdentifierImplementation). This is a fatal logic error in DefaultStore
... though everything seems to work.
Does anyone know what this means in this context? Anything I can do to not have this appear?
Hello,
After iOS 18 fetchChanges() method of CloudKitSync engine does not work as before. Calling the function doesn't fetch changes always, but it does fetch on iOS 17.
However going background and foreground again fetches changes automatically.