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:
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.
