I am using Swift 4 to build a single view iOS 11 application that has a UITableViewController that is also defined as a delegate for a NSFetchedResultsController.
class MyTVC: UITableViewController, NSFetchedResultsControllerDeleagate {
var container:NSPersistentContainer? =
(UIApplication.shared.delegate as? AppDelegate)?.persistentContainer
var frc : NSFetchedResultsController<Student>?
override func viewDidLoad() {
container?.performBackgroundTask { context in
// adds 100 dummy records in background
for i in 1...100 {
let student = Student(context: context)
student.name = "student \(i)"
}
try? context.save() // this works because count is printed below
if let count = try? context.count(for: Student.fetchRequest()) {
print("Number of students in core data: \(count)") // prints 100
}
} // end of background inserting.
// now defining frc:
if let context = container?.viewContext {
let request:NSFetchRequest<Student> = Student.fetchRequest()
request.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)]
frc = NSFetchedResultsController<Student> (
fetchRequest: request,
managedObjectContext: context,
sectionNameKeyPath: nil,
cacheName: nil )
try? frc?.performFetch() // this works and I get no errors
tableView.reloadData()
frc.delegate = self
} // end of frc definition
}
}
If I add one row of Student using the viewContext, the frc will fire the required methods to show it in the tableView. However, the 100 dummy rows are not shown. In fact, If I try to tell the tableview to reload after the insertion is done, my app starts to behave weirdly and becomes buggy, and does not do what it should do (i.e: does not delete rows, does not edit, etc).
But If I restart my app, without calling the dummy insertion, I can see the 100 rows inserted from the previous run.
The only problem is that I can't call tableView.reloadData() from the background thread, so I tried to do this:
// after printing the count, I did this:
DispatchQueue.main.async { [weak self] in
self?.tableView.reloadData() // causes UI to behave weirdly
}
then I tried to call viewContext.perform to reload the table view in the proper thread
func viewDidLoad() {
// code for inserting 100 dummy rows in background thread
// code for defining frc and setting self as delegate
if let context = container?.viewContext {
context.perform { [weak self] in
self?.tableView.reloadData() // that also causes UI to behave weirdly
}
}
}
How can tell my tableview to reload and display the 100 dummy rows in a thread-safe manner?