Learn Courses My Dashboard

Core Data Edit: viewContext.save() generating extra blank element

Hi

I am still studying Core Data by doing an application, but I hit a blocker.
Can some one help please? Here is the description of my issue:
I have 2 views: ManageProblemView and ProblemDetailView
When I do the edit operation, upon execution of viewContext.save(), I am getting extra blank problem instance on my storage.

During debugging, the issue occurred at MARK: Edit Operation second half
I tried to put breakpoint on

Initial View:

The summary of my edit flow is:

  1. when user swipe list item and clicked edit button, the item will be save in optional var problemOnEdit
    image

  2. the application will then display ProblemDetailView (as popover) passing the value of the list item using an instance of a binded struct tempProblem. The value received will displayed in the TextField for editing.

  3. After editing, the user will click save button, and the onDismiss edit branch will be executed (MARK: Edit Operation second half)

Expected result is: the edited value will be save

Actual result: the edited value do saved and reflected, but there is an extra blank item created and saved

What I tried prior to consultation:

  1. Instead of passing the instance to edit, I just save the index of the target item on list for editing to a state variable integer. In the second half of editing, I directly access problems : FetchedResults element and edit with new value.
    Result: same thing happened (extra element is saved)

  2. I removed the display of popover and hardcoded the editing
    p.content = “edited…”
    executed viewContext.save()

Result: element was replaced by “edited…” and no extra eminent was generated

I hope I can get a hand. I am just starting to learn swiftui.

Thank you.

//
//  ManageProblemView.swift
//  QuizPrototype
//
//  Created by Michael Javier on 2/2/22.
//

import SwiftUI

struct ManageProblemView: View {
    
    @Environment(\.managedObjectContext) private var viewContext
    @FetchRequest(sortDescriptors: []) var problems : FetchedResults<Problem>
    
    @State var tempProblem = ProblemDetail(value: "")
    
    @State var searchValue : String = ""
    @State var showModal : Bool = false
    @State private var arrayIndex : Int = -1
    
    @State var problemOnEdit : Problem?
        
    var body: some View {
        NavigationView {
            VStack {
                
                HStack{
                    TextField("Search Problem", text: $searchValue).padding(.leading)
                        .background(Capsule().stroke())
                    Spacer()
                    Button {
                        print("insert handler here")
                    } label: {
                        Image(systemName: "magnifyingglass")
                    }
                    
                }.padding()
                
                Spacer()
                
                List(problems) { p in
                    Text(p.content ?? "")
                        .swipeActions(allowsFullSwipe: false) {
                            //MARK: Delete Operation
                            
                            Button {
                                viewContext.delete(p)

                                do {
                                    try viewContext.save()

                                } catch {

                                }
                                
                            } label: {
                                Label("Delete", systemImage: "minus.circle")
                            }
                            .tint(.red)
                            
                            // MARK: Edit Operation first half
                            Button {
                                
                                problemOnEdit = p
                                
//                                p.content = "edited..."
//                                do {
//                                    try viewContext.save()
//
//                                } catch {
//                                    print(error)
//                                }
                                
                                tempProblem.addMode = false
                                if let x = p.content {
                                    tempProblem.content = x
                                }

                                // save index of p from problems
                                if let tmpArryIdx = problems.firstIndex(of: p) {
                                    arrayIndex = tmpArryIdx
                                    print(tmpArryIdx)
                                }

                                // display Modal for add/edit
                                showModal = true
                                
                                
                            } label: {
                                Label("Edit", systemImage: "pencil.circle")
                            }
                            .tint(.blue)
                        }
                        
                }.listStyle(.plain)
                
                Button(action: {
                    
                    tempProblem.content = ""
                    tempProblem.addMode = true
                    showModal = true }) {
                    Text("Add")
                }
                .padding()
                .background(Capsule().stroke(lineWidth: 1))
                .fullScreenCover(isPresented: $showModal, onDismiss: {
                    let p = Problem(context: viewContext);
                    
                    
                    if(tempProblem.addMode == true)
                    {
                        p.problemId = UUID()
                        p.content = tempProblem.content

                        do {
                            try viewContext.save()
                        }
                        catch
                        {

                        }

                        tempProblem.content = ""
                        
                    }
                    else
                    {
                        // MARK: Edit Operation second half
                        let str = tempProblem.content.trimmingCharacters(in: .whitespacesAndNewlines)
                        if(arrayIndex > 0 && str != "")
                        {
                            if let pb = problemOnEdit {
                                pb.content = str
                                // save context
                                do {
                                    try viewContext.save()
                                    
                                } catch {
                                    print(error)
                                }
                            }

                            // return states and variable to its default value
                            tempProblem.content = ""


                        }
                    }
                    
                }, content: {
//                    let problemInstance = TeProblem()
                    ProblemDetailView(problemDetail: $tempProblem)
                })
                    
                
                
            }.navigationTitle("Manage Problems")
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}

struct ManageProblemView_Previews: PreviewProvider {
    static var previews: some View {
        ManageProblemView()
    }
}

for Problem Detail View:

//
//  ProblemDetailView.swift
//  QuizPrototype
//
//  Created by Michael Javier on 2/9/22.
//

import SwiftUI

struct ProblemDetailView: View {
    
    @Environment(\.presentationMode) var presentationMode
//    @ObservedObject var problemDetail: TempProblem
    @Binding var problemDetail: ProblemDetail
    
    var body: some View {
        NavigationView {
            VStack {
                TextField("Problem Entry", text: $problemDetail.content)
                    .padding()
                    .background(Rectangle().stroke(lineWidth: 1).padding())
                Spacer()
                
                if(problemDetail.addMode)
                {
                    Text("Add")
                        .onTapGesture {
                            presentationMode.wrappedValue.dismiss()
                        }
                    
                }
                else
                {
                    Text("Save")
                        .onTapGesture {
                            presentationMode.wrappedValue.dismiss()
                        }
                }
                
            }
        }
    }
}

struct ProblemDetailView_Previews: PreviewProvider {
    
    static var previews: some View {
        ProblemDetailView(problemDetail: .constant(ProblemDetail(value: "Sample Problem")))
    }
}

I solved it…

let p = Problem(context: viewContext);
// this line cause the bug

I moved it inside the if statement. previously it was left outside, then it is executed even on else case (edit). I thought this statement purely declarative, but it is already altering the instances in problem, creating an empty item.