First of all my apology for long question, i personally prefer short precise question.
Background
i am developing a search feature within a UITableView
. To Store data i'm using core data
. To show the results i'm using NSFetchedResultsController
. In my view model i have a separate NSFetchedResultsController
to denote the search result.
Currently i'm using following method
`textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String)`
from UITextFieldDelegate
to listen the changes in my UITextField
and based on that i initiate the search.
My perform fetch
portion look like this
//
// ALL of this operation Run on MainQueue
//
// Preparing NSFetchRequest and setting
// it's predicate Goes here
//
UIApplication.shared.beginIgnoringInteractionEvents()
managedObjectContext?.perform {
do {
try self.allContactSearchFetchResultController?.performFetch()
DispatchQueue.main.async {
UIApplication.shared.endIgnoringInteractionEvents()
// calling the delegate which will reload the UITableView
}
} catch {
print("Error occur")
// TODO
// handle error here
}
}
I have stopping User interaction so that, user cannot type in keyboard anymore until this search ends. If multiple search initiated, the tableview reloads indefinitely and the application crashed while accessing following
managedObject = allContactSearchFetchResultController?.object(at: indexPath)
From UITableViewDataSource
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
To run one search operation at a time i have start ignoring user interaction. After the search ends i end ignoring user interaction.
Problem
It works fine until i notice pressing & holding the backspace button, the begin ignoring user interaction is not working.
So the question arise how did i check that the begin ignoring user interaction is not working. I put two breakpoint
one at
UIApplication.shared.beginIgnoringInteractionEvents()
another at
UIApplication.shared.endIgnoringInteractionEvents()
when i press and hold the backspace button multiple breakpoint hit the beginIgnoringInteractionEvents
without hitting endIgnoringInteractionEvents
.
Probable solution:
Already i have found a way around by using managedObjectContext?.performAndWait
instead of managedObjectContext?.perform
. But this stuck the main thread. I have to show in the UILabel
that current status is searching...
. If i use managedObjectContext?.performAndWait
it omit the stage searching and directly shows the search result after a while. Instead of this i prefer ignore the User interaction and showing the current status of the search operation.
Any help will be appreciated. T.I.A
As i wanted to stop
tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath)
while i prepare the search result. The result was taken from allContactSearchFetchResultController
but i assign it to a new NSFetchedResultsController
but the UITableView
was calling for managedObject
based on the old section count and row count in allContactSearchFetchResultController
as follows
Section
allContactSearchFetchResultController?.sections?.count
Row
allContactSearchFetchResultController?.sections[section].numberOfObject // There are more guard involved in this number of row fetching, for simplicity purpose i omitted those.
But when i assign allContactSearchFetchResultController
to a new NSFetchedResultsController
, table view keep asking for old managed object at IndexPath
from cellForRowAt
because the table view has no idea that i have assigned it to new NSFetchedResultsController
. So in this scenario my solution is to create a new local NSFetchedResultsController
prepare it, perform fetch on it on managedObjectContext's queue. While the results are ready switch to the mainQueue and assign it. Mine perform fetch portion now looks like this.
//
// ALL of this operation Run on MainQueue
//
let allFetchRequest = prepareCommonFetchRequest()
allFetchRequest.predicate = NSPredicate.init(format: "name CONTAINS[cd] %@ || mobile_number CONTAINS[cd] %@", searchString, searchString)
let tempAllContactSearchFetchResultController = NSFetchedResultsController.init(fetchRequest: allFetchRequest,
managedObjectContext: managedObjectContext!,
sectionNameKeyPath: "section_key",
cacheName: nil)
managedObjectContext?.perform {
do {
try self.allContactSearchFetchResultController?.performFetch()
DispatchQueue.main.async {
self.allContactSearchFetchResultController = tempAllContactSearchFetchResultController
// calling the delegate which will reload the UITableView
}
} catch {
print("Error occur")
// TODO
// handle error here
}
}