Search code examples
swiftcore-dataswiftuiapple-watch

Core Data value check - SwiftUi


Guys.

My WatchOS app allows to create Goals. I am trying to disable the button if the title was used previously in other goal or the user entered nothing in the textfield.

So I tried to fetch my data, and wrote .disabled(goalTitle == item.goalTitle). However it leads to the problem, that if there are no created goals, the button disappears. In other case, after entering the title that exists, it duplicates the buttons - one disabled and one is working.

Here is the code of my view:

struct AddGoalView: View {
@State private var goalTitle = ""
@FetchRequest (
entity:NewGoal.entity(),
   sortDescriptors:[NSSortDescriptor(keyPath: \NewGoal.dateAdded, ascending: false)],
    animation: .easeInOut )

var results:FetchedResults<NewGoal>

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

var body: some View {
    ScrollView{
    VStack (alignment: .leading, spacing: 6){
    TextField("Goal Name...", text: $goalTitle)
            .padding(.bottom)

        ForEach(results){item in ///---> This leads to the Button duplicating
        Button(action: addGoal) {
            Text("Add Goal")
            
        }
        .frame(maxWidth: .infinity, alignment: .center)
        .disabled(goalTitle == "")
        .disabled(goalTitle == item.goalTitle) ///---> Here I am trying to disable a button when the user writes down the existed title, to prevent Goal duplicates. 
        }
    }
}
}
private func addGoal(){
    let goal = NewGoal(context: context)
    goal.goalTitle = goalTitle
    goal.dateAdded = Date()
    do{
        try context.save()
        presentationMode.wrappedValue.dismiss()
    }catch let err{
        print(err.localizedDescription)
        }
    }
}

Solution

  • Instead of using a ForEach to loop through your elements, you can use .contains(where:) on results to determine if there's already a goal that uses that title:

    var body: some View {
        ScrollView{
            VStack (alignment: .leading, spacing: 6){
                TextField("Goal Name...", text: $goalTitle)
                    .padding(.bottom)
                
                Button(action: addGoal) {
                    Text("Add Goal")
                }.disabled(
                    goalTitle.isEmpty ||
                    results.contains(where: { $0.goalTitle == goalTitle})
                )
            }
        }
    }