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?
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
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))
}
}
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
Building an app, which worked fine until I updated to XCode 16.
The app parses data and saves it to SwiftData, and later that store is used for filtering in data.
If I create the store on iOS 18, the store created has issues with relationships, so filtering is not working (I hope that will be fixed in iOS 18.1), so I need to create the store on iOS 17.5.
But before saving the parsed objects to the SwiftData (store), I do the checking for dupes. And after Xcode 16 update that is not working anymore on iOS 17.5, (but this part is working fine under iOS 18):
Here is my object:
import Foundation
import SwiftData
@Model
class CaliberData: Identifiable {
var id: UUID = UUID()
var sizeHeights: [SizeObject]
var featuresABCDIds: [Int]
init(
id: UUID,
sizeHeights: [SizeObject],
featuresABCDIds: [Int],
) {
self.id = id
self.sizeHeights = sizeHeights
self.featuresABCDIds = featuresABCDIds
}
extension CaliberData: Equatable {
static func == (lhs: CaliberData, rhs: CaliberData) -> Bool {
lhs.featuresABCDIds == rhs.featuresABCDIds
&& lhs.sizeHeights == rhs.sizeHeights
}
}
SizeObject if needed:
@Model
class SizeObject: Identifiable, Equatable {
@Attribute(.unique)
var id: Float
@Relationship(inverse: \CaliberData.sizeHeights)
private(set) var caliberDataSizesX: [CaliberData]?
init(_ size: Float) {
self.id = size
}
}
When I am doing the caliberData1 == CaliberData2 comparison to remove dupes before inserting to the SwifData modelContext I am getting exception, and Xcode shows it inside getter part of this expanded section under sizeHeights definition:
@storageRestrictions(accesses: _$backingData, initializes: _sizeHeights)
init(initialValue) {
_$backingData.setValue(forKey: \.sizeHeights, to: initialValue)
_sizeHeights = _SwiftDataNoType()
}
get {
_$observationRegistrar.access(self, keyPath: \.sizeHeights)
return self.getValue(forKey: \.sizeHeights)
}
set {
_$observationRegistrar.withMutation(of: self, keyPath: \.sizeHeights) {
self.setValue(forKey: \.sizeHeights, to: newValue)
}
}
}
Following is the stacktrace:
#1 0x00000001cc168928 in ___lldb_unnamed_symbol4385 ()
#2 0x00000001cc163f34 in ___lldb_unnamed_symbol4354 ()
#3 0x00000001cc165fc4 in ___lldb_unnamed_symbol4369 ()
#4 0x00000001cc169dd4 in ___lldb_unnamed_symbol4403 ()
#5 0x00000001cc123854 in SwiftData.PersistentModel.getValue<τ_0_0, τ_0_1 where τ_1_0: SwiftData.RelationshipCollection, τ_1_1 == τ_1_0.PersistentElement>(forKey: Swift.KeyPath<τ_0_0, τ_1_0>) -> τ_1_0 ()
#6 0x0000000105eda3b0 in CaliberData.sizeHeights.getter at /var/folders/5y/vv3v98ms7jj3z4kj0f3d6f9h0000gq/T/swift-generated-sources/@__swiftmacro_11CaliberDataC11sizeHeights18_PersistedPropertyfMa_.swift:9
#7 0x0000000105ef1a84 in static CaliberData.== infix(_:_:) at ...
My assumption is that "it" thinks that object is already inside the modelContext, when it's actually not yet inserted there and should act like regular class (by regular, I mean - like not marked with @Model macro).
How can I do objects comparison on iOS 17.5 with Xcode 16 (before they are inserted in modelContext)? Any other options?
Anyone getting "Failed to execute query" when querying a Record type in CloudKit Console?
just a simple query on all records
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?
Our app is a document-based app that uses UIDocumentBrowserViewController. We are facing an issue when the user is creating a new document on iOS/iPadOS when in the “Recents” tab (as opposed to the “Browse” tab). Specifically, the document is saved to a hidden ubiquity container. As a result, the user cannot find the file in the document browser. It also does not appear in “Recents”. The expected behaviour would be a folder with our app's icon on it on the user’s iCloud Drive, which contains the files the user creates when in the “Recents” tab.
This issue started to happen when we introduced a new feature that uses iCloud Documents with its own ubiquity container. I'm not sure how UIDocumentBrowserViewController handled saving documents to a default location on iCloud Drive before we had the iCloud Documents entitlement enabled. All I know is that there were no issues.
How to recreate the issue:
Create a document-based app with UIDocumentBrowserViewController. Run the app and create a document while in the recents tab with iCloud enabled. The document will be stored to a folder on iCloud.
Now, enable iCloud Documents and specify a ubiquity container. Then, try to create documents in the Recents tab. The location in which the documents are created cannot be navigated to.
I have a strange crash which I have problems understanding. It only happens on a few devices, after a ModelContainer migration, and it doesn't seem to crash on the migration itself.
The fetch is done in onAppear, and shouldn't necessarily result in a crash, as it is an optional try:
let request = FetchDescriptor<Rifle>()
let data = try? modelContext.fetch(request)
if let data, !data.isEmpty {
rifle = data.first(where: { $0.uuid.uuidString == settings.selectedRifleId }) ?? data.first!
}
When I get logs from users, there seems to be an error in encoding?
Exception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x000000018e8bfd78
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [71687]
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libswiftCore.dylib 0x18e8bfd78 _assertionFailure(_:_:file:line:flags:) + 264
1 SwiftData 0x24e18b480 0x24e14c000 + 259200
2 SwiftData 0x24e193968 0x24e14c000 + 293224
3 SwiftData 0x24e195a78 0x24e14c000 + 301688
4 libswiftCore.dylib 0x18e8e4084 _KeyedEncodingContainerBox.encodeNil<A>(forKey:) + 352
5 libswiftCore.dylib 0x18e8d79f0 KeyedEncodingContainer.encodeNil(forKey:) + 64
6 SwiftData 0x24e19f09c 0x24e14c000 + 340124
7 SwiftData 0x24e1a3dec 0x24e14c000 + 359916
8 libswiftCore.dylib 0x18ec10be8 dispatch thunk of Encodable.encode(to:) + 32
9 SwiftData 0x24e1cd500 0x24e14c000 + 529664
10 SwiftData 0x24e1cd0c8 0x24e14c000 + 528584
11 SwiftData 0x24e1da960 0x24e14c000 + 584032
12 SwiftData 0x24e1ee2ec 0x24e14c000 + 664300
13 SwiftData 0x24e1d97d8 0x24e14c000 + 579544
14 SwiftData 0x24e1eada0 0x24e14c000 + 650656
15 SwiftData 0x24e1d989c 0x24e14c000 + 579740
16 SwiftData 0x24e1eee78 0x24e14c000 + 667256
17 Impact 0x1027403bc 0x10268c000 + 738236
Hi,
I've got an app using SQLLite. Under iOS 17 I could insert and select rows with no issues. Under iOS 18 the same code runs without errors but the select returns no results. Various select statements with and without where clause's, and freshly created database files all behave the same way.
Unless... the phone is in developer mode, then it works same as iOS 17. I'm assuming it's some security change, how do we fix it? Same issue with Swift 5 and Swift 6 for context but I don't think its related to Swift.
Thanks !
Dear Apple Developer Forum
As the title suggests, I have an issue with Swift Data when I want to modify a property of a recursive model class instance. Please consider the following sample project:
import SwiftUI
import SwiftData
@main
struct ISSUEApp: App {
var sharedModelContainer: ModelContainer = {
let schema = Schema([
Item.self,
])
let modelConfiguration = ModelConfiguration(schema: schema, isStoredInMemoryOnly: true)
do {
return try ModelContainer(for: schema, configurations: [modelConfiguration])
} catch {
fatalError("Could not create ModelContainer: \(error)")
}
}()
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(sharedModelContainer)
}
}
@Model
final class Item {
var name: String?
var parent: Item?
init(name: String?, parent: Item?) {
self.name = name
self.parent = parent
}
}
struct ContentView: View {
@Environment(\.modelContext) private var context
@Query private var items: [Item]
@State private var itemToMove: Item?
@State private var count: Int = 0
@State private var presentMoveView: Bool = false
var body: some View {
NavigationStack() {
List(items, id: \.id) {item in
Button(action: {
itemToMove = item
}, label: {
Text("Id: \(item.name ?? "ERROR") and my parent iD is \(item.parent?.name ?? "root")")
.bold(itemToMove == item)
.italic(itemToMove == item)
})
}
.sheet(isPresented: $presentMoveView, content: {
MoveView(toMove: self.itemToMove!)
})
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button(action: {
let i = Item(name: "\(count)", parent: nil)
context.insert(i)
try? context.save()
count += 1
}, label: {
Text("Add an item")
})
}
ToolbarItem(placement: .navigationBarLeading) {
Button(action: {
presentMoveView.toggle()
}, label: {
Text("Move selected item")
})
}
}
}
}
}
struct MoveView: View {
@Environment(\.modelContext) private var modelContext
@Environment(\.dismiss) private var dismiss
@Query private var items: [Item]
@Bindable var toMove: Item
@State private var selectedFutureParent: Item?
var body: some View {
NavigationStack(){
List(items, id: \.id) {item in
Button(action: {
selectedFutureParent = item
}, label: {
Text("Id: \(item.name ?? "ERROR") and my parent iD is \(item.parent?.name ?? "root")")
.bold(selectedFutureParent == item)
.italic(selectedFutureParent == item)
})
}
.toolbar(){
ToolbarItem{
Button("Move", action: {
toMove.parent = selectedFutureParent
dismiss()
})
}
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
Please launch the preview of this app, add items (as many as you'd like), select one and click on the "Move selected item" button. Select the new parent of the item. As you may have noticed, both selected items (moved item and new parent) are modified, whereas only one equality is used. This issue seems to be independent from the @Bindable property wrapper. I tried many things, such as using index instead of direct elements; using local let constant for the parent but the constant is still modified (very weird...)
Thank you in advance for your help !
Best regards
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 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'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 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?
I have a bug I've come across since I've upgraded Xcode (16.0) and macOS (15.0 Sequoia) and figured I'd create a minimal viable example in case someone else came across this and help. I've noticed this in both the macOS and iOS version of my app when I run it.
Essentially I have an area of my code that calls a class that has a step 1 and 2 process. The first step uses async let _ = await methodCall(...) to create some SwiftData items while the second step uses a TaskGroup to go through the created elements, checks if an image is needed, and syncs it. Before this worked great as the first part finished and saved before the second part happened. Now it doesn't see the save and thus doesn't see the insertions/edits/etc in the first part so the second part just isn't done properly. Those step one changes are set and shown in the UI so, in this case, if I run it again the second part works just fine on those previous items while any newly created items are skipped.
I came across this issue when step one handled 74 inserts as each one didn't contain an image attached to it. When I switched the async let _ = await methodCall(...) to a TaskGroup, hoping that would wait better and work properly, I had the same issue but now only 10 to 30 items were created from the first step.
Minimal Viable Example: to reproduce something similar
In my minimal viable sample I simplified it way down so you can't run it twice and it's limited it creating 15 subitems. That said, I've hooked it up with both an async let _ = await methodCall(...) dubbed AL and a TaskGroup dubbed TG. With both types my second process (incrementing the number associated with the subissue/subitem) isn't run as it doesn't see the subitem as having been created and, when run with TaskGroup, only 12 to 15 items are created rather that the always 15 of async let.
Code shared here: https://gist.github.com/SimplyKyra/aeee2d43689d907d7a66805ce4bbf072
And this gives a macOS view of showing each time the button is pressed the sub issues created never increment to 1 while, when using TaskGroup, 15 isn't guaranteed to be created and remembered.
I'm essentially wondering if anyone else has this issue and if so have you figured out how to solve it? Thanks
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.
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.
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)
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'm a bit lost because of a problem I never experienced before: I create entries in 3 Core Data tables and link them. As long as the app is open, everything is fine, I can see the database entries in the three tables. Once the App is closed and restarted, however, the new entries in two of the three tables are gone.
I use Core Data for data storage and DB Browser for SQLite for inspecting the database running in the Simulator.
Here's the relevant function where all Core Data handling happens:
/**
Creates a new ComposedFoodItem from the ComposedFoodItemViewModel.
Creates the related FoodItem and the Ingredients.
Creates all relationships.
- Parameter composedFoodItemVM: The source view model.
- Returns: A Core Data ComposedFoodItem; nil if there are no Ingredients.
*/
static func create(from composedFoodItemVM: ComposedFoodItemViewModel, generateTypicalAmounts: Bool) -> ComposedFoodItem? {
debugPrint(AppDelegate.persistentContainer.persistentStoreDescriptions) // The location of the .sqlite file
let moc = AppDelegate.viewContext
// Create new ComposedFoodItem (1)
let cdComposedFoodItem = ComposedFoodItem(context: moc)
// No existing composed food item, therefore create a new UUID
cdComposedFoodItem.id = UUID()
// Fill data
cdComposedFoodItem.amount = Int64(composedFoodItemVM.amount)
cdComposedFoodItem.numberOfPortions = Int16(composedFoodItemVM.numberOfPortions)
// Create the related FoodItem (2)
let cdFoodItem = FoodItem.create(from: composedFoodItemVM, generateTypicalAmounts: generateTypicalAmounts)
// Relate both (3)
cdComposedFoodItem.foodItem = cdFoodItem
// Add cdComposedFoodItem to composedFoodItemVM
composedFoodItemVM.cdComposedFoodItem = cdComposedFoodItem
// Add new ingredients (4)
if let cdIngredients = Ingredient.create(from: composedFoodItemVM) {
cdComposedFoodItem.addToIngredients(NSSet(array: cdIngredients))
// Save new composed food item
try? moc.save()
// Return the ComposedFoodItem
return cdComposedFoodItem
} else {
// There are no ingredients, therefore we delete it again and return nil
moc.delete(cdComposedFoodItem)
try? moc.save()
return nil
}
}
What the function does:
Creates a new entry in table ComposedFoodItem
Creates another new entry in another table FoodItem
Relates both entries
Creates another 1..n entries in a third table Ingredient and links these to the entry created in step 1
All this works fine, I can see all relations and entries in the database.
Then I quit and restart the app. The entry created in step 2 is still there, but the entries created in steps 1 and 4 are gone, as well as the relationships (of course).
My suspicion: I recently implemented a Core Data migration from Data Model version 1 ("EasyFPU") to version 2 ("EasyFPU 2"). In this migration, I have two custom migration policies for exactly the two tables, which are not stored. The migration policies are pretty simple (and identical for both tables):
/**
No Ingredient is created in the destination model, i.e., there will be no Ingredients
*/
override func createDestinationInstances(forSource sourceIngredient: NSManagedObject, in mapping: NSEntityMapping, manager: NSMigrationManager) throws {
// Do nothing on purpose
debugPrint("Not migrating Ingredient with ID: \((sourceIngredient as? Ingredient)?.id.uuidString ?? "unknown")")
}
And what I suspect is, that this migration policies are somehow called when restarting the app, but I have no idea why, because the migration has already happened before. If I set a breakpoint in the debugPrint line of the code snippet above, I actually never reach this breakpoint - as expected. Nevertheless are the two tables Ingredient and ComposedFoodItem empty after restart.
My AppDelegate Core Data persistentContainer variable looks like this:
lazy var persistentContainer: NSPersistentCloudKitContainer = {
let container = NSPersistentCloudKitContainer(name: "EasyFPU")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
I tried to replace "EasyFPU" with "EasyFPU 2", but this apparently is not the version, but the container name.
Any idea? Thanks in advance!