Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delayed search in swift iOS app

Tags:

ios

swift

I have UITextField for entering search string and UITableView with results. What i want is to run search function when user entered more than 3 letters and it at least 0,5 sec passed since last symbol added to UITextView.

I found (Detect when user stopped / paused typing in Swift) function and I added it to my ViewController that have class SearchVC and method server_search

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    NSObject.cancelPreviousPerformRequests(
        withTarget: self,
        selector: #selector(SearchVC.server_search),
        object: textField)
    self.perform(
        #selector(SearchVC.server_search),
        with: textField,
        afterDelay: 0.5)
    return true
}

But nothing happens.

like image 291
moonvader Avatar asked Apr 10 '17 16:04

moonvader


2 Answers

While using a Timer is still a valid answer, since Swift 3, you can use a DispatchWorkItem

var workItem: DispatchWorkItem?

func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {

    // Cancel any outstanding search
    self.workItem?.cancel() 

    guard let text = textField.text, let textRange = Range(range, in: text) else {
        return true
    }

    let updatedText = text.replacingCharacters(in: textRange, with: string)

    guard updatedText.count >= 3 else { 
        return true
    }

    // Set up a DispatchWorkItem to perform the search
    let workItem = DispatchWorkItem { [weak self] in
        self?.performSearch(updatedText)
    }

    // Run this block after 0.5 seconds
    DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: workItem)

    // Keep a reference to it so it can be cancelled
    self.workItem = workItem 

    return true   
}

func performSearch(_ text: String) {

} 
like image 103
Ashley Mills Avatar answered Oct 29 '22 20:10

Ashley Mills


The use of Timers have some advantages. With your implementation you would cancel all the performs in for your object, which is something that may go out of control.

A Timer, instead, lets you fine grain control it. See the following implementation:

var searchTimer: Timer?
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
    // Invalidate remove the previous timer, since it's optional if the previous timer is already cancelled or nil, it won't affect execution
    searchTimer?.invalidate()
    searchTimer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false, block: { (timer) in
        //do Something crazy
        self.server_search()
    })
    return true
}
like image 5
The Horny Coder Avatar answered Oct 29 '22 21:10

The Horny Coder