Improving app performance is a key concern for all app developers, especially when it comes to the UI. A user can enter a continuous stream of input as they type, resize, or scroll through your app. If any of these actions feel sluggish you’ll likely hear complaints and lose users. Expensive handlers for these actions can bog your app down and make it feel unresponsive. Debouncing minimizes this problem by limiting how often a function can fire and keeping your app responsive.

Debouncing

Recently I was introduced to concept of debouncing in JavaScript, though the concept isn’t limited to web programming. The notion of a debounce function is simple: limit how often function can fire by waiting a certain amount of time before executing it. For an event that fires continuously during input, this effectively means fire the function x amount of time after input stops. If input resumes before that amount of time has passed then you restart the timer.

Applying the concept

I realized I could use this to make improve the data filtering in some FlexGrid 101 samples. Filtering data when you have a large number of rows (10,000+) in a FlexGrid can be an expensive operation. Because of that, most FlexGrid samples require some kind of final input to start filtering (be it pressing enter or a UIButton). This is not ideal solution though, and I’d rather that the filtering occurs automatically. Using debouncing, I can make a small change to have the filtering begin a short interval after keyboard input ends. While there are a few different debouncing libraries available through GitHub, I wanted to walk through some simple changes I can make to the Full Text Filter sample in FlexGrid101 to improve the user experience using this concept.

Implementing a simple solution

Apple already provides you with NSTimer for scheduling when a particular method fires, which gives me a large chunk of what I’ll need. Basically, I'll need to go through the following steps:

  1. Use the timer in the textfield:shouldChangeCharactersInRange:ReplacementString: method (part of the UITextField protocol), which fires when the text in the UITextField changes
  2. Inside this method schedule the filter to fire in 2 seconds using a timer
  3. Check if there's a preexisting timer, in which case invalidate it (that way only the most recent timer will execute)

This ensures that the filter function is only triggered after 2 seconds pass without user input. Objective-C


-(bool)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{  
    if(timer != nil){  
        [timer invalidate];  
    }  
    timer = [NSTimer scheduledTimerWithTimeInterval:2 target:self selector:@selector(filter) userInfo:nil repeats:false];  
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];  

    return true;  
}  

I also made a change to the filtering logic so that it executes asynchronously. This isn’t a huge change, but it does improve UI responsiveness. This ensures that filtering doesn’t hold up the main UI Thread. Basically I’ll wrap the filtering logic into its own function, wrap it in a GCD block, and, once the filtering completes, I can call setNeedsDisplay to refresh the FlexGrid. I’ll just paraphrase what this changes looks like since the filtering code is lengthy (though the included sample has the full implementation). Objective-C


-(void)filter{  
    dispatch\_queue\_t myQueue = dispatch\_queue\_create("myQueue", nil);  
    dispatch_async(myQueue, ^{  
        //filtering code... see sample for full implementation  
        dispatch\_async(dispatch\_get\_main\_queue(), ^{[flex setNeedsDisplay];});  
    });  
}  

Closing thoughts

Debouncing function calls can noticeably improve the responsiveness of your application with a few small changes. Though this was relatively easy to implement in my sample, and a quick google search will reveal several different open source debouncing libraries for iOS if you want something a little less specific. Overall it’s easy to improve you applications performance by debouncing events that occur often and have expensive calculations behind them.

Download the DebouncingFilter sample >>