Benedict's Soapbox

Replacing an NSTimer with performSelector:withObject:afterDelay:

When performing costly tasks that are triggered by user input it is sensible to have a small pause before executing the task to ensure that the user has finished inputing their data. This small pause prevents a request being made for each key press. An example of this is the search function in the App Store app. The app waits for about half a second after each key press before executing the search. It is possible to achieve this by using NSTimers. However, an easier way is to use performSelector:withObject:afterDelay: and cancelPreviousPerformRequestsWithTarget:selector:object:. Here’s how:

- (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string  
{  
    //because the view can be unloaded we must store all data so that its state can be restored in viewDidLoad  
    self.searchQuery = [textField.text stringByReplacingCharactersInRange:range withString:string];

    SEL fetchSearchResults = @selector(fetchSearchResults);

    //cancel the previous search request  
    [[self class] cancelPreviousPerformRequestsWithTarget:self selector:fetchSearchResults object:nil];

    //perform the search in a seconds time. If the user enters addition data then this search will be cancelled by the previous line  
    [self performSelector:fetchSearchResults withObject:nil afterDelay:1];

    return YES;  
}

-(void)fetchSearchResults  
{  
    NSLog(@"performing search");  
    //...  
}