2
votes

I am doing a CoreData fetch inside my init() method of the View. I am not using FetchRequest in SwiftUI, as I need a predicate based on a parameter which is send to the View aswell. Using that parameter in the @FetchRequest will cause an error, as the variable has not been initialized.

I am doing following fetch Request inside the init()

//FetchRequest
let fetch : NSFetchRequest<Article>
self.articleRows = [Article]()
fetch = Article.fetchRequest() as! NSFetchRequest<Article>
fetch.predicate = NSPredicate(format: "type == %d", self.type)

do {
    self.articleRows = try NSManagedObjectContext.current.fetch(fetch)
} catch
{
}

That is working fine. I am displaying all my data inside a ForEach loop.

ForEach(self.articleRows, id:\.self) { article in

However, when I am deleting an entity from my context I need to refresh the view to display the changes. On delete action I toggle a @State variable, to refresh the view. That works, however the entity is still inside my Array. Thats due to the fact that init() is not called again and the fetch request is not made again. If init() would be called, the deleted entity wouldn't be there anymore.

What is the best approach here? How can I refetch my CoreData entities.

My current solution: I am currently using a Binding inside my Fetch view. If I make changes I toggle that binding and my parent view reloads. The will cause my child view (fetch View) to reload aswell and the fetch request is made again. Is that the best way? Any simpler solution for that?

2

2 Answers

10
votes

So, I found a way of solving my problem.

What was the problem?

The View was not updating because I wasn't using FetchRequest property wrapper, because I need a instance variable in that FetchRequest. So I need to do the Fetching inside my Init() method. But what I did was, I just fetched my items once in the init() and that won't be updated unless the parent with is reloaded.

How to solve it without annoying parent update?

Instead of doing a manual fetch only once in the init() I used a FetchRequest and initialized it in the init(), so it still behaves as FetchRequest property wrapper, like an Observable Object.

I declared it like that:

@FetchRequest var articleRows : FetchedResults<Article>

And inside the init()

//Here in my predicate I use self.type variable 
var predicate = NSPredicate(format: "type == %d", self.type)

//Intialize the FetchRequest property wrapper
self._articleRows = FetchRequest(entity: Article.entity(), sortDescriptors: [], predicate: predicate)
1
votes

Assuming you have your managedObjectContext set up as such;

@Environment(\.managedObjectContext) var moc

then I believe this solution might work for you.

func deleteArticle(at index: IndexSet) {
    for i in index {
        // pull the article from the FetchRequest
        let article = articleRows[i]
        moc.delete(article)
    }
    // resave to CoreData
    try? moc.save()
}

call that method at the end of your ForEach block with;

.onDelete(perform: deleteArticle)

That said I am not familiar with the way you are doing the fetch request so you made need to do some tweaking.

  • Dan