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.