0

I have method called collectData in my app which is the most important part of my View Controller. In that method I do a couple of signicant things (downloading, parsing, saving to persistent store), so it would be easier for you to take a look:

-(void)collectData
{
    // Downloading all groups and saving them to Core Data
    [[AFHTTPRequestOperationManager manager] GET:ALL_GROUPS parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
        NSMutableDictionary* groups = [NSMutableDictionary new];
        NSMutableArray* newIds = [NSMutableArray new];
        NSError *error;

        // Saving everything from response to MOC
        for (id group in responseObject) {
            Group *groupEntity = [NSEntityDescription insertNewObjectForEntityForName:@"Group" inManagedObjectContext:self.moc];

            groupEntity.name = [group valueForKey:@"name"];
            groupEntity.cashID = [group valueForKey:@"id"];
            groupEntity.caseInsensitiveName = [[group valueForKey:@"name"] lowercaseString];
            groupEntity.selected = @NO;

            // Filling up helping variables
            groups[groupEntity.cashID] = groupEntity;
            [newIds addObject:groupEntity.cashID];
        }

        // Fetching existing groups from Persistant store
        NSFetchRequest* r = [NSFetchRequest fetchRequestWithEntityName:@"Group"];
        [r setIncludesPendingChanges:NO];
        r.predicate = [NSPredicate predicateWithFormat:@"cashID IN %@",newIds];
        NSArray *existingGroups = [self.moc executeFetchRequest:r error:&error];

        // Deleting groups which already are in database
        for (Group* g in existingGroups) {
            Group* newGroup = groups[g.cashID];
            g.name = [newGroup valueForKey:@"name"];
            g.cashID = [newGroup valueForKey:@"cashID"];
            g.caseInsensitiveName = [[newGroup valueForKey:@"name"] lowercaseString];
            [self.moc deleteObject:newGroup];
        }

        // Saving Entity modification date and setting it to pull to refresh
        [self saveModificationDate:[NSDate date] forEntityNamed:@"Group"];
        [self.pullToRefreshView.contentView setLastUpdatedAt:[self getModificationDateForEntityNamed:@"Group"]
                                       withPullToRefreshView:self.pullToRefreshView];

        // Save groups to presistant store
        if (![self.moc save:&error]) {
            NSLog(@"Couldn't save: %@", [error localizedDescription]);
        }

        [[self fetchedResultsController] performFetch:&error];
        [self.pullToRefreshView finishLoading];
        [self.tableView reloadData];
    } failure:^(AFHTTPRequestOperation *operation, NSError *error) {
        // Show alert with info about internet connection
        [self.pullToRefreshView finishLoading];
        UIAlertView *internetAlert = [[UIAlertView alloc] initWithTitle:@"Ups!" message:@"Wygląda na to, że nie masz połączenia z internetem" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil, nil];
        [internetAlert show];
    }];
}

So when I start collecting data (first run or push to refresh) this method is blocking UI. I want to avoid this but when I put the success block into another dispatch_async and get back to main queue only for [self.tableView reloadData] I face problem with saving to persistent store or something with bad indexes.

How can I do this whole thing in background and leave UI responsive to the user?

cojoj
  • 6,405
  • 4
  • 30
  • 52
  • 1
    Look at this sample app, it includes a background loader method. Note that you have to create a background thread with its own managedObjectContext and you need to register for MOC notifications and then merge changes from the main MOC (on the main thread). Look at the code and if you have specific questions post them. http://ossh.com.au/design-and-technology/software-development/sample-library-style-ios-core-data-app-with-icloud-integration/ – Duncan Groenewald Feb 26 '14 at 21:21
  • Now I've added some logs and I see that `[self.moc save:&error]` is blocking everything and is slow as hell... I think this is the problem... – cojoj Feb 26 '14 at 21:32

2 Answers2

0

Just an idea, give it a try using dispatch_sync. Have a look at this explanation here where log result something similar to your need. Put [yourTableView reloadData] after synchronous block.

Hope it helps!

Community
  • 1
  • 1
NeverHopeless
  • 11,077
  • 4
  • 35
  • 56
  • Unfortunately, still blocking the UI. When it's blocked I can swipe and after it's unblocked again I'm moved to this place. – cojoj Feb 26 '14 at 20:34
  • Have you tried a combination of both dispatch function ? one for success block and one for reloading data ? – NeverHopeless Feb 26 '14 at 20:38
  • Yes... UI is being blocked right after I pull to refresh so I assume that I must put this whole core-data-saving thing into another queue but nothing happens! – cojoj Feb 26 '14 at 20:42
0

It seems AFNetwork call is not async so just try to call your method via performselector.

chintan adatiya
  • 1,233
  • 14
  • 17