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

2
  • stackoverflow.com/… Commented Jan 12, 2021 at 11:50
  • Hi Asperi, if I understand your suggestion here stackoverflow.com/questions/58984109/… 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? Commented Jan 12, 2021 at 12:07

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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)
Yes, you can just declare the dummyVar in the SubView as a @Binding then it will reference back to the parent (ContentView)
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!
Yes, that‘s how to use the variable as a binding :)
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

1 Comment

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?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.