DEV Community

Jaakko Kangasharju
Jaakko Kangasharju

Posted on

I had a bug with ForEach and Range in my SwiftUI code

I have a side project for iOS, and since it's a side project, I thought to try SwiftUI for the UI. There's some complexity because the UI isn't exactly traditional, but overall, I've been happy with SwiftUI.

But when Xcode 11.4 was released, the app wouldn't work properly. Some, but not all, parts of the UI weren't updating anymore when state changed. For a while, I stayed on 11.3 so I could work on the functionality, but as time passed, it became evident that this wasn't a problem with Xcode or iOS, it was something in my code.

Mostly, I thought that I had messed up with the bindings so that the UI wasn't observing my presentation layer properly. No luck there, nothing I tried would work. After a lot of frantic Web searching, I eventually found the answer in the comments to an unrelated blog post.

My code looked something like this

struct MainView: View {
  @Binding var names: [[String?]]

  var body: some View {
    VStack {
      ForEach(0..<self.names.count) { nameIndex in

The problem is that ForEach has three init methods,

init(Data, content: (Data.Element) -> Content)
init(Range<Int>, content: (Int) -> Content)
init(Data, id: KeyPath<Data.Element, ID>, content: (Data.Element) -> Content)

and using it like I did picks the second one, with the Range parameter. This method assumes that the range doesn't change, which is true in my case, but it also seems to assume that the content views don't change, at least from Xcode 11.4 onward. Maybe this is a bug, maybe it's intended behavior, I can't tell from the documentation.

When I had finally discovered the problem, the fix was simple enough. I changed the ForEach to

      ForEach(0..<self.names.count, id: \.self) { nameIndex in

which picks the third init method of ForEach, and allows updates of the views.

Top comments (1)

rabblerousy profile image

I created an account just to say THANK YOU!!!! I had exactly the same problem, what a stupidly easy fix. I hate SwiftUI for making solutions like this so hard to find ...