Conform to range expression

Good morning ,
In using a Navigation SplitView I am getting an error message ;

Subscript 'subscript(_:)' requires that 'Binding<[Batch]>' conform to 'RangeExpression'

For

@State private var selectedBatch: [Batch] = [ ] 

in

 ForEach(batches[$selectedBatch]) { pouch in    //error  here 

I am not clear on Apple’s definition ; A type that can be used to slice a collection.
How can selectedBatch be made to conform to Range Expression ? I read various articles on Swift collection protocols but am still confused .
TIA
Joel

The entire struct is ; note selectedBatch and selected Pouch have changed

import SwiftUI
import SwiftData

struct ContentView: View {
    
    @Environment(\.modelContext) var modelContext
    
    @Query var batches: [Batch]
    @Query var pouches: [Pouch]
    
    @State private var selectedBatch: String?
    @State private var selectedPouch: String?
    
    var body: some View {
        NavigationSplitView {
            List(selection: $selectedBatch) {
                ForEach(batches)  { batch in
                    NavigationLink(value: batch) {
                        Text(batch.id.uuidString)
                    }
                }
            }
                .navigationTitle("Batches")
        } content: {
            if let selectedBatch {
                List(selection: $selectedPouch) {                           // error here  
                    ForEach(batches[$selectedBatch]) { pouch in
                        NavigationLink(value: pouch) {
                            Text(pouch.id.uuidString)
                        }
                    }
                }
                .navigationTitle("Pouches")
            } else {
                Text("Choose a batch from the batch list")
            }
        } detail: {
            NavigationStack {
                ZStack {
                    if let selectedPouch {
                        NavigationLink(value: selectedPouch) {
                            Text(verbatim: selectedPouch)
                                .navigationTitle(selectedPouch)
                        }
                    } else {
                        Text("Choose a pouch")
                    }
                }
            }
        }
    }
}
            

You are trying to use an array index which is defined as a string. Ie:

@State private var selectedBatch: String?.

The fact that is an optional is fine as you are dealing with that in your conditional statement if let selectedBatch {.

It needs to be an integer value to be used as an index.

ForEach(batches[$selectedBatch]) { pouch in

Can you share your model definitions so that I can replicate the issue.

(edit)
Consider defining selectedBatch as an Int and then in the List where you have selection: $selectedBatch, that will yield an integer value comensurate with the batch you have selected and that can be used in your:

ForEach(batches[$selectedBatch]) { pouch in

but remove the $ sign. ie:

ForEach(batches[selectedBatch]) { pouch in

Let me know if that works.

I fiddled with your code a little and I have no idea if the @Model’s that I have set up match those of yours. What I assumed (rightly or wrongly) was that a batch would have an array of pouches so this is what I came up with:

@Model
class Batch {
    var id: UUID
    var name: String = ""
    var pouches: [Pouch] //This might have to be Optional?

    init(id: UUID, name: String, pouches: [Pouch]) {
        self.id = id
        self.name = name
        self.pouches = pouches
    }
}
@Model
class Pouch {
    var id: UUID
    var name: String = ""

    init(id: String, name: String) {
        self.id = UUID()
        self.name = name
    }
}

and with that I did this in your code.

import SwiftUI
import SwiftData

struct ContentView1: View {

    @Environment(\.modelContext) var modelContext

    @Query var batches: [Batch]
    @Query var pouches: [Pouch]

    @State private var selectedBatch: Int?
    @State private var selectedPouch: String?

    var body: some View {
        NavigationSplitView {
            List(selection: $selectedBatch) {
                ForEach(batches)  { batch in
                    NavigationLink(value: batch) {
                        Text(batch.id.uuidString)
                    }
                }
            }
                .navigationTitle("Batches")
        } content: {
            if let selectedBatch {
                List(selection: $selectedPouch) {                           // error here
                    ForEach(batches[selectedBatch].pouches) { pouch in
                        NavigationLink(value: pouch) {
                            Text(pouch.id.uuidString)
                        }
                    }
                }
                .navigationTitle("Pouches")
            } else {
                Text("Choose a batch from the batch list")
            }
        } detail: {
            NavigationStack {
                ZStack {
                    if let selectedPouch {
                        NavigationLink(value: selectedPouch) {
                            Text(verbatim: selectedPouch)
                                .navigationTitle(selectedPouch)
                        }
                    } else {
                        Text("Choose a pouch")
                    }
                }
            }
        }
    }
}

I might be way off beam but that’s why I am interested in what your Model definitions look like to see if I am on the right track.

Cheers.

Hi Chris ,

@Model
class Batch     {
    
    var id: UUID
    var startDate: Date
    var user: String
    var pressure: Bool
    var temperature: Bool
    var bioIndicator: Bool
    var master: Bool
    var void: Bool
    var pouch: [Pouch]?
    
    init(id: UUID, startDate: Date, user: String, pressure: Bool, temperature: Bool, bioIndicator: Bool, master: Bool, void: Bool, pouch: [Pouch]? = nil) {
        self.id = id
        self.startDate = startDate
        self.user = user
        self.pressure = pressure
        self.temperature = temperature
        self.bioIndicator = bioIndicator
        self.master = master
        self.void = void
        self.pouch = pouch
    }
}

@Model
class Pouch {
    
    var id: UUID
    var packDate: Date
    var user: String
    var type:Bool // instrument or kit
    var descInstruments: String
    var chemIdicator: Bool
    var void: Bool
    var batch: Batch?
    
    init(id: UUID, packDate: Date, user: String, type: Bool, descInstruments: String, chemIdicator: Bool, void: Bool, batch: Batch? = nil) {
        self.id = id
        self.packDate = packDate
        self.user = user
        self.type = type
        self.descInstruments = descInstruments
        self.chemIdicator = chemIdicator
        self.void = void
        self.batch = batch
    }

}

Using my model def in trying your example code errors are given


Also I am still confused on the breakdown of ( copying without understanding )
ForEach(batches[selectedBatch].pouches)
filtered array of pouches using selectedBatch ?

This seems to work ;
ForEach(batches[selectedBatch].pouch!) { pouch in
unwrapped pouch
YippiKaiEh (sp) , minor details like navigation title "pouches " not appearing but over the hump
Thanks Chris

The difference is that what you have defined as an array of pouch in the Batches struct differed to mine where I had that defined as pouches since that is the plural. I’m a little pedantic with singular and plural definitions because it reads better when examining the code.

Since you have a batch with a possible array of pouches (pouch if you prefer) then when you want to select a specific batch you can then iterate over the array of pouch within that specific batch item. So that’s why the ForEach is written as:

FoEach(batches[selectedBatch].pouch) { pouch in .....

I’m not sure of any other way of explaining it so I hope that helps.