Recently, I have made quite extensive changes to my schema and need to migrate my CoreData + CloudKit model to a new version. The changes require me to use a custom NSEntityMigrationPolicy because I have a rather complex mapping between some old entities and new entities.
I have added a new model version. Deleted some entities, and added some entities.
I have defined the NSEntityMigrationPolicy with createDestinationInstances(forSource:in:manager:) and createRelationships(forDestination:in:manager:).
I have created a new Core Data Mapping Model. This was probably unnecessary.
Within the Core Data Mapping Model, I have specified Custom Policy for all entities.
In my container setup, I added two options, as below:
storeDescription.setOption(false as NSNumber, forKey: NSMigratePersistentStoresAutomaticallyOption)
storeDescription.setOption(false as NSNumber, forKey: NSInferMappingModelAutomaticallyOption)
I mostly followed a "Core Data Heavyweight Migration" guide. I'm unable to share the link, but it can be quite easily found on the web. (It's not for CloudKit specifically.)
When I run my app, I am getting the most basic of errors, which is:
The managed object model version used to open the persistent store is incompatible with the one that was used to create the persistent store.
So I guess that migration wasn't even attempted, which makes sense because I set NSMigratePersistentStoresAutomaticallyOption to false.
I tried to go beyond what I could find on the web and attempted to manually initialize migration:
let sourceModel = container.persistentStoreCoordinator.managedObjectModel
guard let modelURL = Bundle.main.url(forResource: "MyModelName", withExtension: "xcdatamodeld") else {
fatalError("Unable to locate model file.")
}
guard let destinationModel = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Unable to load destination model.")
}
let migrationManager = NSMigrationManager(sourceModel: sourceModel, destinationModel: destinationModel)
let mappingModel = NSMappingModel(from: [Bundle.main], forSourceModel: sourceModel, destinationModel: destinationModel)!
migrationManager.currentEntityMapping.entityMigrationPolicyClassName = "MyMigrationPolicyClassName"
I am then stuck at the migrateStore(from:type:mapping:to:type:) method. It seems to target only local storage, not CloudKit. Otherwise, what do I provide for URLs and types?
Any guidance for custom logic CoreData with CloudKit migration would be greatly appreciated.
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 have an Apple app that uses SwiftData and icloud to sync the App's data across users' devices. Everything is working well. However, I am facing the following issue:
SwiftData does not support public sharing of the object graph with other users via iCloud. How can I overcome this limitation without stopping using SwiftData?
Thanks in advance!
I’m using NSPersistentCloudKitContainer and I’m utilising the public database and also the user’s private database.
For example I have an entity called Category which has a many-to-many relationship to an entity called NewsArticle. So the NewsArticles exist in the public database for the user to browse, but he can add them to a category which will live in his private database. So that’s my question, is it possible for an entity which exists a in the private database to have a relationship to another entity in a public database?
Cloudkit.js user authentication cannot work any more.
This causes one of my web apps which has been working for quite a few years stop to work. Can someone fix it?
You can reproduce the error with the official CloudKitJS catalog: https://cdn.apple-cloudkit.com/cloudkit-catalog/#authentication
It's been frustrating to solve this error. My iOS device and Xcode are fully updated. I can easily run app on simulator, but issue happens on my iPhone.
dyld[23479]: Symbol not found: _$s9SwiftData12ModelContextC6insert6objectyx_tAA010PersistentC0RzlFTj
Referenced from: <6FC773BB-E68B-35A9-B334-3FFC8B951A4E> Expected in: /System/Library/Frameworks/SwiftData.framework/SwiftData
I have set up the app identifier in Apple developer with the document URL set up for iCloud and also updated the info.plist file and entitlements according to this.
info.plist
<dict>
<key>iCloud.com.abc.MyApp</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerName</key>
<string>MyApp</string>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
</dict>
</dict>
<key>UIFileSharingEnabled</key>
<true/>
<key>LSSupportsOpeningDocumentsInPlace</key>
<true/>
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
<key>NSUbiquitousContainersUsageDescription</key>
<string>This app uses iCloud containers to store and sync documents.</string>
Entitlement.plist
<array>
<string>iCloud.com.abc.MyApp</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudDocuments</string>
</array>
<key>com.apple.developer.ubiquity-container-identifiers</key>
<array>
<string>iCloud.com.abc.MyApp</string>
</array>
// Then I am using iCloud for CRUD operation in the app
// Code snippet
{
try {
var iCloudDocumentsURL = NSFileManager.DefaultManager.GetUrlForUbiquityContainer(null);
if (iCloudDocumentsURL != null)
{
var path = iCloudDocumentsURL.ToString().Replace("%C3%97", "x");
var filepath = path.Replace("file://", string.Empty).Replace("%20", " ");
var destinationdirectoryPath = Path.combine(filePath,"MyAppDocuments");
if (Directory.Exists(destinationdirectoryPath))
{
Directory.Delete(destinationdirectoryPath, recursive: true);
}
}
}catch(Exception ex) {
LogHandler.LogError(ex);
}
}
But in Delete operation gives Exception -> System.IO.IOException: Access to the path '/Users/USERABC/Library/Mobile Documents/iCloudcomabc~MyApp/MyAppDocuments' is denied.
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)
}
I have spent hours trying to get @Query macros to compile. Mostly they throw up meaningless errors for example the following produces 3 compiler errors:
@Query var stylesheets: [StyleSheet]
Here's the expansion.
The compiler complains that 'private' can't be used here, and it can't find _stylesheets. I searched everywhere to find a resolution then I came across the Query struct. I used it as follows to replace the @Query:
let query = Query(FetchDescriptor<StyleSheet>(), animation: .smooth)
let styleSheets = query.wrappedValue
This also solves another issue that was bugging me - how to get the context when the environment variable is often rejected. All I need to do now is write:
let context = query.modelContext
None of the WWDC23 SwiftData videos mentions the use of the struct, which is a shame. It feels much like the CoreData approach to fetching data.
I hope this helps some of you.
I am following this document from Apple to implement sharing with CloudKit. In it, Apple says
NSPersistentCloudKitContainer uses CloudKit zone sharing to share objects. Each share has its own record zone on the CloudKit server. CloudKit has a limit on how many record zones a database can have.
What is the record zone limit for a private CloudKit database?
I can find information about record and participant limits but not on record zone limits.
Dear all,
I'm building an app leveraging SwiftData and I have the following two classes:
Stagione:
import SwiftData
@Model
class Stagione {
@Attribute(.unique) var idStagione: String
var categoriaStagione: String
var miaSquadra: String
@Relationship(deleteRule: .cascade) var rosa: [Rosa]?
@Relationship(deleteRule: .cascade) var squadra: [Squadre]?
@Relationship(deleteRule: .cascade) var partita: [CalendarioPartite]?
init(idStagione: String, categoriaStagione: String, miaSquadra: String) {
self.idStagione = idStagione
self.categoriaStagione = categoriaStagione
self.miaSquadra = miaSquadra
}
}
Squadre:
import SwiftData
@Model
class Squadre {
var squadraCampionato: String
var stagione: Stagione?
init(squadraCampionato: String) {
self.squadraCampionato = squadraCampionato
}
}
Now, I have a view in which I'm calling a sheet to insert some Squadre:
// Presenta il foglio per aggiungere una nuova partita
GroupBox(label: Text("Dettagli Partita").font(.headline).padding()) {
VStack {
HStack {
Text("Giornata:")
TextField("Giornata", text: $idGiornata)
.frame(width: 30)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
DatePicker("Data Partita:", selection: $dataPartita, displayedComponents: .date)
.padding()
HStack {
Text("Squadra Casa:")
.frame(width: 150)
TextField("Squadra Casa", text: $squadraCasa)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("Gol Casa", text: $golCasa)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
HStack {
Text("Squadra Trasferta:")
.frame(width: 150)
TextField("Squadra Trasferta", text: $squadraTrasferta)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
TextField("Gol Trasferta", text: $golTrasferta)
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
}
HStack {
Button("Salva") {
if let partitaSelezionata = partitaSelezionata {
// Se è stata selezionata una partita, aggiorna i suoi dati
if let index = partite.firstIndex(where: { $0.id == partitaSelezionata.id }) {
partite[index].idGiornata = Int(idGiornata) ?? 0
partite[index].dataPartita = dataPartita
partite[index].squadraCasa = squadraCasa
partite[index].golCasa = Int(golCasa) ?? 0
partite[index].squadraTrasferta = squadraTrasferta
partite[index].golTrasferta = Int(golTrasferta) ?? 0
}
} else {
// Altrimenti, aggiungi una nuova partita
aggiungiPartita(stagione: stagione)
}
// Chiudi il foglio di presentazione
mostraAggiungiPartita = false
// Resetta il campo di input
idGiornata = ""
dataPartita = Date()
squadraCasa = ""
golCasa = ""
squadraTrasferta = ""
golTrasferta = ""
}
.buttonStyle(.borderedProminent)
.disabled(idGiornata.isEmpty || squadraCasa.isEmpty || squadraTrasferta.isEmpty || golCasa.isEmpty || golTrasferta.isEmpty)
// Bottone Chiudi
Button("Chiudi") {
mostraAggiungiPartita = false
}
.buttonStyle(.borderedProminent)
}
}
.padding()
}
}
I'd like to insert a autocomplete function in the textfields "Squadra Casa" and "Squadra Trasferta", based on the list of Squadre contained in the class "Squadre" and filtered for a specific Stagione.
Has anybody of you made something similar? Do you have any suggestions or code example which I can use?
Thanks,
A.
How can we identify whether the remote change notification is triggered because some data was changed on a different device and it is downloaded from CloudKit, or it is triggered from the current device because new entity was saved into database.
Because this notification is posted when both remote data is downloaded or local data is created. It would be great if there is a way to understand the origin of the notification.
CoreData: Zone metadata is missing it's encoded share data but is marked for a mutation: <CKRecordZoneID: 0x600003f044e0; zoneName=com.apple.coredata.cloudkit.share.30BE5FAE-3FE0-4142-90C4-E78FFA90B2A2, ownerName=defaultOwner> - <decode: bad range for [%@] got [offs:312 len:1242 within:0]>
I have a CoreData model, in a Swift Package with tests.
I can successfully run the tests, when I load the model using NSPersistentContainer, however attempting this with NSPersistentCloudKitContainer always fails in the call to loadPersistentStores(completionHandler block: @escaping (NSPersistentStoreDescription, Error?) -> Void)
The error is : [CK] BUG IN CLIENT OF CLOUDKIT: Not entitled to listen to push notifications. Please add the 'aps-connection-initiate' entitlement.
This issue is described on StackOverflow however, the accepted solution does not appear to work in my case.
I have tried adding the aps-connection-initiate key to my App Info.plist, the Test Info.plist and the SPM bundle. No change. (NB. adding this to the entitlements file just breaks auto signing).
I have enabled App entitlements for App Groups, Remote Notifications background mode, Push Notifications and iCloud CloudKit with a store identifier.
I have checked my model. All relationships have inverses. No unique constraints, etc. etc.
I am sharing a Bundle ID with a previous version of the App, that also uses CoreData+CloudKit, each version has it's own Model and container identifier, each container identifier is available in the provisioning profile.
The new version of the model has two NSPersistentStoreDescriptions, one is configured for CloudKit, the other is a local cache.
I am completely stuck, suggestions would be very welcome.
I deployed Xcode to iPhone, emulator and iPhone, and iCloud data is synchronized. But I am using testlight for distribution, so I cannot synchronize. I did not respond in iCloud Kit Database developer mode, and now I have changed to production mode, but there is no response, so I cannot synchronize. That's why.
I am new to swift. This is my Item.swift.
import SwiftData
@Model
final class Item: Codable {
var id: String
var soundId: String
var soundAppleId: String
var soundType: String
var type: String
var authorId: String
var text: String
var createdAt: Date
var actionsCount: Int
var chainsCount: Int
var rating: Int
var loved: Bool
var replay: Bool
var heartedByUser: Bool
@Relationship var author: Author?
init(id: String, soundId: String, soundAppleId: String, soundType: String, type: String, authorId: String, text: String, createdAt: Date, actionsCount: Int, chainsCount: Int, ratings: Int, loved: Bool, replay: Bool, heartedByUser: Bool, author: Author) {
self.id = id
self.soundId = soundId
self.soundAppleId = soundAppleId
self.soundType = soundType
self.type = type
self.authorId = authorId
self.text = text
self.createdAt = createdAt
self.actionsCount = actionsCount
self.chainsCount = chainsCount
self.rating = ratings
self.loved = loved
self.replay = replay
self.heartedByUser = heartedByUser
self.author = author
}
private enum CodingKeys: String, CodingKey {
case id
case soundId = "sound_id"
case soundAppleId = "sound_apple_id"
case soundType = "sound_type"
case type
case authorId = "author_id"
case text
case createdAt = "created_at"
case actionsCount = "actions_count"
case chainsCount = "chains_count"
case rating
case loved
case replay
case heartedByUser
case author
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
soundId = try container.decode(String.self, forKey: .soundId)
soundAppleId = try container.decode(String.self, forKey: .soundAppleId)
soundType = try container.decode(String.self, forKey: .soundType)
type = try container.decode(String.self, forKey: .type)
authorId = try container.decode(String.self, forKey: .authorId)
text = try container.decode(String.self, forKey: .text)
let dateString = try container.decode(String.self, forKey: .createdAt)
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
if let date = formatter.date(from: dateString) {
createdAt = date
} else {
throw DecodingError.dataCorruptedError(forKey: .createdAt,
in: container,
debugDescription: "Date string does not match format expected by formatter.")
}
actionsCount = try container.decode(Int.self, forKey: .actionsCount)
chainsCount = try container.decode(Int.self, forKey: .chainsCount)
rating = try container.decode(Int.self, forKey: .rating)
loved = try container.decode(Bool.self, forKey: .loved)
replay = try container.decode(Bool.self, forKey: .replay)
heartedByUser = try container.decode(Bool.self, forKey: .heartedByUser)
author = try container.decode(Author.self, forKey: .author)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(soundId, forKey: .soundId)
try container.encode(soundAppleId, forKey: .soundAppleId)
try container.encode(soundType, forKey: .soundType)
try container.encode(type, forKey: .type)
try container.encode(authorId, forKey: .authorId)
try container.encode(text, forKey: .text)
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
let dateString = formatter.string(from: createdAt)
try container.encode(dateString, forKey: .createdAt)
try container.encode(actionsCount, forKey: .actionsCount)
try container.encode(chainsCount, forKey: .chainsCount)
try container.encode(rating, forKey: .rating)
try container.encode(loved, forKey: .loved)
try container.encode(replay, forKey: .replay)
try container.encode(heartedByUser, forKey: .heartedByUser)
try container.encode(author, forKey: .author)
}
}
@Model
final class Author: Codable {
var id: String
var image: URL
var username: String
var bio: String?
init(id: String, image: URL, username: String, bio: String?) {
self.id = id
self.image = image
self.username = username
self.bio = bio
}
private enum CodingKeys: String, CodingKey {
case id
case image
case username
case bio
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
image = try container.decode(URL.self, forKey: .image)
username = try container.decode(String.self, forKey: .username)
bio = try container.decodeIfPresent(String.self, forKey: .bio)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
try container.encode(image, forKey: .image)
try container.encode(username, forKey: .username)
try container.encodeIfPresent(bio, forKey: .bio)
}
}
In my ItemView when I try to access something inside author, Swift preview crashes.
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 SwiftData 0x1cb459e90 0x1cb3d3000 + 552592
1 SwiftData 0x1cb45ba7c 0x1cb3d3000 + 559740
2 SwiftData 0x1cb45e5f8 0x1cb3d3000 + 570872
3 SwiftData 0x1cb4190e4 0x1cb3d3000 + 286948
4 audition 0x100b436e0 Item.author.getter + 320 (@__swiftmacro_8audition4ItemC6author18_PersistedPropertyfMa_.swift:9)
5 ContentView.1.preview-thunk.dylib 0x105f23a20 closure #1 in closure #1 in closure #1 in closure #1 in ItemCard.__preview__body.getter + 820 (ContentView.swift:89)
6 SwiftUI 0x1cba41308 0x1cb47b000 + 6054664
7 ContentView.1.preview-thunk.dylib 0x105f22ee4 closure #1 in closure #1 in closure #1 in ItemCard.__preview__body.getter + 472 (ContentView.swift:84)
8 SwiftUI 0x1cc2e6c40 0x1cb47b000 + 15121472
9 ContentView.1.preview-thunk.dylib 0x105f228b8 closure #1 in closure #1 in ItemCard.__preview__body.getter + 388 (ContentView.swift:83)
...
A few months back, I launched an app that operated solely on a local level. Recently, I've begun the process of integrating it with CloudKit, and so far, the model integration has been successful. I've utilized SwiftData for this task, making it relatively straightforward to adjust the models, as shown below:
`
@Relationship(deleteRule: .cascade, inverse: \ItemForCategory.category)
var itemForCategory : [ItemForCategory]? = [ItemForCategory]()
`
In my initial version of the code, the widget functioned perfectly. However, I've encountered an error recently stating Missing return in instance method expected to return 'ItemForCategory?'.
@MainActor
private func getLastItem () -> ItemForCategory? {
guard let modelContainer = try? ModelContainer(for: Category.self) else {
return nil
}
let descriptor = FetchDescriptor<Category>()
let appCategories = try? modelContainer.mainContext.fetch(descriptor)
let lastItem = appCategories?.compactMap { $0.itemForCategory }.last
return lastItem
}
The error surfaces at the return line of code. I'm hopeful that someone can assist me in resolving this issue. Thank you very much.
I have a Category model that's defined like so:
@Model
final class Category {
@Attribute(.unique) var id: UUID
var name: String
var parent_id: UUID? //categories can be children of other categories
init(id: UUID, name: String, parent_id: UUID?) {
self.id = id
self.name = name
self.parent_id = parent_id
}
}
And I'm getting my categories from an API call and putting it into my View:
import SwiftUI
import SwiftData
struct CategoryView: View {
@Environment(\.modelContext) private var modelContext
@Query private var categories: [Category]
@Query(filter: #Predicate<Category>{ $0.parent_id == nil })
private var top_level_categories: [Category]
var spacing: CGFloat = 25
var body: some View {
HStack() {
Text("Categories")
.font(.title.bold())
Spacer()
Text("see all")
}
.padding([.bottom, .top], 0)
VStack(spacing: 20) {
ScrollView(.horizontal) {
HStack(spacing: spacing) {
ForEach(top_level_categories) { category in
Text(category.name!)
}
}
}
}
.onAppear{
getCategories()
}
}
func getCategories() {
get_refresh_token { token in
guard let token = token else {
return
}
var urlRequest = URLRequest (url: URL(string:"https://api.test.com/categories")!)
urlRequest.httpMethod = "GET"
urlRequest.addValue("application/json", forHTTPHeaderField: "Content-Type")
urlRequest.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
URLSession.shared
.dataTask(with: urlRequest) { (data, response, error) in
do {
if let data = data {
let c = try JSONDecoder().decode([Category].self, from: data)
c.forEach { modelContext.insert($0) }
try? modelContext.save()
}
}
catch {
print(error)
}
}.resume()
}
}
}
This runs fine the first time I run the app but when I run it again, I get the following error:
ForEach<Array<Category>, UUID, Text>: the ID XXXXXX-XXXX-XXXX-XXXX-XXXXXX occurs multiple times within the collection, this will give undefined results!
Not sure why this is happening since I thought putting the @Attribute(.unique) on ID means that the same category won't get added twice?
I am currently writing an app that is about writing stories. As of now, it is fairly simple: ContentView is your "Collection" of stories. PopupView is when you click on a button in ContentView. In popup view you enter the story title. Once you do that, you are brought to a blank page which is StoryView, where the NavigationTitle is what your story title is. Once I finish the story and leave the page / StoryView, it is still there, but once I close the app on my phone and reopen it, the story is gone and is not saved. I am a relatively new developer, so ive been relying on ChatGPT and Google Gemini for the saving parts of this, but it rarely works, and the furthest ive gotten with it is that it saves the story title but doesn't save the content of the story. I have a feeling that the AI is overdoing it as well.
If anyone could help, please do so. Ive been trying to fix this for days. If you need me to provide any code, I am happy to do so.
[Edited by Moderator]
It still didn't work when I signed in again
I know that this has been posted many times, but I am facing an issue where I can't save a model instance. I know that this is due to enums from others trying to find solutions.
The error that shows:
CoreData: error: Row (pk = 2) for entity 'ResidentInfo' is missing mandatory text data for property 'name'
Solutions that I've tried:
removed all enums from their respective structs to being standalone.
making sure all enum calls use the full keyPath
removing CustomStringConvertible from all enums and moving them to rawValue strings
updating Xcode to 15.4b
probably other things I've forgotten at this point
File is too long to fit in post or comments (I'm sorry it's insanely long, it's a complex model), so I'll try to fit what I can where, are there anythings that stand out that may be an issue?
Enums:
enum Gender: String, CaseIterable, Codable {
case male = "Male"
case female = "Female"
case transmale = "Trans Male"
case transfemale = "Trans Female"
case nonbinary = "Nonbinary"
case other = "Other"
}
enum BodyBuild: String, CaseIterable, Codable {
case small = "Small"
case medium = "Medium"
case large = "Large"
}
enum Status: String, Codable {
case incomplete, complete
}
enum HairColor: String, CaseIterable, Codable {
case black = "Black"
case blonde = "Blonde"
case dirtyBlonde = "Dirty Blonde"
case brown = "Brown"
case lightBrown = "Light Brown"
case darkBrown = "Dark Brown"
case red = "Red"
}
enum EyeColor: String, CaseIterable, Codable {
case blue = "Blue"
case brown = "Brown"
case black = "Black"
case green = "Green"
case hazel = "Hazel"
case gray = "Gray"
case heterochromatic = "Heterochromatic"
}
enum RaceEthnicity: String, CaseIterable, Codable {
case native = "American Indian or Alaska Native"
case asian = "Asian"
case black = "Black or African American"
case pi = "Native Hawaiian or Other Pacific Islander"
case white = "White"
case mixed = "2 or More Races/Mixed"
}
enum MarkType: String, CaseIterable, Codable {
case birthMark = "Birth Mark"
case scar = "Scar"
case tattoo = "Tattoo"
case piercing = "Piercing"
case other = "Other"
}
enum heightFormatStyle: String {
case short, long, hash
}
enum suicideRiskLevel: String, Codable {
case low = "Low"
case medium = "Medium"
case high = "High"
}
enum MedicationFrequency: String, CaseIterable, Codable {
case daily = "Daily"
case weekly = "Weekly"
case biweekly = "Bi-Weekly"
case monthly = "Monthly"
case asNeeded = "As Needed"
case temporary = "Temporary Use"
case other = "Other"
}
enum SchoolOption: String, CaseIterable, Codable {
case ged = "GED"
case communityCollege = "Community College"
case university = "4-Year College"
}
enum FamilyDiseaseTypes: String, CaseIterable, Codable {
case alcoholism = "Alcoholism"
case asthma = "Asthma"
case cancer = "Cancer"
case drugAbuse = "Drug Abuse"
case hypertension = "High Blood Pressure"
case nervousBreakdown = "Nervous Breakdown"
case sca = "Sickle Cell Anemia"
case seizures = "Convulsions, Seizures Epilepsy"
case allergies = "Allergies"
case birthDefect = "Birth Defect"
case diabetes = "Diabetes"
case heartDisease = "Heart Disease"
case migraines = "Migraine Headaches"
case obesity = "Obesity"
case tuberculosis = "Tuberculosis"
case thyroid = "Glandular/Thyroid Issues"
}
Structs:
struct IdentifyingInformation: Codable {
var hairColor: HairColor = HairColor.black
var height: Height = Height(feet: 5, inches: 8)
var race: RaceEthnicity = RaceEthnicity.native
var build: BodyBuild = BodyBuild.small
var eyeColor: EyeColor = EyeColor.blue
var hispanic: Bool = false
var distinguishingMarks: [DistinguishingMark] = []
var currentLivingSitutation: String = ""
var currentSupportServices: String = ""
struct DistinguishingMark: Codable, Identifiable {
var id: UUID = UUID()
var type: MarkType = MarkType.tattoo
var location: String = ""
var description: String = ""
var photos: [Data] = []
}
struct Height: Codable {
var feet: Int
var inches: Int
func formatted(formatStyle: HeightFormatStyle = .short) -> String {
switch formatStyle {
case HeightFormatStyle.hash:
return "\(feet)'\(inches)\""
case HeightFormatStyle.long:
return "\(feet) feet \(inches) inches"
case HeightFormatStyle.short:
return "\(feet)ft \(inches)in"
}
}
}
}
struct EmergencyNeeds: Codable {
var food: Bool = false
var shelter: Bool = false
var clothing: Bool = false
var hygeine: Bool = false
var medical: Bool = false
var mentalHealth: Bool = false
var otherNeeds: String = ""
var abuseNeglectAllegations: Bool = false
var abuseReport: AbuseReport? = nil
struct AbuseReport: Codable {
var reportAccepted: Bool = false
var whoAccepted: String = ""
var reportNumber: String = ""
}
}
struct RiskAssessment: Codable {
var selfHarm: Bool = false
var selfHarmAttempt: Bool = false
var selfHarmExplanation: String? = nil
var currentFeelings: String? = nil
var dangerToOthers: Bool = false
var dangerToOthersExplanation: String? = nil
var suicideAssessmentRequired: Bool {
return selfHarm || dangerToOthers
}
}
struct SuicideAssessment: Loopable, Codable {
var dateCompleted: Date = Date()
var familyMember: Bool = false
var treatedMentalIllness: Bool = false
var chronicIllness: Bool = false
var historyOfSelfDestructiveBehavior: Bool = false
var recentLoss: Bool = false
var isolatedAloneHopeless: Bool = false
var goodbyeNoteOrPosessionDelegation: Bool = false
var plan: Bool = false
var betterOff: Bool = false
var wishToDie: Bool = false
var manualReview: Bool = false
var comments: String = ""
var helpCallMade: Bool = false
var riskLevel: suicideRiskLevel {
let riskScore = try? allProperties().reduce(0) { result, property in
if let isTrue = property.value as? Bool, isTrue {
return result + 1
} else {
return result
}
}
switch riskScore! {
case 0 ... 3:
return .low
case 4 ... 7:
return .medium
default:
return .high
}
}
}
[Continued in comments]