1

I'm quite new to IOS development so please bear with me if I have used the code in a completely improper way.

I use swiftui 2 and my problem is that I have in my data structure an array which contains information I would like to present in another view via ForEach. That's so far working but if I change the array within one of the subviews, then I get an index out of range error.

Here's the example data structure:

struct Test  {
    var id: String
    var array : Array<String>
}

The EnvironmentObject:

class DBhandler: ObservableObject {
    
    @Published var arrays: Test
    
    init(arrays: Test) {
        self.arrays = arrays
    }
}

The app file:

@main
struct DummyApp: App {
    @ObservedObject var dbhandler = DBhandler(arrays: Test(id: "1", array: ["Car", "Bus", "Train"]))
    
    var body: some Scene {
        WindowGroup {
            ContentView().environmentObject(dbhandler)
        }
    }
}

The MainView:

struct ContentView: View {
    @EnvironmentObject var dbhandler: DBhandler
    
    var body: some View {

        VStack{
            ForEach(dbhandler.arrays.array.indices, id: \.self) {index in
                SubView(index: index)
            }
        }
        
    }
}

And the SubView:

struct SubView : View {
    @EnvironmentObject var dbhandler: DBhandler
    
    let index: Int
    var body: some View {
        VStack{
            Text(dbhandler.arrays.array[index]) <--- here's the index out of range error
            Button(action: {
                dbhandler.arrays.array.remove(at: index)
            }, label: {
                Text("Remove object")
            })
        }

    }
}

My assumption is that the ForEach does not refers to the latest dbhandler.arrays.array.indices but rather to an old stored one but I don't know how to circumvent it.

Does anyone of you has an idea on how I could fix this?

Any help is much appreciated :)

FriendlyGuy
  • 331
  • 5
  • 14
  • https://stackoverflow.com/search?q=%5Bswiftui%5D+%22index+out+of+range+error%22 – Asperi Jan 12 '21 at 11:50
  • Hi Asperi, if I understand your suggestion here https://stackoverflow.com/questions/58984109/fatal-error-index-out-of-range-when-deleting-bound-object-in-view correctly, then I should just remove the .indices in the foreach. However I need the index. If I generate it via SubView(index: dbhandler.arrays.array.firstIndex(of: index)!) I get a problem with the last index. Do you have any suggestion on how to solve this? – FriendlyGuy Jan 12 '21 at 12:07

2 Answers2

2

For anyone who has the same problem. I've figured out a way on how to make it happen. As the ForEach stores the original array and is not referencing to the original array a possible workaround is to force the ForEach to update itself. You can do it like this:

struct ContentView: View {
    @EnvironmentObject var dbhandler: DBhandler
    @State var dummyVar : Int = 0

    var body: some View {

        VStack{
            ForEach(dbhandler.arrays.array.indices, id: \.self) {index in
                SubView(index: index, dummyVar : dummyVar)
            }.id(dummyVar)
        }
        
    }
}

and in the SubView you just need to increase the dummyVar by 1.

FriendlyGuy
  • 331
  • 5
  • 14
  • How do you increment dummyVar in SubView? SubView is a struct, so it's working on a copy of dummyVar. Changing dummyVar in SubView does not change dummyVar in ContentView. Or, is there some way for SubView to access the State variable declared in ContentView? (I'm new to SwiftUI) – P. Stern Aug 26 '21 at 20:07
  • Yes, you can just declare the dummyVar in the SubView as a @Binding then it will reference back to the parent (ContentView) – FriendlyGuy Aug 26 '21 at 20:40
  • I assume you're incrementing dummyVar in the button action in SubView. Also, I had to use $dummyVar (dollar sign in front) for the argument to SubView. Thanks for the help! – P. Stern Aug 26 '21 at 22:08
  • Yes, that‘s how to use the variable as a binding :) – FriendlyGuy Aug 27 '21 at 06:29
0

You can fix the crash by adding a check in SubView index < dbhandler.arrays.array.count which will not render View if index is out of bounds

struct SubView : View {
    @EnvironmentObject var dbhandler: DBhandler

    let index: Int
    var body: some View {
    VStack{
        if index < dbhandler.arrays.array.count{
            Text(dbhandler.arrays.array[index]) //<--- here's the index out of range error
            Button(action: {
                dbhandler.arrays.array.remove(at: index)
            }, label: {
                Text("Remove object")
            })
            
            }
        }

    }
}

enter image description here

abh
  • 1,189
  • 2
  • 14
  • 30
  • Hi @Babar thanks for the hint. I've also thought of that solution but thought that there must be an easier way on how to handle that problem. Is there another way on how to use ForEach or something else properly so I don't have to implement the workaround for each subview? – FriendlyGuy Jan 12 '21 at 13:04