iCloud & Data

RSS for tag

Learn how to integrate your app with iCloud and data frameworks for effective data storage

CloudKit Documentation

Post

Replies

Boosts

Views

Activity

SwiftData does not work on a background Task even inside a custom ModelActor.
I have created an actor for the ModelContainer, in order to perform a data load when starting the app in the background. For this I have conformed to the ModelActor protocol and created the necessary elements, even preparing for test data. Then I create a function of type async throws to perform the database loading processes and everything works fine, in that the data is loaded and when loaded it is displayed reactively. actor Container: ModelActor { nonisolated let modelContainer: ModelContainer nonisolated let modelExecutor: ModelExecutor static let modelContainer: ModelContainer = { do { return try ModelContainer(for: Empleados.self) } catch { fatalError() } }() let context: ModelContext init(container: ModelContainer = Container.modelContainer) { self.modelContainer = container let context = ModelContext(modelContainer) self.modelExecutor = DefaultSerialModelExecutor(modelContext: context) self.context = context Task { do { try await loadData() } catch { print("Error en la carga \(error)") } } } } The problem is that, in spite of doing the load inside a Task and that there is no problem, when starting the app it stops responding the UI while loading to the user interactions. Which gives me to understand that actually the task that should be in a background thread is running somehow over the MainActor. As I have my own API that will provide the information to my app and refresh it at each startup or even send them in Batch when the internet connection is lost and comes back, I don't want the user to be continuously noticing that the app stops because it is performing a heavy process that is not really running in the background. Tested and compiled on Xcode 15 beta 7. I made a Feedback for this: FB13038621. Thanks Julio César
8
1
5.6k
Aug ’23
File size limit?
I keep getting crashes with the following error in Xcode 15 beta 7: CoreData: debug: PostSaveMaintenance: fileSize 15009192 greater than prune threshold CoreData: annotation: PostSaveMaintenance: wal_checkpoint(TRUNCATE) Is 15M bytes too much data?
2
0
1.1k
Aug ’23
'ID' is inaccessible due to '@_spi' protection level
When I reference the ID of a SwiftData model entity (aka PersistentIdentifier) anywhere as a type (like @State var selection: Set<SomeEntity.ID> = Set<SomeEntity.ID>()) I now get the following error: 'ID' is inaccessible due to '@_spi' protection level This never was a problem with CoreData and also not with SwiftData until the Xcode 15 RC. Does anybody know, if this is a bug or intended behaviour?
3
2
1.6k
Sep ’23
SwiftData @Query crashes when trying to filter or sort using an enum or relationship
Like the title says, I've realised that when I try to use filter or sort on properties that aren't standard supported data types i.e. Using a transformable or a value type like an enum, I seem to be getting the following crash... SwiftData/DataUtilities.swift:1140: Fatal error: Unexpected type for Expansion: Optional<UIColor> Xcode expands and shows me when trying to access the wrapped value it's crashing. I'm assumung that the query property wrapper can't handle these custom data types @Query private var items: [Item] { get { _items.wrappedValue <--- Crash here } } Which seems to be pointing to a transferable property in one of my models. Below are my two models i'm using. enum Priority: Int, Codable, Identifiable, CaseIterable { case low case medium case high var title: String { switch self { case .low: return "Low" case .medium: return "Medium" case .high: return "High" } } var image: Image? { switch self { case .medium: return Image(systemName: "exclamationmark.2") case .high: return Image(systemName: "exclamationmark.3") default: return nil } } var id: Self { self } } @Model final class Item: Codable { var title: String @Attribute(originalName: "timestamp") var dueDate: Date var isCompleted: Bool var isFlagged: Bool = false var isArchived: Bool = false var isCritical: Bool? var priority: Priority? @Relationship(deleteRule: .nullify, inverse: \Category.items) var category: Category? @Attribute(.externalStorage) var image: Data? enum CodingKeys: String, CodingKey { case title case timestamp case isCritical case isCompleted case category case imageName } init(title: String = "", dueDate: Date = .now, priority: Priority? = nil, isCompleted: Bool = false) { self.title = title self.dueDate = dueDate self.priority = priority self.isCompleted = isCompleted } init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.dueDate = Date.randomDateNextWeek() ?? .now self.isCompleted = try container.decode(Bool.self, forKey: .isCompleted) self.category = try container.decodeIfPresent(Category.self, forKey: .category) if let imageName = try container.decodeIfPresent(String.self, forKey: .imageName), let imageData = UIImage(named: imageName) { self.image = imageData.jpegData(compressionQuality: 0.8) } if let isCritical = try container.decodeIfPresent(Bool.self, forKey: .isCritical), isCritical == true { self.priority = .high } } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) try container.encode(dueDate, forKey: .timestamp) try container.encode(isCompleted, forKey: .isCompleted) try container.encode(category, forKey: .category) } } @Model class Category: Codable { @Attribute(.unique) var title: String var items: [Item]? @Attribute(.transformable(by: ColorValueTransformer.self)) var color: UIColor? init(title: String = "", color: UIColor) { self.title = title self.color = color } enum CodingKeys: String, CodingKey { case title } required init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) self.title = try container.decode(String.self, forKey: .title) self.color = UIColor(possibleColors.randomElement()!) } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encode(title, forKey: .title) } } And below is an example of me sorting based on my enum (Priority) & Relationship (Category name) func sort() -> [SortDescriptor<Item>]{ switch self { case .title: [SortDescriptor(\Item.title)] case .date: [SortDescriptor(\Item.dueDate)] case .category: [SortDescriptor(\Item.category?.title)] case .priority: [SortDescriptor(\Item.priority?.rawValue)] } } And a filter example below creating a predicate that we will execute to return and matches found in the title or category title let highPriority = Priority.high if let query { return #Predicate { $0.priority == highPriority && ($0.title.contains(query) || $0.category?.title.contains(query) == true) && $0.isArchived == false } } I'm pretty sure this is a SwiftData bug since when using strings, bools and dates it's all fine using anything outside of that box causes these crashes...
8
1
2.7k
Sep ’23
Custom struct Codable for SwiftData
I'm encountering an issue encoding/decoding a custom struct from SwiftData. As it's all happening behind the generated code of SwiftData and a decoder, I'm not really sure what's going on. I have a custom type defined kind of like this: public struct Group<Key: Hashable, Element: Hashable> { private var elementGroups: [Element: Key] private var groupedElements: [Key: [Element]] } In short, it allows multiple elements (usually a string), to be grouped, referenced by some key. I have Codable conformance to this object, so I can encode and decode it. For simplicity, the elementGroups is encoded/decoded, and the groupedElements is rebuilt when decoding. My implementation is similar to this: extension Group: Codable where Key: Codable, Element: Codable { private enum Keys: CodingKey { case groups } public init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: Keys.self) let decoded = try container.decode([Element: Key].self, forKey: .groups) // Enumerate over the element groups, and populate the list of elements. // var elements: [Key: [Element]] = [:] for group in decoded { elements[group.value] = (elements[group.value] ?? []) + [group.key] } elementGroups = decoded groupedElements = elements } public func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: Keys.self) try container.encode(elementGroups, forKey: .groups) } } This works fine when encoding and decoding to JSON, but when I attempt to use this structure as a value within a SwiftData model, then decoding the type crashes my application. @Model final class MyModel { var id: UUID = UUID() var groups: Group<UUID, String> = Group<UUID, String>() init(id: UUID) { self.id = id } } When something attempts to decode the groups property on the MyModel object, it crashes with the following error: Could not cast value of type 'Swift.Optional<Any>' (0x1ed7d0290) to 'Swift.Dictionary<Swift.String, Foundation.UUID>' (0x121fa9448). I would guess that there is a nil value stored for groups in SwiftData, and attempting to decode it to a Group<UUID, String> type is failing. It's odd that it doesn't throw an exception though, and hard crashes. Also, I'm not sure why it's optional, as a value is being written out. Does anyone have any ideas?
4
1
1.3k
Oct ’23
Swift Data value is duplicated between two variables in a class
I'm using two loops, one nested in another to create and assign a subclass to the parent class. I'm using x for one loop and y for the other loop. Print statement shows the loop variables being updated correctly, however the subclass has both variables as the same value. I'm not sure if the value is being stored as the same value or being displayed as the same value. I've checked both creation and display code. I can't find the error. var body: some View { NavigationSplitView { List { ForEach(routes) { item in NavigationLink { Text("Route: \(item.route_name) \nSeason: \(item.route_season)\nCoordinates: \(item.route_coordinates)\nProcessed: \(String(item.route_processed))\nNumber of baseboards: \(item.route_baseboards.count)") } label: { Text(item.route_name) } //end route nav link } //end route for each .onDelete(perform: deleteItems) //end route nav ForEach(baseboards) { item in NavigationLink { //if let test = item.baseboard_heightPoints.count { Text("Grid Size: \(String(item.baseboard_grid_size))\nRow:\(String(item.baseboard_row))\nColumn:\(String(item.baseboard_column))\nHeight Point Count:")//\(String(item.baseboard_heightPoints.flatMap { $0 } .count))") //fix count later //Text("Test") // Text("test2") // } else { // Text("Grid Size: \(String(item.baseboard_grid_size))\nRow:\(String(item.baseboard_row))\nColumn:\(String(item.baseboard_column))\nHeight Point Count: 0") // } } label: { Text(String(item.baseboard_grid_size)) }} .onDelete(perform: deleteItemsBaseboards) //end baseboard route nav // ForEach(heightPoints) { item in NavigationLink { // Text("Row:\(String(item.hp_row))\nColumn:\(String(item.hp_column))\nElevation:\(String(item.hp_elevation))") // } label: { // Text("Row:\(String(item.hp_row))\nColumn:\(String(item.hp_column))") // }} //.onDelete(perform: deleteItemsBaseboards) } .toolbar { ToolbarItem { Button(action: addItem) { Label("Add Route", systemImage: "plus") } //Button(action: addItem) { //not showing up // Label("Add Baseboard", systemImage: "arrow.up") //} } } //end toolbar } detail: { Text("Select a route") } //end NavigationSplitView } //end view body private func addItem() { /*withAnimation { */ let newItem = Route( route_name: "test route " + UUID().uuidString, route_season: "summer", route_processed: false, route_coordinates: "Somewhere", route_region: "US", route_baseboards: []) modelContext.insert(newItem) //end add route //add baseboards to each route let bb_StartCol = 0 let bb_EndCol = 3 let bb_StartRow = 0 let bb_EndRow = 3 //let grid5m = 148 //let grid10m = 76 //let gridHD = 5760 var bb_grid_size = 5760 let bb_sectionsVerticle = 180 let bb_sectionsHorizonal = 360 var sectionData: Data var dataInputArray: [UInt8] = [0x03, 0x00, 0x00, 0x00, 0x00, 0x82, 0xFF, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x20, 0xF8, 0xFF] sectionData = Data(withUnsafeBytes(of: dataInputArray, Array.init)) for x in bb_StartCol...bb_EndCol { //columns for y in bb_StartRow...bb_EndRow { //rows print(x,y) if bb_grid_size < 1000 { let newItem2 = Baseboard( baseboard_column: Int16(x), baseboard_row: Int16(y), texture: "Grid", baseboard_processed: false, baseboard_grid_size: Int16(bb_grid_size),//(x+2)+(y+2), baseboard_heightPoints: Array(repeating: Array(repeating: 0, count: bb_grid_size), count: bb_grid_size), baseboard_HPSection: [], baseboard_route: newItem //baseboard_hps: [] ) modelContext.insert(newItem2) //insert baseboard for each run } else { let newItem2 = Baseboard( baseboard_column: Int16(x), baseboard_row: Int16(y), texture: "Grid", baseboard_processed: false, baseboard_grid_size: Int16(bb_grid_size),//(x+2)+(y+2), baseboard_heightPoints: [], baseboard_HPSection: Array(repeating: Array(repeating: sectionData, count: bb_sectionsVerticle), count: bb_sectionsHorizonal), baseboard_route: newItem //baseboard_hps: [] ) modelContext.insert(newItem2) //insert baseboard for each run } //print(x,y) } //end y for loop - baseboards } //end x for loop - baseboards // } // end animation of adding new items } // end function add items private func deleteItems(offsets: IndexSet) { withAnimation { for index in offsets { modelContext.delete(routes[index]) } } } private func deleteItemsBaseboards(offsets: IndexSet) { withAnimation { for index in offsets { modelContext.delete(baseboards[index]) } } } //private func deleteItemsHeightPoints(offsets: IndexSet) { // withAnimation { // for index in offsets { // modelContext.delete(heightPoints[index]) // } // } // } } #Preview { ContentView() .modelContainer(for: Route.self, inMemory: false) }
3
0
803
Oct ’23
CloudKit Console - Internal Error during Query Records
Issue: I'm experiencing intermittent CloudKit Console 'Internal Error' when performing simple queries on a Private database. I am experiencing this issue with multiple CloudKit databases. In all instances, the issue is intermittent. Steps to Replicate in the CloudKit Console: Select a Database Select Records Select Private Database Select Custom Zone Select Record Type Select Query Records IF the records populate, click 'Query Records' again until Internal Error occurs. Expected Results: A list of records Actual Results: 'Internal Error' (see screenshot below) Additional Info: Starting last month, I started receiving CKCloudKit Error 15 when attempting to sync data from a private CloudKit database. My query code has not changed for a few years now with no previous issues. I believe these errors are the 'same' and on the Apple Server side. Is anyone else experiencing this issue? Any help would be greatly appreciated. Thank you.
3
2
1k
Oct ’23
Added new field to swiftdata model, now crashes
I had a simple class called Entry with one field (timestamp: Date) and I tried to add a new field without having to uninstall the app , so I would not crash. I tried implementing the MigrationPlan stuff but I do not know if I have to manually add a value to the new field or how exactly it works. Here is my code. When I use the EntrySchemaV0 it works fine but if I try to use the V1 it crashes. (could not fetch ModelContainer) I also tried it with a custom migration stage. Crashes with error (SwiftData/BackingData.swift:432: Fatal error: Expected only Arrays for Relationships - String) import Foundation import SwiftData enum EntrySchemaV0: VersionedSchema { static var versionIdentifier = Schema.Version(0, 1, 0) static var models: [any PersistentModel.Type] { [Entry.self] } @Model final class Entry { var timestamp: Date init(timestamp: Date) { self.timestamp = timestamp } } } enum EntrySchemaV1: VersionedSchema { static var versionIdentifier = Schema.Version(0, 2, 0) static var models: [any PersistentModel.Type] { [Entry.self] } @Model final class Entry { var name: String var timestamp: Date init(name: String = "", timestamp: Date = .now) { self.name = name self.timestamp = timestamp } } } enum EntryMigrationPlan: SchemaMigrationPlan { static var schemas: [any VersionedSchema.Type] { [EntrySchemaV0.self, EntrySchemaV1.self] } static var stages: [MigrationStage] { [migrateV0toV1] } static let migrateV0toV1 = MigrationStage.lightweight( fromVersion: EntrySchemaV0.self, toVersion: EntrySchemaV1.self ) }
3
3
1.4k
Oct ’23
SwiftData Schema Migration Plan when the type of a field in your model changes
Hi I am trying to figure out how to properly migrate my schema when a field in my model needs to become a different type. I start with a simple model User with an array of type String called friends @Model final class User { @Attribute(.unique) var id: String var friends: [String]? } Now I want to change the type of friends to [User]? but I am unsure how to make a migration plan for this. I created two versioned schemas enum UserSchemaV1: VersionedSchema { static var versionIdentifier = Schema.Version(1, 0, 0) static var models: [any PersistentModel.Type] { [User.self] } @Model final class User { @Attribute(.unique) var id: String var friends: [String]? } } enum UserSchemaV2: VersionedSchema { static var versionIdentifier = Schema.Version(2, 0, 0) static var models: [any PersistentModel.Type] { [User.self] } @Model final class User { @Attribute(.unique) var id: String var friends: [User]? } } Then I make a migration plan enum UsersMigrationPlan: SchemaMigrationPlan { static var stages: [MigrationStage] { [migrateV1toV2] } static var schemas: [any VersionedSchema.Type] { [UserSchemaV1.self, UserSchemaV2.self] } static let migrateV1toV2 = MigrationStage.custom( fromVersion: UserSchemaV1.self, toVersion: UserSchemaV2.self, willMigrate: nil, didMigrate: nil ) } And I make a container using my migration plan let container: ModelContainer init() { do { container = try ModelContainer(for: User.self, migrationPlan: UsersMigrationPlan.self ) } catch { fatalError("Failed to initialize user model container") } } I also use a typealias so I can keep using User in my code typealias User = UserSchemaV2.User But no matter what I try I get an error when running my code Thread 1: Fatal error: Unknown related type - User In the closure for v2 of the Schema set { _$observationRegistrar.withMutation(of: self, keyPath: \.friends) { self.setValue(forKey: \.friends, to: newValue) } } What am I misunderstanding about migrating my schema with this change? Is this something that's possible when doing a schema migration? Is there a simple/better/easier way to modify my schema to change the type of the field?
1
0
1.1k
Nov ’23
SwiftData upsert duplicates problem or ?
Context: Importing JSON to have some data during development of SwiftData MacOS app. One object is repeated several times in the JSON and each copy is in the decoded data and does get inserted into the context and can be saved to the container, but then there are multiple copies of the same object with the same identifier in the database. When I qualify If the troublesome object's class with an @Attribute(.unique) identifier then the app crashes on .save. I was expecting a silent upsert. In this case there are three @Model classes. The model has a primary object with children objects and each child may have a third object type as a property and that third object may have a distinct third object listed as one of its own properties. It is this third object which appears several times in the file since it can be associated with many children. The insert code only specifies the primary object as I was expecting nested insertion of the children and the third object. Does this need to be handled differently? I'd like to only insert the first instance of the third object and keep the relationship intact. Should upsert work on .insert and might there be a technical problem?
1
0
716
Nov ’23
CloudKit Public Database and User Access
Hello, I have a couple of questions about the CloudKit public database: Is it possible to control what authenticated users can write to the database on the server side? I know I could write logic in my iOS app to prevent to prevent certain actions, like uploading more data than I want to allow, but traditionally one would write server-side checks too. Even if I don't use CloudKit JS, it still seems like a malicious user could intercept network traffic and interact directly with iCloud to bypass any validations I've included in my app. Is it possible to block a user from using my app and interacting with CloudKit entirely? Along the same lines as question 1, I may want to block a malicious user from interacting with my public database, particularly writing to it. Is it common practice to write configuration settings to the public database as an administrative user and to have the app look for these known records? For example, I could add records to a configuration zone via the iCloud dashboard and have my app read them at startup to do things like enable/disable signups. Thanks for any help and guidance on these issues!
0
0
626
Nov ’23
closure #1 in closure #1 in CoreDataManager.persistentContainer.getter
Hello everyone, I am experiencing a very weird issue. There are two scenario, and the crash issue direct to persistentContainer.getter last. I know the error is occur in this line : CoreDataManager.persistentContainer.getter + 51 code block: fatalError("Unresolved error \(error), \(error.userInfo)") scenario1, Xcode crash report: scenario 2, firebase crashlystic: detail info: ProjectName/CoreDataManager.swift:51: Fatal error: Unresolved error Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={sourceURL=file:///private/var/mobile/Containers/Shared/AppGroup/8DA377D9-5CE2-47BB-A985-8A8B7CCD071C/FraudInfo.sqlite, reason=Cannot migrate store in-place: I/O error for database at /private/var/mobile/Containers/Shared/AppGroup/8DA377D9-5CE2-47BB-A985-8A8B7CCD071C/FrankInfo.sqlite. SQLite error code:1, 'duplicate column name: ZISSEARCH', destinationURL=file:///private/var/mobile/Containers/Shared/AppGroup/8DA377D9-5CE2-47BB-A985-8A8B7CCD071C/FrankInfo.sqlite, NSUnderlyingError=0x283398de0 {Error Domain=NSCocoaErrorDomain Code=134110 "An error occurred during persistent store migration." UserInfo={NSSQLiteErrorDomain=1, NSFilePath=/private/var/mobile/Containers/Shared/AppGroup/8DA377D9-5CE2-47BB-A985-8A8B7CCD071C/FrankInfo.sqlite, NSUnderlyingException=I/O error for database at /private/var/mobile/Containers/Shared/AppGroup/8DA377D9 The “duplicate column name: ZISSEARCH'” issue, we had been update xcdatamodel version, Old version user was update to newest app version, but migration fail, why the crash occurring the newest version? But I can’t reproduce the crash, and I have no idea how to fix this. please support me how can I handle the issue, thanks more.
0
0
661
Nov ’23
SwiftData Model didset
Hi, I'm trying out new SwiftData in a small Xcode project. It seems that the property observers didSet and willSet don't work anymore for swift data anymore. In code like this, the didSet does nothing and seems to never be called. @Model public final class importConfig: Identifiable, ObservableObject{ @Attribute(.unique) public var id: UUID /// Name of the configuration var configName: String /// Numbers App document to open var numbersFilePath: URL? /// Indicate wether current <numbersFilePath> Numbers App document has been loaded and analyzed var isLoaded: Bool = false /// Current selected sheet var selectedSheetID: UUID? { didSet { selectedSheetID = nil print("test") } } } Am I doing something wrong or is it the expected behavior ? If it is the expected behavior, how can I add "business" rules when setting/unsetting value to model properties ? I tried to add rules directly with .onchange() in my views, but this way I have to repeat the same rules/code. Is there any alternative to do so ? Thank you
2
0
1.1k
Nov ’23
No effect from CloudKit container API token discoverability checkbox
An iOS app I'm developing also has an external website where users can contribute data to the app's CloudKit public database. Using CloudKit JS, I require users to log in with their Apple ID on the website before submitting data that is sent to the public database. I need to capture the user's name and/or email address from their Apple ID info so the contributed data can be tied to a particular person, I can limit how much data each person contributes, etc. It all works great if I log in with my Apple ID, including that the web app captures my name and saves it to the public database. The problem is that for any other Apple ID I try (a family member's or friend's, or a sandbox one I've created), the web app cannot obtain the user's name or email address, even when they successfully authenticate with their Apple ID. I also can't figure out how to make the web app ask the user for permission for their name or email to be discoverable. On the CloudKit container page where I created the web API token, I checked the box for "Request user discoverability at sign in", but that doesn't make any prompt appear during the login process. Anyone know how to force a prompt to the user to let their info be discoverable? Thanks!
0
0
417
Nov ’23
Selecting which data models sync with iCloud SwiftData
So I am trying to sync only some of my Models with iCloud and others kept locally in the default.store. I am having a world of issues so before I start looking for the needle in my haystack. I would like to ask this forum, is my approach one that should work as desired or is my code obviously why things are not working? All my Models have default values and relationships where needed are optionals. iCloud is working but my issue arises when i try to exclude some models. So I want to sync "modelsForCloudSyncing" but not "modelNotForCloudSyncing" In advance thank you var avoidCloudSyncModelContainer : ModelContainer = { let modelNotForCloudSyncing = Schema([NoCloudSyncModel.self]) let modelConfigForNoCloudSync = ModelConfiguration(schema: modelNotForCloudSyncing, cloudKitDatabase: .none) let modelsForCloudSyncing = Schema([CloudSyncModelA.self, CloudSyncModelB.self, CloudSyncModelC.self]) let modelConfigForCloudSync = ModelConfiguration(schema: modelsForCloudSyncing, cloudKitDatabase: .automatic) do { return try ModelContainer(for: NoCloudSyncModel.self, CloudSyncModelA.self, CloudSyncModelB.self, CloudSyncModelC.self, configurations: modelConfigForNoCloudSync, modelConfigForCloudSync) } catch { fatalError("Could not create ModelContainer: \(error)") } }() ... { ContentView() }.modelContainer(avoidCloudSyncModelContainer)
11
2
1.8k
Nov ’23