Swiftui Tabview with List Not Refreshing After Objected Deleted From/Added to Core Data

SwiftUI TabView with List not refreshing after objected deleted from / added to Core Data

I found a pure SwiftUI working solution:

/// This View that init the content view when selection match tag.
struct SyncView<Content: View>: View {

@Binding var selection: Int

var tag: Int

var content: () -> Content

@ViewBuilder
var body: some View {
if selection == tag {
content()
} else {
Spacer()
}
}
}

You can use it then in this way:

struct ContentView: View {  
@State private var selection = 0

var body: some View {
TabView(selection: $selection) {

SyncView(selection: $selection, tag: 0) {
ViewThatNeedsRefresh()
}
.tabItem { Text("First") }
.tag(0)

Text("Second View")
.font(.title)
.tabItem { Text("Second") }
.tag(1)
}
}
}

You can use the SyncView for each view that needs a refresh.

SwiftUI List Keeps Showing Core Data Items After They Were Deleted

The problem was that for some reason ListOfSingers struct was called twice. Even though I've deleted the last singer from Core Data the render time was faster than reindexing of Core Data. So I ended up having a "Ghost View", which was rendered before I item was fully deleted from the Core Data. When I tried to delete a Ghost View I was reaching an array that didn't exist. It always lead to an app crash.

The solution was to add a function that checked if there are any items in Core Data right after deletion fired off.

  1. Create @State var noMoreSingers = false in the main view to check which view should be presented and add this conditional to the VStack to explicitly tell which view should be presented:

     VStack {
    if !noMoreSingers {
    ListOfSingers(filterKey: "lastName", filterValue: lastNameFilter) { (singer: Singer) in
    Text("\(singer.wrappedName) \(singer.wrappedLastName)")
    } } else {
    Spacer()
    }
  2. Add a @Binding var noMoreSingers: Bool to ListOfSingers to be able to tell the main view when there are no more items in Core Data

  3. Add function to Persistanse Controller to get all items:

         func getAllSingers() -> [Singer] {
    let request: NSFetchRequest<Singer> = Singer.fetchRequest()

    do {
    return try container.viewContext.fetch(request)
    } catch {
    fatalError("Error Fetching Users")
    }
    }
  4. Add function to ListOfSingers for fire off getAllSingers and check if the retrieved array is empty:

     func checkIfSingersAreEmpty() {
    let checkSingers = PersistenceController.shared.getAllSingers()
    if checkSingers.isEmpty {
    noMoreSingers = true
    }
    }

Now everything works!

SwiftUI Core Data does not refresh one to many relations properly and lists are not refreshed

A huge surprise in iOS: core data controllers only work on basic fields. Strange but true.

Incredibly, CoreData does not update based on relationships.

Only basic fields (int, string, etc).

You can see many QA and articles about this bizarre issue, example:

NSFetchedResultsController with relationship not updating

It is "just how it is".

There have been many attempts to work around this bizarre situation. There is no real solution. NSFetchedResultsController "is what it is" - it only works on basic fields (string, int, etc).

Unfortunately that is the situation.

Note: if I misunderstand the question here, sorry. But I believe this is the issue.

SwiftUI TabView crashes when displaying tab containing data that changed when said tab was invisible

It looks like a bug to me. A possible workaround may be to force-refresh the TabView:

struct ContentView: View {
@ObservedObject var model: Model

var body: some View {
TabView {
Tab(name: "Tab 1", model: model)
.tabItem {
Label("Tab 1", systemImage: "1.circle")
}
Tab(name: "Tab 2", model: model)
.tabItem {
Label("Tab 2", systemImage: "2.circle")
}
}
.id(model.value) // add here
}
}

Alternatively, instead of redrawing the view when model.value changes, you can create some other variable that changes only once (so you don't refresh the TabView every time but only once at the beginning).

SwiftUI holding reference to deleted core data object causing crash

I basically had the same issue. It seems that SwiftUI loads every view immediately, so the view has been loaded with the Properties of the existing CoreData Object. If you delete it within the View where some data is accessed via @ObservedObject, it will crash.

My Workaround:

  1. The Delete Action - postponed, but ended via Notification Center
    Button(action: {
//Send Message that the Item should be deleted
NotificationCenter.default.post(name: .didSelectDeleteDItem, object: nil)

//Navigate to a view where the CoreDate Object isn't made available via a property wrapper
self.presentationMode.wrappedValue.dismiss()
})
{Text("Delete Item")}

You need to define a Notification.name, like:

extension Notification.Name {

static var didSelectDeleteItem: Notification.Name {
return Notification.Name("Delete Item")
}
}

  1. On the appropriate View, lookout for the Delete Message

// Receive Message that the Disease should be deleted
.onReceive(NotificationCenter.default.publisher(for: .didSelectDeleteDisease)) {_ in

//1: Dismiss the View (IF It also contains Data from the Item!!)
self.presentationMode.wrappedValue.dismiss()

//2: Start deleting Disease - AFTER view has been dismissed
DispatchQueue.main.asyncAfter(deadline: .now() + TimeInterval(1)) {self.dataStorage.deleteDisease(id: self.diseaseDetail.id)}
}


  1. Be safe on your Views where some CoreData elements are accessed - Check for isFault!

VStack{
//Important: Only display text if the disease item is available!!!!
if !diseaseDetail.isFault {
Text (self.diseaseDetail.text)
} else { EmptyView() }
}

A little bit hacky, but this works for me.



Related Topics



Leave a reply



Submit