1

I'm working on a patient management system where I need to enter all the relevant info of a patient and have them listed, saved and edited if need be. I've managed to save them, list them, and display them when I go into detail view but I cannot edit them and save the new info over the old one.

I've looked into all the answers possible to be able to solve this and the closest I've come was a duplicate patient file being created.

Here is my code (attributes are in french)

PatientList file:

  struct PatientList: View {
    @Environment(\.managedObjectContext) var moc
    @FetchRequest(entity: PatientInfo.entity(), sortDescriptors: [
        NSSortDescriptor(keyPath: \PatientInfo.nom, ascending: true),
        NSSortDescriptor(keyPath: \PatientInfo.prenom, ascending: true)])
    var patients: FetchedResults<PatientInfo>

    @State private var showingAddScreen = false

    var body: some View {
        NavigationView {
            List {
                ForEach(patients, id: \.self) { patient in
                    NavigationLink(destination: PatientDetail(patients: patient))
                    {
                        VStack{
                            HStack{
                                Text(patient.nom ?? "Inconnu")
                                Text(patient.prenom ?? "Inconnu")
                            }
                        }

                    }
                }
                .onDelete(perform: deletePatients)
            }
            .navigationBarTitle("Patients")
            .navigationBarItems(leading: EditButton(), trailing:
                    Button(action: {
                        self.showingAddScreen.toggle()
                    }) {
                        Image(systemName: "plus")
                }
            )
            .sheet(isPresented: $showingAddScreen) {
                AddNewPatient().environment(\.managedObjectContext, self.moc)
            }

            }


        }
    func deletePatients(at offsets: IndexSet) {
                for offset in offsets {
                    let patient = patients[offset]
                    moc.delete(patient)
                }
                try? moc.save()

        }


struct PatientList_Previews: PreviewProvider {
    static var previews: some View {
        PatientList()
    }
}
}

AddNewPatient file:

    struct AddNewPatient: View {

    @Environment(\.managedObjectContext) var moc
    @Environment(\.presentationMode) var presentationMode

    @State private var nom = ""
    @State private var prenom = ""
    @State private var ddn = Date()
    @State private var adresse = ""
    @State private var codepostal = ""
    @State private var ville = ""
    @State private var province = ""
    @State private var numerotelephone = "000-000-0000"
    @State private var dernierexamen = Date()



    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Nom", text: $nom)
                    TextField("Prénom", text: $prenom)
                }
                DatePicker("Date de naissance", selection: $ddn, displayedComponents: .date)

                Section {
                    TextField("Adresse", text: $adresse)
                    TextField("Code Postal", text: $codepostal)
                    TextField("Ville", text: $ville)
                    TextField("Province", text: $province)
                }
                Section {
                    TextField("Numéro de téléphone", text: $numerotelephone).keyboardType(.numberPad)
                }
                DatePicker("Dernier examen", selection: $dernierexamen, displayedComponents: .date)
                Section {
                    Button("Enregistrer") {
                        let newPatient = PatientInfo(context: self.moc)
                        newPatient.nom = self.nom
                        newPatient.prenom = self.prenom
                        newPatient.ddn = self.ddn
                        newPatient.adresse = self.adresse
                        newPatient.codepostal = self.codepostal
                        newPatient.ville = self.ville
                        newPatient.province = self.province
                        newPatient.numerotelephone = self.numerotelephone
                        newPatient.dernierexamen = self.dernierexamen

                        try? self.moc.save()
                        self.presentationMode.wrappedValue.dismiss()
                    }
            }
        }
        .navigationBarTitle("Nouveau Patient")
        .navigationBarItems(leading: EditButton())
    }
}

    struct AddNewPatient_Previews: PreviewProvider {
        static var previews: some View {
            AddNewPatient()
        }
    }
    }

