swifttimeoutsettimeoutcleartimeout

What is the "cleartimeout" equivalent for Swift?


I'm trying to set up a timeout on a text input field that only implements the inner code a second after the user stops typing. So while the user is typing, I would continually call a cleartimeout and re-initiate the setTimeout.

I was originally looking at the performSelector function in Objective C, but it looks like there is no Swift equivalent for this.

Then I moved on to the GCD functions in Swift, looking for a way to execute this.

Here is what I came up with:

var delta: Int64 = 1 * Int64(NSEC_PER_SEC)
var time = dispatch_time(DISPATCH_TIME_NOW, delta)
dispatch_suspend(dispatch_get_main_queue())
dispatch_after(time, dispatch_get_main_queue(), {
    println("timeout")
});

The dispatch_suspend function is not working as I was hoping.

Maybe the dispatch functions are not appropriate here?


Solution

  • You can use dispatch_after rather than one of the performSelector. But I don't think either of these is what you want.

    If you are looking to call a block of code only after it's been idle for one second, then I think you want to use a timer (e.g. Timer is fine, or you could use a dispatch timer). Bottom line, every time you get keyboard interaction, see if there is a pending timer, and if so, invalidate it, and then schedule the new one.

    So I might be inclined to do something like the following in Swift 3. For example, in iOS 10 and later, you can use the block rendition:

    weak var timer: Timer?
    
    func resetTimer() {
        timer?.invalidate()
        timer = .scheduledTimer(withTimeInterval: 1.0, repeats: false) { [weak self] timer in
            // do whatever you want when idle after certain period of time
        }
    }
    

    Or, if you need to support earlier iOS versions that do not have block-based timers:

    weak var timer: Timer?
    
    func resetTimer() {
        timer?.invalidate()
        timer = .scheduledTimer(timeInterval: 1, target: self, selector: #selector(handleIdleEvent(_:)), userInfo: nil, repeats: false)
    }
    
    @objc func handleIdleEvent(_ timer: Timer) {
        // do whatever you want when idle after certain period of time
    }
    

    If you use this latter approach, though, recognize that this Timer keeps a strong reference to its target, so you might want to cancel the timer in viewDidDisappear (but not deinit). This is why we prefer the block-based rendition or GCD timers.


    By the way, I am not sure what your intent of dispatch_suspend was, but don't suspend the main queue. You never want to do anything that could potentially interfere with the main queue's timely processing of events (i.e., never block/suspend the main queue).