Reputation: 1223
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
inSearchMode = false
} else {
if allInterestsArray.contains(searchBar.text!.lowercaseString) {
ref.child(searchBar.text!.lowercaseString).child("users")
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
print("this should be running")
print(searchBar.text!.lowercaseString)
let handle = roomsRef.observeSingleEventOfType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
print(snapshot.value)
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
print(snap)
}
}
})
}
}
else {
print("doesn't exist")
}
}
}
I'm running a query to my firebase database when my searchBar.text is equal to something in my allInterestsArray. This is being checked for on keystroke, and I'm using dispatch_after to prevent a query being sent until the user is probably done typing.
What is happening is if I have the item in my array of "Dog" and the user gets to "dog" and then types in an "s" which makes it "dogs"..the query is still being sent off for dog and then again for dogs, so I need to cancel the query up at the top of my textDidChange func I think so on each keystroke it's being canceled, and only after a second of no typing is the query being sent off.
I was thinking of using removeHandle but I don't think that's what that's meant to be used for?
Example:
If my array = ["dog","dogs","doggies"]
The user types quickly enough so there's not a full 1 second between any two letters (one second because of the dispatch_after time I set) and they type in "dogs".. the query for dog should not have gone off, only for "dogs".
Upvotes: 0
Views: 1181
Reputation: 32922
You could add a property that gets incremented every time a dispatch_after
block is submitted, and check if that value corresponds to the current one. This way you'll be able to tell if you should kick-off or not the request. Here how your controller class might look like (included only the relevant pieces to this question):
class MyController: <base class, protocols> {
var requestsCounter = 0
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
// mark that a new request is made
self.requestsCounter++
// save the number in a local var to be used when the block
// actually executes
let currentCounter = self.requestsCounter
if searchBar.text == nil || searchBar.text == "" {
inSearchMode = false
} else {
dispatch_after(...) { () -> Void in
// if another dispatch_after was submitted, then the value of the
// currentCounter local variable won't match the property value,
// so we should no longer proceeed
guard currentCounter == self.requestsCounter else { return }
// ... the original code here
}
}
How does it work:
requestsCounter
property, so now it has a value of 1currentCounter
, whos value is captured by the dispatch_after
closure.dispatch_after
executes the closure, then the requestsCounter
will be incremented to 2, and a new currentCounter
variable, with that value, will be created and captured by the 2nd dispatch_after
closuredispatch_after
closure executes, it captured currentCounter
value will hold 1, but self.requestsCounter
will be 2, thus the closure will return without doing actual worksearchBar: textDidChange:
delegate method will increment every time the requestsCounter
property, which in turn will invalidate previously scheduled closures, as each captures the old values of the requestsCounter
property.Upvotes: 0
Reputation: 109
This problem could be solved using a global variable called "keepSearching" as below :
EDIT : I've now also used NSTimer to give a one-second gap for "keepSearching" to be false.
var keepSearching: Bool = true
var timer = NSTimer()
func timerAction() {
keepSearching = false
}
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
if searchBar.text == nil || searchBar.text == "" {
inSearchMode = false
}
else if(keepSearching) {
if allInterestsArray.contains(searchBar.text!.lowercaseString) {
// just in case user keeps on typing, invalidate the previous timer, before it happens.
timer.invalidate()
// schedule a new timer everytime.
timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(timerAction), userInfo: nil, repeats: false)
ref.child(searchBar.text!.lowercaseString).child("users")
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(1 * Double(NSEC_PER_SEC))), dispatch_get_main_queue()) { () -> Void in
print("this should be running")
print(searchBar.text!.lowercaseString)
let handle = roomsRef.observeSingleEventOfType(.Value, withBlock: { (snapshot: FIRDataSnapshot) in
print(snapshot.value)
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
for snap in snapshots {
print(snap)
}
}
})
}
}
else {
print("doesn't exist")
}
}
Upvotes: 1