0

I continue to read that i should use [_context performBlock]:^... to do asynchronous searches when using core-data. What i cant figuered out for the life of me is how to subscribe to the end or the complete event sort of speak of this block. In other words in my code I'm using a UIActivityIndicatorView prior to my fetch of data and i would like to remove this view once the data has been retrieve. However I don't understand how to properly accomplish this. In the past i have used dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{... and then implemented

dispatch_async(dispatch_get_main_queue(), ^(void) { 

to know when the que is finish doing the background processing. I might have this all completely wrong , but i figuered i ask the question. What delegate do i need to implement or what method do i need to subscribe to. To know when my fetch is complete that i have just executed within the performBlock:

Again my end goal is to set a UIActivityIndicatorView visible before the fetch , fetch the data and set it back to not visible. thanks in advance for any help you guys can offer.

**update**

I'm required to do the search asynchronously due to the large amount of records that i have to search through. I have roughly 195k records and so there is like a 1 to 2 second lag if i try to do this in the main thread when the user start to type letters in the search bar. Hence the reason why i throw up a UIActivityIndicatorView and then I do the search on the background thread and update the main thread when I'm done.

This is the code I'm using to acomplish this.

@property (strong, nonatomic)  NSArray *filteredLocations;
@property (strong, nonatomic) NSArray * locationsFiltered;



 //returns the search for this particular search.
- (BOOL)searchDisplayController:(UISearchDisplayController *)controller
shouldReloadTableForSearchString:(NSString *)searchString
{
    if ([searchString length ]>= 3) {
        [self searchForText:searchString];
        return YES;
    }
    else{
        self.filteredLocations = nil;
        return NO;
    }

    //return YES;
}





- (void)searchForText:(NSString *)searchText
{
    if (self.context && self.isSearching == FALSE)

    {
        //[searchController ]
        //[self.searchDisplayController.searchResultsTableView.]
        self.isSearching = TRUE;
        NSString *predicateFormat = @"%K BEGINSWITH[cd] %@ and state ==%@";
        NSString *searchAttribute = @"name";
        self.filteredLocations = nil;

        NSPredicate *predicate = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute, searchText,Searchstate];

        [self.searchFetchRequest setPredicate:predicate];

           UIActivityIndicatorView *spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];

            if (self.spinnerShowing ==FALSE) {


                spinner.center = CGPointMake(160, 190);
                spinner.hidesWhenStopped = YES;
                spinner.color = [UIColor grayColor];


                [self.view addSubview:spinner];

                //self.spinnerShowing = ;

                [spinner startAnimating];
         }

        [_context performBlock:^(void){

            dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{


            NSError *error = nil;

        self.filteredLocations = [self.managedObjectContext executeFetchRequest:self.searchFetchRequest error:&error];
    NSLog(@"this is how many items are in the filtered locations at this time %i", [_filteredLocations count]);
            if (error)

            {
            NSLog(@"searchFetchRequest failed: %@",[error localizedDescription]);

            }
                dispatch_async(dispatch_get_main_queue(), ^(void) {

                    NSLog(@"stopping the spinner now.");

                    self.spinnerShowing = FALSE;
                    [spinner stopAnimating];
                    [spinner hidesWhenStopped];
                    self.isSearching = FALSE;
                    //self.searchDisplayController.searchResultsTableView.hidden = NO;
                  //  [searchController reloadData];
                    [self.searchDisplayController.searchResultsTableView reloadData];

                });
            });
        }];
    }
}
Miguel
  • 2,019
  • 4
  • 29
  • 53

1 Answers1

1

It seems you have changed your question significantly after I started writing my response...

Let me know if this is of any use - otherwise I will delete as it no longer relates to your question.


Generally a second NSFetchedResultsController is, in my opinion and experience, overkill.

Note that this is written on the understanding that you have correctly implemented an instance of UISearchBar and UISearchDisplayController, either programmatically or using a Storyboard. Read a previous answer I have written that may assist if you are unsure.

As you are using a UISearchDisplayController delegate method in your question I will assume that you have declared your intended use against your @interface (i.e. <UISearchBarDelegate, UISearchDisplayDelegate>).

So finally to my answer, I prefer to implement the following code to create a reliable search method for any UITableViewController that implements an NSFetchedResultsController...

Create a public or private property (depending on your requirements) NSMutableArray to hold the contents of your search results:

@property (nonatomic, retain) NSMutableArray *searchResults;

You use this NSMutableArray to set your UITableViewController data source methods in the case that:

if (tableView == self.searchDisplayController.searchResultsTableView) {
    //code for searchResultsTableView
}

Then I use UISearchDisplayController delegate methods to control any active instance of self.searchController.searchResultsTableView.

This is the most important one...