PatientDetail File:

    struct PatientDetail: View {
    @Environment(\.managedObjectContext) var moc
    @Binding var patients: PatientInfo


    @State private var showingEditScreen = false




    var body: some View {
        NavigationView{
            Form{

                Section{
                    Text(self.patients.nom ?? "Nom")
                    Text(self.patients.prenom ?? "Prénom")
                    }

                Section{
                    Text(self.patients.adresse ?? "Adresse")
                    }

                Section{
                    Text(self.patients.codepostal ?? "Code Postal")
                    }
                Section{
                    Text(self.patients.ville ?? "Ville")
                }
                Section{
                    Text(self.patients.province ?? "Province")
                }
                Section{
                    Text(self.patients.numerotelephone ?? "000-000-0000")
                }


            }

        }
        .navigationBarTitle(Text(patients.nom ?? "Inconnu"), displayMode: .inline)
        .navigationBarItems(trailing: Button(action: { self.showingEditScreen.toggle()

        }) {
            Image(systemName: "square.and.pencil")
        }
    )
            .sheet(isPresented: $showingEditScreen) {
                EditPatientView(patients: self.patients).environment(\.managedObjectContext, self.moc)
            }
    }

struct PatientDetail_Previews: PreviewProvider {
    static let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
    static var previews: some View {
        let patients = PatientInfo(context: moc)
        patients.nom = "Amchou"
        patients.prenom = "Hicham"
        patients.ddn = Date()
        patients.adresse = "7580 Francois Chartrand"
        patients.codepostal = "H7A 3Z9"
        patients.ville = "Laval"
        patients.province = "Québec"
        patients.numerotelephone = "000-000-0000"
        patients.dernierexamen = Date()
        return NavigationView {
            PatientDetail(patients: PatientInfo)
        }
    }
}
}

and the EditPatientView file:

struct EditPatientView: View {

    @Environment(\.presentationMode) var presentationMode
    @Environment(\.managedObjectContext) var moc

    @ObservedObject var patients : PatientInfo


     @State private var nom = ""
     @State private var prenom = ""
     @State private var ddn = Date()
     @State private var adresse = ""
     @State private var codepostal = ""
     @State private var ville = ""
     @State private var province = ""
     @State private var numerotelephone = "000-000-0000"
     @State private var dernierexamen = Date()

    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Nom", text: self.$nom)
                    TextField("Prénom", text: self.$prenom)
                }
                Section {
                DatePicker("Date de naissance", selection: self.$ddn, displayedComponents: .date)
                }
                Section {
                    TextField("Adresse", text: self.$adresse)
                    TextField("Code Postal", text: self.$codepostal)
                    TextField("Ville", text: self.$ville)
                    TextField("Province", text: self.$province)
                }
                Section {
                    TextField("Numéro de téléphone", text: self.$numerotelephone).keyboardType(.numberPad)
                }
                DatePicker("Dernier examen", selection: self.$dernierexamen, displayedComponents: .date)
                Section {
                    Button("Enregistrer") {
                        let newPatient = PatientInfo(context: self.moc)
                        newPatient.nom = self.nom
                        newPatient.prenom = self.prenom
                        newPatient.ddn = self.ddn
                        newPatient.adresse = self.adresse
                        newPatient.codepostal = self.codepostal
                        newPatient.ville = self.ville
                        newPatient.province = self.province
                        newPatient.numerotelephone = self.numerotelephone
                        newPatient.dernierexamen = self.dernierexamen

                        try? self.moc.save()
                        self.presentationMode.wrappedValue.dismiss()
                    }
            }
        }
        .navigationBarTitle("Patient")

    }
}
}


struct EditPatientView_Previews: PreviewProvider {
    static var previews: some View {
        EditPatientView(patients: PatientInfo())
    }
}

(Hope this isn't too much info. First time posting)

AMchou
  • 11
  • 6

1 Answers1

0

In EditPatientView, you have the patient to edit available. Why not edit this instead of creating a new patient? Like:

struct EditPatientView: View {
    @ObservedObject var patient : PatientInfo
    @Environment(\.managedObjectContext) var managedObjectContext

    var body: some View {
        Form {
            TextField("Name", text: $patient.name ?? "")
        }
        .onDisappear {
            try? self.managedObjectContext.save()
        }
    }
}

The problem with that is that the TextField bindings may not be optional, but Core Data fields are optional.

This handy extension from https://stackoverflow.com/a/61002589/128083 does the trick:

func ?? <T>(lhs: Binding<T?>, rhs: T) -> Binding<T> {
    Binding(
        get: { lhs.wrappedValue ?? rhs },
        set: { lhs.wrappedValue = $0 }
    )
}

There is also a Core Data example demonstrating this in my SwiftUIPlayground.

Ralf Ebert
  • 3,556
  • 3
  • 29
  • 43
  • This totally solved my problem. I kept trying to understand why it wouldn't work and then I got the binding thing (sorry very new to all of this). Went through API and it didn't help. I feel relieved. Thanks very much!! – AMchou May 19 '20 at 03:56