简体   繁体   中英

Stopping a query to firebase?

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

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")
    }
}

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:

  • the first time the delegate method is called, it increments the requestsCounter property, so now it has a value of 1
  • this value (1) gets saved into the local variable currentCounter , whos value is captured by the dispatch_after closure.
  • if the delegate method is called before 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 closure
  • now when the first dispatch_after closure executes, it captured currentCounter value will hold 1, but self.requestsCounter will be 2, thus the closure will return without doing actual work
  • the same logic applies for subsequent requests, the searchBar: 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM