Search code examples
swiftuinspredicate

SwiftUI - Use value passed from previous view in predicate


So I'm new to using SwiftUI. Normally I'd have the fetch request in the viewdidload method but not sure how to go about it now as the method doesn't seem to have an equivalent.

So I have a view where I'm passing a variable to another view like so

NavigationLink(destination: ItemAddView(series: series)) {
    Label("Add Item", systemImage: "plus")
}

I'm now wanting to use this variable in a predicate on a fetch request. The fetch request is setup as follows

struct ItemAddView: View {
@Environment(\.managedObjectContext) private var viewContext

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true)],
    animation: .default)
private var items: FetchedResults<Item>

var series: Series

@State private var searchText = ""

var body: some View {
    List {
        ForEach(items) { item in
            NavigationLink(destination: ItemDetailView(item: item)) {
                Image(item.mainImage ?? "")
                    .renderingMode(.original)
                    .resizable()
                    .scaledToFit()
                    .frame(width: 100.0)
                Text("\(item.name ?? "Error")")
            }
        }
    }
    .searchable(text: $searchText)
    .onSubmit(of: .search) {
        items.nsPredicate = NSPredicate(format: "series = %@", series)
        if(!searchText.isEmpty) {
            items.nsPredicate = NSPredicate(format: "series = %@ AND amount = 0 AND name CONTAINS[cd] %@", series, searchText)
        }
    }
    .onChange(of: searchText) { _ in
        items.nsPredicate = NSPredicate(format: "series = %@", series)
        if(!searchText.isEmpty) {
            items.nsPredicate = NSPredicate(format: "series = %@ AND amount = 0 AND name CONTAINS[cd] %@", series, searchText)
        }
    }
    .navigationBarTitle("\(series.name ?? "Error") Add Items", displayMode: .inline)
    .onAppear() {
        items.nsPredicate = NSPredicate(format: "series = %@ AND amount = 0", series)
    }
}
}

I get an error if I use the predicate here

Cannot use instance member 'series' within property initializer; property initializers run before 'self' is available

I've taken to using the predicate in the onAppear method as such

.onAppear() {
    items.nsPredicate = NSPredicate(format: "series = %@ AND amount > 0", series)
}

But the issue is I'm seeing the full list before it then goes to the filtered request. What's the correct way of doing this?


Solution

  • You just need to declare the variable in the header. You can then do the initialization in the init() and use the variable that is passed in. If you aren't subsequently needing series, you do not need to have any variable in the view to assign it to. Also, as you didn't post your full view, I had to guess at the type of series.

    struct ItemAddView: View {
        @Environment(\.managedObjectContext) private var viewContext
    
        @FetchRequest private var items: FetchedResults<Item>
    
    
        init(series: Series) {
            let request = Item.fetchRequest(sortDescriptors: [NSSortDescriptor(keyPath: \Item.name, ascending: true)], predicate: NSPredicate(format: "series = %@ AND amount > 0", series), animation: .default)
            _items = FetchRequest(fetchRequest: request)
    
            // Initialize anything else that is necessary
        }
    
        ...
    }