0

In my app which uses SwiftUI for interface I have a button that performs an action:

func getBooksCountNumber() -> Int {
    var countResult = 0
    let booksFetch = NSFetchRequest<NSFetchRequestResult>(entityName: "Book")
    
    do {
        let booksFetch = try self.persistentContainer.viewContext.fetch(booksFetch) as! [Book]
        countResult = booksFetch.count
        print("countResult is \(countResult)")
    } catch {
        fatalError("Failed to fetch: \(error)")
    }

    return countResult
}

When I delete rows in the app I use moc.delete(book) and try? moc.save(). I see that deleted rows gone, using interface at icloud.developer.apple.com. For example — if from 3 records I delete 1, I still getting 3 in countResult and not 2. Any help is appreciated.

Igor R.
  • 399
  • 4
  • 23
  • Unrelated but there is a [count(for:)](https://developer.apple.com/documentation/coredata/nsmanagedobjectcontext/1506868-count) API and specify the generic type in the fetch request `NSFetchRequest` – vadian Sep 19 '20 at 18:51

1 Answers1

0

SwiftUI relies on data invalidation to force a View to reload.

So you need to think about how you make your View code rely on the change in your data, for countResult (as an example).

If you're using SwiftUI, I'd recommend that you consider using the @FetchRequest property wrapper to acquire your dataset...

@FetchRequest(entity: Book.entity(),
              sortDescriptors: []
) var books: FetchedResults<Book>

Note: the sortDescriptors: parameter is required, so if you do not want to sort the FetchedResults, you include an empty array [].

Then use the property associated with that wrapper to feed the data into your view...

struct YourView: View {
    // @FetchRequest here
    var body: some View {
        ...
        Text("Book count = \(books.count)")
        ...
    }
}

This way, you don't even need to press the button to determine the count, because the Text view is updated each time a book is added to or deleted from your Book entity.

Written in a SwiftUI way, as the data changes, the Text view component will be invalidated and the view struct will be forced to redraw.

If you want to keep the button in your view, you could do something like this instead...

    var body: some View {
        ...
        Button(action: {
            // code to perform your action here...
        }) {
            Text("You have \(books.count) books")
        }
        ...
    }

Where the label of the button includes the count of books (and the button performs some other action).

andrewbuilder
  • 3,629
  • 2
  • 24
  • 46