I have a list displaying all my elements. I want to add a button deleting all my elements from my list but also from my Realm Database. I have well understood that I cannot call a Realm Object after deleting it however I cannot understood why when I click on my "Delete all" button I got this error : Object has been deleted or invalidated Here is my code : SettingsView :
import SwiftUI
struct SettingsView: View {
@Binding var showSetting: Bool
@State var allAction01: [Action] = []
@State var allAction12: [Action] = []
init(showSetting: Binding<Bool>) {
self._showSetting = showSetting
}
var body: some View {
NavigationStack {
List{
Section{
Button(action: deleteAll){
Text("Delete")
.foregroundStyle(.red)
}
.buttonStyle(.plain)
} // Section
Section {
Text("Liste des actions")
.fontWeight(.bold)
.font(.system(size: 20))
} // Section
Section {
ForEach(allAction01, id: \.id) { action in
Text(action.text)
}
.onDelete(perform: delete)
Section {
ForEach(allAction12, id: \.id) { action in
Text(action.text)
}
} // List
.onAppear(perform: updateAction)
} // NavigationStack
} // var body: some View
private func delete(indexSet: IndexSet){
indexSet.forEach { index in
DataManager.deleteAction(action: allAction01[index])
}
updateAction()
} // private func delete(indexSet: IndexSet)
private func deleteAll(){
DataManager.deleteAllActions()
updateAction()
}
private func updateAction(){
allAction01 = DataManager.getActionBetween(inf: 0, sup: 1)
allAction12 = DataManager.getActionBetween(inf: 1, sup: 2)
}
} // struct SettingsView: View
And here is my
DataManager.deleteAllActions()
static func deleteAllActions() {
do {
try realm.write(){
let actionToDelete = realm.objects(Action.self)
realm.delete(actionToDelete)
}
} catch {
print("Error : \(error)")
}
}
If you have any idea il would really help me !
PS : I display my SettingsView as a Sheet overlaying another view but it doesn't cause any problems when deleting only one action.
I try to delete all my action, I wish to have an empty BDD and list after this
The core issue is the use of an Array to hold your Realm data.
While deleting objects from Realm removes the underlying object, which also invalidates the object in the array, it does NOT remove the index from the array.
As an example, suppose the following; There's a PersonClass object and 5 persons are stored in Realm.
let personResults = realm.objects(PersonClass.self) //get 5 persons
let personArray = Array(personResults) //instantiate an array with 5 persons
print(personResults.count) //prints 5
print(personArray.count) // also prints 5
try! realm.write {
realm.delete(personResults) //delete the persons from Realm
}
print(personResults.count) //prints 0 as there are no persons in Realm
print(personArray.count) //print 5 since 5 indexes exist, containing nil objects
There are multiple approaches to resolving this
When removing an object from Realm, also remove it from the array
Use either @ObservedRealmObject for working with a specific object or in this use case, use the @ObservedResults property wrapper, which is defined as:
Observe a collection of query results. You can perform a quick write to an ObservedResults collection, and the view automatically updates itself when the observed query changes. For example, you can remove a dog from an observed list of dogs using onDelete.
Replacing the current vars with something along these lines will fix the issue
@ObservedResults(Action.self) var actions01
//@State var allAction12: [Action] = []
init(showSetting: Binding<Bool>) {
self._showSetting = showSetting
}
var body: some View {
NavigationStack {
List {
//use actions01 here for the UI
and use the action01 as the Lists datasource. Then, when deleting all actions, the view will update automatically as actions01 will always reflect the current state of the underlying data - an object is removed, it's removed from actions 01 and the UI updates accordingly.
Results can be treated very much like a read only array; iterate over it, access a specific index etc. It cannot be written to - write to Realm to update the results instead