skip to Main Content

How can I create a predicate so that when the user selects "Full Body" it returns the entire list with no predicate? Right now, it is returning "part" which corresponds to the muscle groups I have set (Abs, Legs, Push, Pull). I want to return all of the options when "Full Body" is selected. How could I write an If statement so that the predicate is not used?

import SwiftUI

var parts = ["Abs", "Legs", "Push", "Pull", "Full Body"]
struct ExerciseList: View {
    
    @State private var selectedPart = " "
    
    var body: some View {
        NavigationView {
            VStack (alignment: .leading) {
                
                NavigationLink(destination: AddExerciseView()){
                    Text("Add Exercise")
                        .fontWeight(.bold)
                }
                
                Picker("Body Part", selection: $selectedPart) {
                    ForEach(parts, id:.self) { part in
                        Text(part)
                    }
                }.pickerStyle(.segmented)
                
                ListView(part:selectedPart)
            }    
        }
    }
}

import SwiftUI

struct ListView: View {
    
    var part: String
    
    @FetchRequest var exercises: FetchedResults<Exercise>
    
    init(part: String) {
        self.part = part
        self._exercises = FetchRequest(
            entity: Exercise.entity(),
            sortDescriptors: [],

            predicate: NSPredicate(format: "musclegroup == %@", part as any CVarArg)
        )
    }
    
    var body: some View {
        List(exercises) { e in
            Text(e.exercisename)
        }
    }
}

2

Answers


  1. you could try this simple approach in ListView:

    init(part: String) {
        self.part = part
        self._exercises = FetchRequest(
            entity: Exercise.entity(),
            sortDescriptors: [],
            predicate: (part == "Full Body")
            ? nil
            : NSPredicate(format: "musclegroup == %@", part as any CVarArg)
        )
    }
    
    Login or Signup to reply.
  2. It’s not a good idea to init objects inside View structs because the heap allocation slows things down. You could either have all the predicates created before hand or create one when the picker value changes, e.g. something like this:

    // all the Picker samples in the docs tend to use enums.
    enum Part: String, Identifiable, CaseIterable {
        case abs
        case legs
        case push
        case pull
        case fullBody
        
        var id: Self { self }
    
    // Apple sometimes does it like this
    //    var localizedName: LocalizedStringKey {
    //        switch self {
    //            case .abs: return "Abs"
    //            case .legs: return "Legs"
    //            case .push: return "Push"
    //            case .pull: return "Pull"
    //            case .fullBody: return "Full Body"
    //        }
    //    }
        
       
    }
    
    struct ExerciseListConfig {
        var selectedPart: Part = .fullBody {
            didSet {
                if selectedPart == .fullBody {
                    predicate = nil
                }
                else {
                    // note this will use the lower case string
                    predicate = NSPredicate(format: "musclegroup == %@", selectedPart.rawValue)
                }
            }
        }
        var predicate: NSPredicate?
    }
    
    struct ExerciseList: View {
        
        @State private var config = ExerciseListConfig()
        
        var body: some View {
            NavigationView {
                VStack (alignment: .leading) {
                    Picker("Body Part", selection: $config.selectedPart) {
                        //ForEach(Part.allCases) // Apple sometimes does this but means you can't easily change the display order.
                        Text("Abs").tag(Part.abs)
                        Text("Legs").tag(Part.legs)
                        Text("Push").tag(Part.push)
                        Text("Pull").tag(Part.pull)
                        Text("Full Body").tag(Part.fullBody)
                        
                    }.pickerStyle(.segmented)
                    
                    ExerciseListView(predicate:config.predicate)
                }
            }
        }
    }
    
    
    struct ExerciseListView: View {
        
       // var part: String
        let predicate: NSPredicate?
      //  @FetchRequest var exercises: FetchedResults<Exercise>
        
        init(predicate: NSPredicate?) {
           self.predicate = predicate
    //        self._exercises = FetchRequest(
    //            entity: Exercise.entity(),
    //            sortDescriptors: [],
    //
    //            predicate: NSPredicate(format: "musclegroup == %@", part as any CVarArg)
    //        )
        }
        
        var body: some View {
            Text(predicate?.description ?? "")
    //        List(exercises) { e in
    //            Text(e.exercisename)
    //        }
        }
    }
    

    Since you are using Core Data you might want to use an Int enum in the entity for less storage and faster queries.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search