SwiftData

RSS for tag

SwiftData is an all-new framework for managing data within your apps. Models are described using regular Swift code, without the need for custom editors.

Posts under SwiftData tag

200 Posts
Sort by:

Post

Replies

Boosts

Views

Activity

SwiftData modelContainer Error
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
2
3
905
May ’24
Swiftdata Model crash
I am converting a large core data app to SwiftData and have hit a problem I can't fix, namely code generated by a Model crashing. Here is the model showing the failing expansion. Here is the model containing the inverse for the failing attribute: /Users/writingshedprod/Desktop/Screenshot 2024-05-08 at 20.21.51.png And here is the mass of core data output generated when the app launches. If anyone can make sense of this I'd be grateful. This is where the problem might lie. Debugger.txt
0
0
242
May ’24
SwiftData enum No exact matches in call to instance method 'setValue' error
I'm getting an "No exact matches in call to instance method 'setValue'" error for a property that has a enum as a value. How do I fix this? Any help would be appreciated. enum FluidUnit: CaseIterable, Identifiable { case ounce, liter var id: Self { self } var title: String { switch self { case .ounce: return "ounce" case .liter: return "liters" } } } @Model class Drink: Identifiable, Hashable { let id: UUID = UUID() let name: String = "" var shortName: String = "" var amount: Double = 0.0 let unitOfMeasure: FluidUnit = FluidUnit.ounce let date: Date = Date() var image: String = "water" var favorite: Bool = false init(name: String, amount: Double, unitOfMeasure: FluidUnit, image: String, favorite: Bool = false, shortName: String = "") { self.id = UUID() self.name = name self.amount = amount self.unitOfMeasure = unitOfMeasure self.date = Date() self.image = image self.favorite = favorite self.shortName = shortName } static func == (lhs: Drink, rhs: Drink) -> Bool { lhs.id == rhs.id } func hash(into hasher: inout Hasher) { hasher.combine(id) } }
1
0
300
May ’24
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
607
May ’24
Xcode error
I just purchased a new M3 chip MacBook Air a little after it came out. I was able to create and deploy an iOS application. However, now that I am trying to create a new project Xcode throws an error upon launch. Steps to recreate the error (if possible) Open Xcode Select create new app An error is where the preview should be. At this point I have added no code. It is all boiler plate from Apple.
3
0
370
May ’24
@Query macro vs Query struct
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.
1
0
328
May ’24
Autocomplete with SwiftData
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.
2
0
263
May ’24
CLMonitor Add region after starting to monitor for event changes
I am currently working on an app that uses the CLMonitor to check when the user has entered a specific region. When the user enters the region, a new region should be monitored, and the old region should be removed. Currently, I have a startMonitoring() method that contains the event handling logic: func startMonitoringConditions() { Task { monitor = await CLMonitor(MonitorNames.monitorName) if let identifiers = await monitor?.identifiers { if identifiers.count == 0 { if let coordinate = manager.location?.coordinate { await addNewRegionAtCoordinate(coordinate: coordinate) } } else { print("Previous Monitor Region is used.") } } for try await event in await monitor!.events { if let coordinate = manager.location?.coordinate { // do something... await monitor!.remove(event.identifier) await addNewRegionAtCoordinate(coordinate: coordinate) } } } } Unfortunately, adding a new region will not update the events collection in the CLMonitor, so the new region's events will not be handled in this method. Any help on how I could fix this problem would be greatly appreciated!
5
1
801
May ’24
SwiftData app always at 100% cpu when idle after upgrading to Xcode 15.3
Our app is using SwiftData + SwiftUI. After upgrading to Xcode 15.3 Beta, RC1, RC2, we’ve experienced the same issue that the app is having 100% CPU activity even when left idle. This happens as long as the SwiftUI is using @Query to fetch SwiftData and the query does return at least 1 result (there is no issue if the query returns empty result). We’ve tried the following and therefore confirmed that this is a Xcode 15.3 beta issue: Xcode 15.3 Beta + iOS 17.2 -> 100% CPU activity when idle Xcode 15.3 Beta + iOS 17.4 beta -> 100% CPU activity when idle Xcode 15.2 Beta + iOS 17.2 -> normal This only happens during debug run. When profiling / using instrument, this doesn't happen.
16
5
1.8k
May ’24
SwiftData nested models crash upon trying to access nested data.
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) ...
3
1
328
Apr ’24
Widget error integrate it with CloudKit
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.
0
0
217
Apr ’24
ForEach complaining about duplicate values with SwiftData using Unique attribute
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?
1
0
261
Apr ’24
How to save stories
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]
0
0
245
Apr ’24
Regarding the Preview issue with SwiftData
I referred to the code provided in the article Adding and Editing Persistent Data in Your App in the SwiftData documentation and tested the "/PreviewHelper" approach. I found that if a model class contains a property of a struct, and that struct includes a Set property or an Array property, the Preview would throw an error. Here is an example of the model: @Model class Book { // ... var someStruct: SomeStruct // ... } struct SomeStruct: Codable { // ... var someCollection: Set<Int> // ... } In actuality, this model can run and store data without any issues, but it fails to run in the Preview. Is there a problem with the implementation of this Preview or is there any other reason? Are there better ways to perform Preview in SwiftData?
1
0
299
Apr ’24
CoreData: error: Row (pk = 1) for entity 'ResidentInfo' is missing mandatory text data for property 'name'
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]
2
0
281
Apr ’24
Appintent and swiftdata
Hi, I'm new to Swift development and encountering an issue that I believe may be due to an error on my part. I have two questions regarding the following code: import AppIntents import SwiftUI import SwiftData @available(iOS 17.0, *) struct CopiarEventoIntent: AppIntent { @Environment(\.modelContext) private var context @Parameter(title: "Nome do Evento") var name: String @Parameter(title: "Data Inicial") var datai: Date @Parameter(title: "Data Final") var dataf: Date @Parameter(title: "Tipo de Evento") var tipo: String @Parameter(title: "Endereço") var endereco: String @Parameter(title: "Lembrete") var reminder: Bool static var title: LocalizedStringResource = "Adicionar Eventos" static var description = IntentDescription("Copiar Eventos e alterar datas", resultValueName: "Resultado") @MainActor func perform() async throws -> some IntentResult & ProvidesDialog { let calData = CalData(title: name, datei: datai, datef: dataf, tipo: tipo, endereco: endereco,reminder: reminder) context.insert(calData) return .result(dialog: "Evento copiado com sucesso!") } } @available(iOS 15.0, *) struct AppShortcuts: AppShortcutsProvider { static var appShortcuts: [AppShortcut] { AppShortcut( intent: CopiarEventoIntent(), phrases: [ "Copiar Evento" ], shortTitle: "Copiar Evento", systemImageName: "square.and.arrow.down" ) } } @available(iOS 15.0, *) struct ShortcutSelectionView: View { @Environment(\.modelContext) private var context @Query(sort: \CalData.title) private var caldatas: [CalData] @State private var selectedEvent: CalData? var body: some View { List(caldatas, id: \.self) { eventData in Button(action: { selectedEvent = eventData handleEventSelection() }) { Text(eventData.title) } } } private func handleEventSelection() { guard selectedEvent != nil else { return } } } When I run the shortcut, gives me the following error: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Cannot insert 'CalData' in this managed object context because it is not found in the associated managed object model. I attempted to save this using SwiftData with the following model: import SwiftData @Model class CalData { var title : String var datei : Date var datef : Date var tipo : String var endereco : String @Relationship(deleteRule: .cascade) var caldataval = [CalDataVal]() var reminder: Bool = false init(title:String, datei:Date, datef:Date, tipo:String, endereco:String, reminder:Bool){ self.title = title self.datei = datei self.datef = datef self.tipo = tipo self.endereco = endereco self.reminder = reminder } } As for the second question, how can I add a parameter that can have multiple values to save them to EventDataVal, resembling a one-to-many relationship: import SwiftData @Model class CalDataVal { var name : String var value : Double init(name:String, value:Double){ self.name = name self.value = value } } Thanks in advanced
0
0
385
Apr ’24
Using SwiftData: Model not saved when inserting from background actor
Hi everyone, I'm trying to make use of a background actor in my SwiftUI project. Inserting data works with the ModelContainer's mainContext. Another context in a ModelActor, however, fails to write into the same database. I verify the results by opening the SQLite file on the file system. While the mainContext.insert call does indeed insert a row into the table, the ModelActor's context fails to do so. There is no error or message received in the ModelActor. The property autosaveEnabled is set to true. I wrote a sample project to reproduce the issue in isolation. It consists of a single source file that introduces the Model ToDo, the ToDoView, initializes the ModelContainer and ModelActor. Please find the source code below. Is there any mistake in my approach? import SwiftUI import SwiftData @Model final class ToDo { let title: String init(title: String) { self.title = title } } @main struct testSwiftDataApp: App { @State var modelContainer: ModelContainer @State var backgroundData: BackgroundDataActor init() { let modelContainer: ModelContainer = try! ModelContainer(for: ToDo.self) self.modelContainer = modelContainer self.backgroundData = BackgroundDataActor( modelContainer: modelContainer ) } var body: some Scene { WindowGroup { ToDoView(backgroundData: self.backgroundData) .modelContainer(modelContainer) } } } struct ToDoView: View { @Environment(\.modelContext) var modelContext @Query var todos: [ToDo] let backgroundData: BackgroundDataActor var modelContainer: ModelContainer { self.modelContext.container } var body: some View { VStack { Text("Add ToDo") TextField( "", text: .constant( "URL to database: " + "\(self.modelContainer.configurations.first!.url)" ) ) // This action will be invoked on the ModelActor's context Button { Task { let todo = ToDo(title: "Step 1") await self.backgroundData.store( id: todo.persistentModelID ) } } label: { Text("Create ToDo in background") } // This action will be invoked on the mainContext Button { Task { let todo = ToDo(title: "Step 2") self.modelContainer.mainContext.insert(todo) } } label: { Text("Create ToDo in foreground") } // Show the query results VStack { Text("Available ToDos") ForEach(self.todos) { Text($0.title) } } } } } @ModelActor actor BackgroundDataActor: ModelActor { func store(id: PersistentIdentifier) { print("Trying to save") print("Is auto save enabled: \(self.modelContext.autosaveEnabled)") if let dbo = self[id, as: ToDo.self] { self.modelContext.insert(dbo) try! self.modelContext.save() print("Saved into database") } } }
1
0
369
Apr ’24
What's the correct way to delete a SwiftData model that is in a many to many relationship?
The deletion is working, but it does not refresh the view. This is similar to a question I asked previously but I started a new test project to try and work this out. @Model class Transaction { var timestamp: Date var note: String @Relationship(deleteRule: .cascade) var items: [Item]? init(timestamp: Date, note: String, items: [Item]? = nil) { self.timestamp = timestamp self.note = note self.items = items } func getModifierCount() -> Int { guard let items = items else { return 0 } return items.reduce(0, {result, item in result + (item.modifiers?.count ?? 0) }) } } @Model class Item { var timestamp: Date var note: String @Relationship(deleteRule: .nullify) var transaction: Transaction? @Relationship(deleteRule: .noAction) var modifiers: [Modifier]? init(timestamp: Date, note: String, transaction: Transaction? = nil, modifiers: [Modifier]? = nil) { self.timestamp = timestamp self.note = note self.transaction = transaction self.modifiers = modifiers } } @Model class Modifier { var timestamp: Date var value: Double @Relationship(deleteRule: .nullify) var items: [Item]? init(timestamp: Date, value: Double, items: [Item]? = nil) { self.timestamp = timestamp self.value = value self.items = items } } struct ContentView: View { @Environment(\.modelContext) private var context @Query private var items: [Item] @Query private var transactions: [Transaction] @Query private var modifiers: [Modifier] @State private var addItem = false @State private var addTransaction = false var body: some View { NavigationStack { List { Section(content: { ForEach(items) { item in LabeledText(label: item.timestamp.formatAsString(), value: .int(item.modifiers?.count ?? -1)) } .onDelete(perform: { indexSet in withAnimation { for index in indexSet { context.delete(items[index]) } } }) }, header: { LabeledView(label: "Items", view: { Button("", systemImage: "plus", action: {}) }) }) Section(content: { ForEach(modifiers) { modifier in LabeledText(label: modifier.timestamp.formatAsString(), value: .currency(modifier.value)) } .onDelete(perform: { indexSet in indexSet.forEach { index in context.delete(modifiers[index]) } }) }, header: { LabeledView(label: "Modifiers", view: { Button("", systemImage: "plus", action: {}) }) }) Section(content: { ForEach(transactions) { transaction in LabeledText(label: transaction.note, value: .int(transaction.getModifierCount())) } .onDelete(perform: { indexSet in withAnimation { for index in indexSet { context.delete(transactions[index]) } } }) }, header: { LabeledView(label: "Transactions", view: { Button("", systemImage: "plus", action: {addTransaction.toggle()}) }) }) } .navigationTitle("Testing") .sheet(isPresented: $addTransaction, content: { TransactionEditor() }) } } } } Here's the scenario. Create a transaction with 1 item. That item will contain 1 modifier. ContentView will display Items, Modifiers, and Transactions. For Item, it will display the date and how many modifiers it has. Modifier will display the date and its value. Transactions will display a date and how many modifiers are contained inside of its items. When I delete a modifier, in this case the only one that exist, I should see the count update to 0 for both the Item and the Transaction. This is not happening unless I close the application and reopen it. If I do that, it's updated to 0. I tried to add an ID variable to the view and change it to force a refresh, but it's not updating. This issue also seems to be only with this many to many relationship. Previously, I only had the Transaction and Item models. Deleting an Item would correctly update Transaction, but that was a one to many relationship. I would like for Modifier to have a many to many relationship with Items, so they can be reused. Why is deleting a modifier not updating the items correctly? Why is this not refreshing the view? How can I resolve this issue?
2
0
323
Apr ’24
SwiftData: Deleting a model is working, but the previous view isn't showing it
I have a TabView which consists of a few different tabs. One of which does an @Query to retrieve an array of Transaction models. These are then displayed in a list using a ForEach. struct TransactionsTab: View { @Query private var transactions: [Transaction] ... other code Section(content: { ForEach(transactions) { transaction in transaction.getListItem() } }, header: { LabeledView(label: "Recent Transactions", view: { ListButton(mode: .link(destination: { ListView(list: transactions) .navigationTitle("All Transactions") })) }) }) Transaction contains a different model called TransactionItem and that has a variable called amount. That amount variable is used in the getListItem() function to show how much the total transaction was in the list item. The issue is that I can delete a Transaction and the ForEach will update to reflect that. However, if I delete an TransactionItem separately, that getListItem() will not show that it's been deleted. The total amount shown will still be as if the TransactionItem was never deleted. It will only update when the app is closed and reopened. Below is the code that's ran when deleting a model, in this case a TransactionItem. // Deletes a single item private func delete() { deleteWarning = false if let item = itemToDelete { // If last item is being delete, dismiss the view if list.count == 1 { dismissView() } context.delete(item) context.saveContext() itemToDelete = nil } mode = .view } I would think that deleting the model and having it save will cause the transaction query to update. What's going on here to cause it to not update? By the way, saveContext() just calls the ModelContext save function. extension ModelContext { func saveContext() { do { try self.save() } catch { print("Could not save context: \(error.localizedDescription)") } } }
0
0
249
Apr ’24