I’m learning SwiftData, I used the Stewart Lynch playlist on Youtube to understand and I have this little project to try this out. I use almost the same code as he is but I have a problem and I can’t figured it out…
Here are my models :
import Foundation
import SwiftData
@Model
class Location {
var locationName: String = ""
var locationAddress: String = ""
var locationZipCode: String = ""
var locationCity: String = ""
@Relationship(deleteRule: .cascade) var doctors: [Doctor]?
init(locationName: String, locationAddress: String, locationZipCode: String, locationCity: String, doctors: [Doctor]? = [Doctor]()) {
self.locationName = locationName
self.locationAddress = locationAddress
self.locationZipCode = locationZipCode
self.locationCity = locationCity
self.doctors = doctors
}
}
@Model
class Doctor {
var firstName: String
var lastName: String
var phoneNumber: String?
var defaultRetrocession: Double
@Relationship(deleteRule: .cascade) var days: [Day]?
init(firstName: String, lastName: String, phoneNumber: String? = nil, defaultRetrocession: Double, location: Location? = nil, days: [Day]? = [Day]()) {
self.firstName = firstName
self.lastName = lastName
self.phoneNumber = phoneNumber
self.defaultRetrocession = defaultRetrocession
self.days = days
}
var location: Location?
}
@Model
class Day{
var date: Date
var retrocession: Int
var total: Double
var totalRetro: Double{
return (Double(retrocession)/100)*total
}
var comments: String?
//photo compta
init(date: Date, retrocession: Int, total: Double, comments: String? = nil) {
self.date = date
self.retrocession = retrocession
self.total = total
self.comments = comments
}
var doctor: Doctor?
}
and here is the code I used on one view, it works good :
@Environment(.modelContext) private var context
let location: Location
var doctors: [Doctor]
@State var createNewDoctor: Bool = false
@State var editLocation: Bool = false
var body: some View {
Group{
if doctors.isEmpty{
ContentUnavailableView(label: {
Label("Créez la page du médecin que vous remplacez !", systemImage: "person.crop.circle.fill.badge.plus")
}, description: {
Text("La liste des médecins apparaitra ici.")
}, actions: {
Button(action: {createNewDoctor.toggle()}) {
Text("Ajouter")
}
})
} else {
List{
ForEach(doctors){ doctor in
NavigationLink{
DoctorDetailledView(doctor: doctor, days: doctor.days!)
} label: {
VStack(alignment: .leading){
HStack(spacing: 0) {
Text(doctor.lastName)
Text(" ")
Text(doctor.firstName)
}
.fontWeight(.bold)
if let phoneNumber = doctor.phoneNumber {
Text(phoneNumber)
.font(.footnote)
}
Text("Somme due en €")
.foregroundStyle(.red)
}
}
}
.onDelete{ indexSet in
indexSet.forEach { index in
let doctor = doctors[index]
context.delete(doctor)
}
}
}
}
}
}
For the new view I used the same code, litteraly copy and paste and change the variables to fit with my Day model but it doesn’t work as expected. When I swipe to delete, the entry in the DB is deleted but it stay on my List when I come back to the view and it crashes my app. So I try to fix this but I don’t like it, I want to understand why it does not work as expected…
Here is the fix I achieved, helped by chatGPT for somes things, it’s not nice, but it works ! If you have the solution to use the same code as above and if you can explain to me why this didn’t work, I’m in ! 🙂
struct DoctorDetailledView: View {
@Environment(.modelContext) private var context
var doctor: Doctor
@State var days : [Day]
@State var createNewDay: Bool = false
@State var editDoctor: Bool = false
var body: some View {
NavigationStack{
Group{
if days.isEmpty{
ContentUnavailableView(label: {
Label("Créez votre premier remplacement pour le Dr (doctor.lastName)", systemImage: "person.crop.circle.fill.badge.plus")
}, description: {
Text("La liste des remplacements apparaitra ici.")
}, actions: {
Button(action: {createNewDay.toggle()}) {
Text("Ajouter")
}
})
} else {
List{
ForEach(days){ day in
NavigationLink{
Text("Test, retro = (day.retrocession)")
} label: {
VStack(alignment: .leading){
Text(day.date.formatted(date: .abbreviated, time: .omitted))
.fontWeight(.bold)
}
}
}
.onDelete(perform: deleteDays)
}
}
}
.navigationTitle(doctor.lastName)
.navigationBarTitleDisplayMode(.inline)
.sheet(isPresented: $editDoctor) {
Text("A venir, EditDoctorView")
.presentationDetents([.medium])
.onDisappear(perform: {
loadDays()
})
}
.sheet(isPresented: $createNewDay) {
NewDayView(doctor: doctor, retrocession: doctor.defaultRetrocession)
.presentationDetents([.large])
}
.onAppear {
loadDays()
}
.onChange(of: createNewDay, { oldValue, newValue in
if !newValue {
withAnimation {
loadDays()
}
}
})
.toolbar{
ToolbarItem(){
Button{
editDoctor = true
} label: {
Image(systemName: "pencil")
}
}
if !days.isEmpty{
ToolbarItem(placement: .bottomBar){
Button{
createNewDay = true
} label: {
Image(systemName: "plus.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30)
}
}
}
}
}
}
private func deleteDays(at offsets: IndexSet) {
offsets.forEach { index in
let day = days[index]
context.delete(day)
}
try? context.save() // Save changes
days.remove(atOffsets: offsets) // Update state
}
private func loadDays() {
days = doctor.days ?? []
}
}
It’s probably full of mistakes, sorry I’m very bad …