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
iCloud & Data
RSS for tagLearn how to integrate your app with iCloud and data frameworks for effective data storage
Post
Replies
Boosts
Views
Activity
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?
is anyone facing the error, "The current model reference and the next model reference cannot be equal", when using SwiftData with migration and iCloud/CloudKit integration?
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...
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?
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)
}
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.
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
)
}
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?
I enabled Advanced Data Protection for my developer account, and this (understandably) broke access to my private records in CloudKit Console.
I disabled Advanced Data Protection but CloudKit Console still cannot connect. In the database popup the "Click here to retry..." option always fails silently.
Does anyone know a workaround?
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)
Hi,
I'm updating my app from CoreData to SwiftData and came across an issue. My app has multiple users so in CoreData and I assigned each a myID value set to an UUID so I could use UserDefaults to set the preferred user on app load. When switching over I noticed the PersistentIdentifier value and got excited as I could fetch the matching entity with modelContext.model(for: yourID). I decided to use that instead so I updated my UserDefaults code from UUID to this:
@Published var selectedUserID: PersistentIdentifier? {
didSet {
UserDefaults.standard.set(selectedUserID, forKey: "selectedUserID")
}
}
init() {
self.selectedUserID = UserDefaults.standard.object(forKey: "selectedUserID") as? PersistentIdentifier ?? nil
}
This code compiles and, of course the id is currently set to nil. My issue now is when I try to assign a user to it ar my app crashes and I get the following error:
Attempt to set a non-property-list object SwiftData.PersistentIdentifier(id: SwiftData.PersistentIdentifier.ID(url: x-coredata://6FE80FC9-0B4C-491E-8093-DED37A619F1B/EnteredUser/p834), implementation: SwiftData.PersistentIdentifierImplementation) as an NSUserDefaults/CFPreferences value for key selectedUserID
Should I go back to an additional UUID field in my user model and find it that way or is there a way to use the PersistentIdentifier value with my UserDefaults?
Thanks for any tips.
We're doing some disaster recovery management / risk management and a point-of-failure for our app is if we lose access to our bundle id.
From my understanding, secure keychain items are scoped to your bundle ID as well as iCloud files stored under the app with 'hidden' scope.
Losing our bundle ID is a scenario we want to eliminate completely from our threat/disaster modelling.
Is this a realistic concern we should have?
Friends,
I have created an App in SwiftData that uses CloudKit to sync between devices. I am using Xcode 15.0.1, and iOS 17.1.1. For further context, I am calling modelContext.save() with every change that I want to synchronise, and this seems to call a CloudKit save, as expected, within the development environment.
The synchronisation works great in the development environment with no issues, it updates fairly quickly and I use @Query to manage SwiftUI updates in the most direct way possible. It is a complex model with many relationships, and I call @Query and filter instances to get to the instance required, rather than using traditional Bindings, as many tutorials and WWDC have suggested.
When I switch to the production environment the app synchronises between devices great using the production CloudKit container. I have set up push notification certificates, and these seem to work. The issues is that, after a few minutes, the synchronisation stops happening between devices.
When synchronisation is working, Device 1 initially saves to CloudKit, and you can see the items update in the CloudKit console. When synching stops working, Device 1 stops saving, and the CloudKit console stops updating.
If I then delete and install the app on Device 1 from TestFlight, you can see that it starts saving again, with updates to the CloudKit console as expected.
But, Device 2 does not begin updating again unless you delete the app on that device, and install again.
This behaviour seems to suggest that both devices become logged out or deregistered from CloudKit after a short period of time as a consequence of some unknown process within CloudKit or SwiftData. When logged out, each device cannot write to, or read from, CloudKit.
Now, the problem with trying to fix the issue is that the registration with CloudKit, silent push notifications, and fetches, are all managed "under the hood" by SwiftData.
In the production environment, you cannot configure any registration state or refresh registration within your code, because you don't have access to the parts of SwiftData that manage this.
I wanted to find out if this is an issue that has been encountered by other users, and if anyone can help with a solution, or perhaps a debugging strategy that I can use to find out what is happening.
Thank you all in advance.
I made simple school & student model with RelationShip
import SwiftUI
import SwiftData
@Model final class School {
var name: String
@Relationship(deleteRule: .cascade, inverse: \Student.school)
var studentList: [Student] = [Student]()
init(name: String) {
self.name = name
}
}
@Model final class Student {
var name: String
var school: School?
init(name: String) {
self.name = name
}
}
~
struct SchoolView: View {
@Environment(\.modelContext) private var modelContext
@Query(sort: \School.name) var schools: [School]
@Query(sort: \Student.name) var students: [Student]
var body: some View {
NavigationStack {
List {
ForEach(schools) { school in
NavigationLink {
StudentView(school: school)
} label: {
HStack {
Text(school.name)
Spacer()
Text("\(school.studentList.count)")
}
}
.swipeActions {
Button(role: .destructive) {
deleteSchool(school)
} label: {
Label("Delete", systemImage: "trash")
}
}
}
.onDelete(perform: deleteSchools(at:))
}
.navigationTitle("Home")
.listStyle(.plain)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
addSchool()
} label: {
Image(systemName: "plus")
}
}
ToolbarItem(placement: .topBarLeading) {
VStack(alignment: .leading) {
Text("School: \(schools.count)")
Text("Student: \(students.count)")
}
}
}
}
}
private func randomString() -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<5).map { _ in letters.randomElement()! })
}
private func addSchool() {
let newSchool = School(name: randomString())
modelContext.insert(newSchool)
}
private func deleteSchools(at offsets: IndexSet) {
offsets.map { schools[$0] }.forEach(deleteSchool)
}
private func deleteSchool(_ school: School) {
modelContext.delete(school)
try? modelContext.save()
}
}
struct StudentView: View {
@Environment(\.modelContext) private var modelContext
var school: School
var body: some View {
List {
ForEach(school.studentList) { student in
Text(student.name)
}
.onDelete(perform: deleteStudents(at:))
}
.navigationTitle("Home")
.navigationBarTitleDisplayMode(.inline)
.listStyle(.plain)
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
Button {
addStudent()
} label: {
Image(systemName: "plus")
}
}
}
}
private func addStudent() {
let newSchool = Student(name: randomString())
modelContext.insert(newSchool)
newSchool.school = school
school.studentList.append(newSchool)
}
private func randomString() -> String {
let letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
return String((0..<5).map { _ in letters.randomElement()! })
}
private func deleteStudents(at offsets: IndexSet) {
withAnimation {
offsets.forEach {
let student = school.studentList[$0]
modelContext.delete(student)
try? modelContext.save()
}
}
}
}
I try my best to follow the SampleTrips app from Apple developer.
When I delete one of the schools, then all the students in the school should be deleted. But the student count never changes.
Deleting the student itself works well - the student count decreases.
Thank you!
Hi,
My app has been receiving a huge increase in the number of CKHTTPStatus=503 errors over the past couple of months. I created a thread before, and also a Feedback (FB13300807) over a month ago, but I haven't gotten any assistance on this, and am wondering if there is any better way to get the attention of a CloudKit engineer who might be able to help.
From my users, I was able to print out the error code and error userInfo in the console:
error.code == 15 (the same as CKErrorServerRejectedRequest),
UserInfo={ContainerID=, CKHTTPStatus=503, RequestUUID=17C6B9B9-35DD-411B-8AED-7A497075D228, OperationID=5285362CCD2DDB32}}, CKHTTPStatus=503}
How can I get this issue addressed? A lot of users are reporting this issue and it's creating a big support burden.
I have a shopping list app and the data model is very simple, just 5 entities. These have names List, Product, Item, Section and Data. For CoreData I was able to use the auto-generated classes and gave my own names for the classes (prefixing my entity names with CD - e.g CDList)
However with SwiftData I don't have that capability. My @Model classes are named List, Data etc and obviously these clash with the standard Swift/SwiftUI classes. Even the much quoted example app has the same issue a task todo list with an entity named Task, which would clash with Swift's own Task class.
Is the correct/only approach that I don't use the @Model macro but roll my own SwiftData classes - e.g inline the macros, or is there a way I can give the @Model classes my own name (e.g SDList, and leave the CoreData entity names as they are). I don't really want to rename my entities if at all avoidable.
I tried to practice SwiftData by creating a LinkedList. However, contrary to my expectations.
even though a.next == b
but b.next != nil, b.next == a. Why is that?
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
Button {
var a = Item(value: 0)
var b = Item(value: 1)
a.next = b
modelContext.insert(a)
} label: {
Text("+++")
}
@Model
final class Item {
var value: Int
weak var next: Item?
init(value: Int, next: Item? = nil) {
self.value = value
self.next = next
}
}
this is whole code
import SwiftUI
import SwiftData
struct ContentView: View {
@Environment(\.modelContext) private var modelContext
@Query private var items: [Item]
var body: some View {
ZStack {
VStack {
ForEach(items){item in
Button {
modelContext.delete(item)
} label: {
Text(String(item.value))
}
}
}
}
VStack {
Button {
var a = Item(value: 0)
var b = Item(value: 1)
a.next = b
modelContext.insert(a)
// modelContext.insert(b)
// let fetch = FetchDescriptor<Item>(
// predicate: #Predicate{$0.value == 0}
// )
// let data = try! modelContext.fetch(fetch)
// data[0].next = b
} label: {
Text("+++")
}
Button {
for i in items {
modelContext.delete(i)
}
} label: {
Text("---")
}
Button {
for i in items {
print(i.value)
print(i.next)
}
} label: {
Text("Print")
}
}
}
}
#Preview {
ContentView()
.modelContainer(for: Item.self, inMemory: true)
}
Very simple one. how do I add “create“ permissions for my CloudKit security roles. For some reason “create“ is greyed out when I try to check the box for _creator
Starting today around 2PM MST I started receiving emails from users reporting their CloudKit synced data was 'completely gone'...
They are reporting no error messages... so that means the CKQuery on the private database is completing without error, but simply not returning any previously saved records...
This appears to be only occurring to a percentage of my users and I cannot replicate it on my device (I'm using iOS 17.1.2.
Are any other developers experiencing this?!