How can I debounce a method call?

前端 未结 13 1525
醉梦人生
醉梦人生 2020-11-30 01:18

I\'m trying to use a UISearchView to query google places. In doing so, on text change calls for my UISearchBar, I\'m making a request to google pla

13条回答
  •  南方客
    南方客 (楼主)
    2020-11-30 01:43

    Despite several great answers here, I thought I'd share my favorite (pure Swift) approach for debouncing user entered searches...

    1) Add this simple class (Debounce.swift):

    import Dispatch
    
    class Debounce {
    
        private init() {}
    
        static func input(_ input: T,
                          comparedAgainst current: @escaping @autoclosure () -> (T),
                          perform: @escaping (T) -> ()) {
    
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                if input == current() { perform(input) }
            }
        }
    }
    

    2) Optionally include this unit test (DebounceTests.swift):

    import XCTest
    
    class DebounceTests: XCTestCase {
    
        func test_entering_text_delays_processing_until_settled() {
            let expect = expectation(description: "processing completed")
            var finalString: String = ""
            var timesCalled: Int = 0
            let process: (String) -> () = {
                finalString = $0
                timesCalled += 1
                expect.fulfill()
            }
    
            Debounce.input("A", comparedAgainst: "AB", perform: process)
            Debounce.input("AB", comparedAgainst: "ABCD", perform: process)
            Debounce.input("ABCD", comparedAgainst: "ABC", perform: process)
            Debounce.input("ABC", comparedAgainst: "ABC", perform: process)
    
            wait(for: [expect], timeout: 2.0)
    
            XCTAssertEqual(finalString, "ABC")
            XCTAssertEqual(timesCalled, 1)
        }
    }
    

    3) Use it wherever you want to delay processing (e.g. UISearchBarDelegate):

    func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
        Debounce.input(searchText, comparedAgainst: searchBar.text ?? "") {
            self.filterResults($0)
        }
    }
    

    Basic premise is that we are just delaying the processing of the input text by 0.5 seconds. At that time, we compare the string we got from the event with the current value of the search bar. If they match, we assume that the user has paused entering text, and we proceed with the filtering operation.

    As it is generic, it works with any type of equatable value.

    Since the Dispatch module has been included in the Swift core library since version 3, this class is safe to use with non-Apple platforms as well.

提交回复
热议问题