- (BOOL)searchDisplayController:(UISearchDisplayController *)controller shouldReloadTableForSearchString:(NSString *)searchString {
    //  Set search predicate and filter array
    if (searchString && searchString.length) {

        //  Your predicateFormat and searchAttribute
        NSString *predicateFormat = @"%K BEGINSWITH[cd] %@ and state ==%@";
        NSString *searchAttribute = @"name";

        //  My code
        NSPredicate *searchPredicate = nil;
        NSArray *fetchedObjects = nil;
        NSMutableArray *arrayResults = nil;

        [self.searchResults removeAllObjects];

        searchPredicate = [NSPredicate predicateWithFormat:predicateFormat, searchAttribute, searchString, searchState];
        fetchedObjects = self.fetchedResultsController.fetchedObjects;
        arrayResults = [NSMutableArray arrayWithArray:[fetchedObjects filteredArrayUsingPredicate:searchPredicate]];
        [self setSearchResults:arrayResults];
    }
    //  Return YES to reload the search result table view
    return YES;
}

PS- change your Searchstate variable to searchState!

You might also like to implement the following UISearchDisplayController delegate method.

- (void)searchBarCancelButtonClicked:(UISearchBar *)searchBar {
    [self setSearchResults:nil];
}

Hope this helps.

Community
  • 1
  • 1
andrewbuilder
  • 3,629
  • 2
  • 24
  • 46
  • Andrew thanks for the response. I will take a stab at your solution. you are right I changed my question dramatically it had been 6 hours and i had gotten even more frustrated with my issue. My biggest issue however is the fact that i need to do this asynchronously. Reason being is that i do not want to lock my main thread. Do you have any suggestions on how to properly acomplish that? Thanks a million in advance truly appreciated. – Miguel Jul 19 '14 at 05:00
  • @Miguel some questions... 1. Why are you required to do an asynchronous search? 2. Are you searching beyond the contents of your `NSFetchedResultsController`? 3. Could you please edit your question to provide a little more information on what it is you are trying to achieve - for example - the `NSFetchedResultsController` feeds `UITableViewController` data source methods, and you need to obtain unrelated/related records / records only from within the current `fetchedObjects` property of your FRC / downloaded records from an external data source??? – andrewbuilder Jul 19 '14 at 05:16
  • Question updated i do not need to obtain unrelated records but there are so many records that the ui tends to freeze for 1 or 2 seconds, hence the reason why im trying to do this from a background thread. my concern is that I may not be doing this properly. – Miguel Jul 19 '14 at 05:39
  • @Miguel have you thought about or investigated faulting your `NSFetchedResultsController` results instead of attempting the background fetch? – andrewbuilder Jul 20 '14 at 01:28
  • never heard of faulting in all my readings. can you please explain? I'll google for what it means as well. – Miguel Jul 20 '14 at 02:58
  • OK @Miguel so how you progressing with faulting? The best I can suggest is this... from The Pragmatic Bookshelf – "Core Data, 2nd Edition, Data Storage and Management for iOS, OS X, and iCloud" (Jan 2013) by Marcus S. Zarra, and in particular Chapter 4 Performance Tuning, specifically 4.3 Fetching, 4.4 Faulting, and 4.5 Access Patterns. If you still need help let me know. – andrewbuilder Jul 21 '14 at 15:25
  • not good i cant find an clear answer on faulting or an example of how it actually works. I'm trying to buy that book that you pointed out i found it yesterday and it seems like it would be a good place to – Miguel Jul 21 '14 at 19:31
  • 1
    Another book suggestion @Miguel... It's an old one but a worthwhile read - Chapter 7 titled "Tuning Performance and Memory Usage", from "Pro Core Data for iOS", by Michael Privat and Robert Warner (soon to be superceded by their new book "Pro iOS Persistence Using Core Data"). I would recommend you complete the tutorial in chapter 7. It will teach you a lot about faulting and how to make efficient calls to the Core Data stack. While I prefer the Zarra style of Core Data code, this relatively old Privat / Warner book takes a practical approach to educating the reader that I found very helpful. – andrewbuilder Jul 25 '14 at 15:02
  • Thanks Andrew for the suggestions i will get my paws on that book. I like books where you can actually follow the code this Marcus Zarra book is kind of hard to follow. I was hoping for a book where you can follow with a sample project from beginning to end. On this book he gives you the project and then you follow, not as fun on my opinion. – Miguel Jul 25 '14 at 17:43
  • Agreed, perhaps more theoretical, but as you become more proficient at using Core Data you will find the Zarra book invaluable. For me it took a long while for some of the concepts to sink in. Keep at it, revisit it every now and then, and you will realise how much you are learning. For example Zarra's ideas about parent-child `NSManagedObjectContext`s allowed me to build a really simple and effective implementation so my primary context never blocks on a save. In the meantime and if I have time I'll try to work a solution to your specific asynchronous fetch problem. – andrewbuilder Jul 26 '14 at 00:51