1
votes

I would like to understand why fetchBatchSize is not working correctly with NSFetchRequest.

On initial fetch, after fault array loaded, the data array are loaded in a loop by batchSize until all data is loaded rather than until just the data required is loaded. using coreData instruments or editing run scheme clearly shows all items data loaded in loops of batchSize number of items until all data loaded instead of only loading data of those rows appearing in tableView.

Expected result:

Expect all items in fault array to be loaded followed by only first batch as data to be loaded.

Actual result:

All items loaded in fault array and then all data loaded in looped batches of batch size prior to any scrolling.

Here is the sample project viewController I have created to demonstrate this:

import UIKit
import CoreData

class BatchTestTableViewController: UITableViewController {

    var context: NSManagedObjectContext!
    var items: [Item]?

    override func viewDidLoad() {
        super.viewDidLoad()

        tableView.register(UINib(nibName: "BatchTableViewCell", bundle: nil), forCellReuseIdentifier: "BatchTableViewCell")

        let appDelegate = UIApplication.shared.delegate as! AppDelegate
        self.context = appDelegate.persistentContainer.viewContext

        self.initializeItems()

        if items != nil {
            if items!.count == 0 {
                for i in 0..<10000 {
                    let entityDescription =  NSEntityDescription.entity(forEntityName: "Item", in: context)
                    let item = Item(entity: entityDescription!, insertInto: self.context)
                    item.objectId = UUID().uuidString
                    item.itemId = UUID().uuidString
                    item.itemName = "\(i)"

                }
                try? context.save()
                self.initializeItems()
            }
        }
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        if items != nil {
            return items!.count
        }
        return 0
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: "BatchTableViewCell", for: indexPath) as! BatchTableViewCell
        let item = self.items![indexPath.row]
        cell.itemNameLabel.text = item.itemName
        return cell
    }

    override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 50
    }

    // MARK: - Helper Methods

    func initializeItems() {
        let request = NSFetchRequest<Item>(entityName: "Item")
        let messageSort = NSSortDescriptor(key: "itemName", ascending: true)
        request.fetchBatchSize = 20
        request.sortDescriptors = [messageSort]

        do {
            self.items = try context.fetch(request)
        } catch {
            print("Could not fetch \(error)")
        }
    }

}

And here is the read out from CoreData Instruments:

enter image description here

From the above image of coredata instrument it can be seen that all data is loaded in 20 batch size loop until all 10,000 items are loaded prior to any scrolling of the tableView.

1

1 Answers

0
votes

fetchBatchSize doesn't limit your data fetching. It enables fetchRequest to fetch data in a batch of 20 in your case. You can use this feature to restrict the working set of data in your application. In combination with fetchLimit, you can create a subrange of an arbitrary result set. You should use fetchLimit together

    request.fetchLimit = 20

From Apple doc

When the fetch is executed, the entire request is evaluated and the identities of all matching objects recorded, but only data for objects up to the batchSize will be fetched from the persistent store at a time. The array returned from executing the request is a proxy object that transparently faults batches on demand. (In database terms, this is an in-memory cursor.)