5

I am having a weird SwiftUI crash that I am not understanding. I have a TabView with a list of 3 images inside of it. I am trying to remove the first image from the list by tapping on the button on the screen, but I get this crash.

'NSInternalInconsistencyException', reason: 'attempt to delete and reload the same index path (<NSIndexPath: 0x968bfe135e5a98d1> {length = 2, path = 0 - 0})' terminating with uncaught exception of type NSException

If I remove the TabView from the code, it works as expected and removes the first item. Here is the minimum amount of code needed to reproduce this crash. I have also created a Git repo of this code here -> https://github.com/cameronhenige/TestCrash Could somebody help me figure out what is going on?

import SwiftUI

struct ContentView: View {
    @StateObject var testViewModel = TestViewModel()
    
    var body: some View {
        GeometryReader { proxy in
            
            ScrollView {
                
                VStack(alignment: .leading, spacing:0) {
                    
                    TabView {
                        
                        ForEach(testViewModel.images, id: \.self) { image in
                            Image(image)
                        }
                        
                    }.tabViewStyle(PageTabViewStyle())
                    .clipShape(RoundedRectangle(cornerRadius: 15))
                    .padding()
                    .frame(width: proxy.size.width, height: proxy.size.height/2.5)
                    
                }
                Button(action: {
                    testViewModel.removeFirst()
                }) {
                    Text("Remove first item from list")
                }
                
            }
            
            
        }.frame(maxWidth: .infinity).background(Color.black)
    }
}
import Foundation

class TestViewModel: NSObject, ObservableObject {
    
    @Published var images: [String] = ["dog", "cat", "bird"]
    
    func removeFirst() {
        images.remove(at: 0)
    }
}

shim
  • 9,289
  • 12
  • 69
  • 108
Cameron Henige
  • 368
  • 2
  • 17
  • Does this answer your question? [SwiftUI TabView gives an error message during add/delete the element of CoreData](https://stackoverflow.com/questions/67469940/swiftui-tabview-gives-an-error-message-during-add-delete-the-element-of-coredata) – lorem ipsum Jul 17 '21 at 11:58
  • I tried your sample on iOS 14 and reproduced the crash. It is gone with Xcode 13/iOS15. For testing I also replaced the `ForEach` with a `List` and it worked well. This sounds like a bug in SwiftUI/iOS like @loremipsum suggested. – mgratzer Jul 20 '21 at 12:41
  • 3
    https://stackoverflow.com/questions/68422128/how-to-update-swiftuis-foreach-in-a-tabview-with-pagetabviewstyle-modifier-when/68423533#68423533 – lorem ipsum Jul 21 '21 at 13:34

2 Answers2

6
TabView {
    
    ForEach(testViewModel.images, id: \.self) { image in
        Image(image)
    }
    
}.tabViewStyle(PageTabViewStyle())
.clipShape(RoundedRectangle(cornerRadius: 15))
.padding()
.frame(width: proxy.size.width, height: proxy.size.height/2.5)
.id(testViewModel.images.count)

Adding an id to the TabView fixes this issue!

Paul Solt
  • 8,375
  • 5
  • 41
  • 46
Cameron Henige
  • 368
  • 2
  • 17
4

For more of an explanation of why using .id(_:) fixed the issue for you, it's because when testViewModel.images.count changes (AKA the view is now using different data) you are invalidating the view because the ID has changed.

For more on invalidating views and how to fix weird view updates, see the Demystify SwiftUI - WWDC21 talk.

shim
  • 9,289
  • 12
  • 69
  • 108
George
  • 25,988
  • 10
  • 79
  • 133