skip to Main Content

I’ve got an example SwiftUI project as follows below.

If you run the code in the iOS simulator, make some changes to the text in one of the TextFields, and click the Button to "deleteAll", you get the error:

Swift/ContiguousArrayBuffer.swift:600: Fatal error: Index out of range

What’s strange is if you don’t make changes to any of the TextFields, and simply click "deleteAll", all the notes are deleted as expected.

Is this a bug with SwiftUI?

import SwiftUI

struct Note: Identifiable {
    var id = UUID()
    var text = "Hello"
}

class Category: ObservableObject {
    @Published var notes = [Note(), Note(), Note()]
    
    func deleteAll() {
        notes.removeAll()
    }
}

struct TestViewThree: View {
    @StateObject var category = Category()
    
    var body: some View {
        List {
            ForEach($category.notes) { $note in
                TextField("", text: $note.text)
            }
            Button {
                category.deleteAll()
            } label: {
                Text("Delete")
            }

        }
    }
}

TextField while editing

Tried to edit a TextField, and then clicked on a button to delete the underlying object that’s bound to the TextField. I expected the TextField to disappear, but instead, the program crashed.

2

Answers


  1. import SwiftUI
    
    struct Note: Identifiable , Hashable{
        var id = UUID()
        var text = "Hello"
    }
    
    class Category: ObservableObject {
        @Published var notes = [Note(), Note(), Note()]
        
        func deleteAll() {
            notes.removeAll()
            
        }
    }
    
    struct TestViewThree: View {
        @StateObject var category = Category()
        
        var body: some View {
            List {
                ForEach($category.notes, id: .self) { note in
                    TextField("", text: note.text)
                }
                Button {
                    category.deleteAll()
                } label: {
                    Text("Delete")
                }
    
    
            }
        }
    }
    

    function "ForEach " cause this problem.
    using this form instead. "init(_ data: Data, id: KeyPath<Data.Element, ID>, content: @escaping (Data.Element) -> Content)"

    Login or Signup to reply.
  2. This happens when at least on of your text flied is in focus mode and the keyboard is visible i think because you are binding to the published array and at the same time you are trying to remove it. So i suggest that you disable delete when any of the textFields are in focus mode. You can do it like this

    struct Note: Identifiable {
       var id = UUID()
       var text = "Hello"
    }
    
    class Category: ObservableObject {
       @Published var notes = [Note(), Note(), Note()]
    
       func deleteAll() {
         self.notes.removeAll()
       }
    }
    
    struct TestViewThree: View {
        @StateObject var category = Category()
        @State var deleteButtonClicked = false
        @FocusState var focused: Bool
    
    
        var body: some View {
           List {
              ForEach($category.notes) { $notes in
                TextField("", text: $notes.text)
                    .focused($focused)
              }
              Button {
                  if !focused {
                      self.deleteButtonClicked.toggle()
                  }
              } label: {
                  Text("Delete")
              }
           }
           .onSubmit {
              print("done")
           }
           .onChange(of: deleteButtonClicked) { _ in
              category.deleteAll()
           }
            
        }
    }
    

    one more thing it’s not recommended to use a published object for binding or change published object from button view check why from here
    https://www.youtube.com/watch?v=3a7tuhVpoTQ

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search