Home > Enterprise >  SwiftUI - Dynamic NSPredicate If Statement
SwiftUI - Dynamic NSPredicate If Statement

Time:11-11

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)
        }
    }
}

CodePudding user response:

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)
    )
}

CodePudding user response:

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.

  • Related