简体   繁体   中英

Imported records with Magical Record not immediately displayed in tableView

My app downloads XML files from a 3rd party database (using AFNetworking), then uses NSXMLParser to pull out the info I need, creating records with sub-nodes 'on the go'. I'm using MagicalRecord to communicate with CoreData. The download and parsing part works fine, but after I'm done, not all entries are visible in my table, I'm guessing that MR isn't done yet when I refresh my tableView. If I navigate away from the tableView and back, all the records are shown.

I'm using a dispatch_group to make sure that the UI isn't updated until all download and import tasks are done. Using NSLog I have confirmed that the code in the dispatch_group_notify block is indeed executed after all the downloading and parsing is done.

Any suggestions on how to fix the code so that all records are immediately shown in the table?

Here's some code:

-(void) importRecords
{
    dispatch_group_t dispatchGroup = dispatch_group_create();

for (NSString *s in self.newRecords)
    {
       dispatch_group_enter(dispatchGroup);

       NSData *data = [self downloadDataforRecord: s]; // using AFNetworking
       if (data)
         [self importData: data];

       dispatch_group_leave(dispatchGroup);
     }

     dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^{
     // done with all the downloading and parsing
     // now update the UI

        self.records = [NSMutableArray arrayWithArray: [Record MR_findAllSortedBy: @"id" ascending: ascending]];

        [self.tableView reloadData];
    });
}


- (void) importData:(NSData *) data
{
    [MagicalRecord saveWithBlock: ^(NSManagedObjectContext *localContext) {
        Root *rootObject = [[Root alloc] init];
        rootObject.context = localContext;

        NSXMLParser *parser = [[NSXMLParser alloc] initWithData: data];

        parser.delegate = rootObject;
        [parser setShouldProcessNamespaces: YES];
        [parser parse];

      // during the parse, records and subnodes are created using: 
      // [Record MR_createInContext: context]; 
      // where context is the same as in the rootObject above
       }
                  completion: ^(BOOL success, NSError *error) {
                      if (error)
                      {
                          // show error alert
                      }
                      else if (success)
                      {
                      }
                  }];

}

Why can't you make use of your completion handler as follows

completion: ^(BOOL success, NSError *error) {
                  if (error)
                  {
                      // show error alert
                  }
                  else if (success)
                  {
                       self.records = [NSMutableArray arrayWithArray: [Record MR_findAllSortedBy: @"id" ascending: ascending]];
                       [[NSOperationQueue mainQueue] addOperationBlock:^{
                            [self.tableView reloadData];
                       }];
                  }
              }];

and you won't need to implement dispatch_group_t.

Your primary problem is that you are dispatching within a dispatch block. So, your dispatch_group_notify block is being called prior to the completion of your import. If you want to still use a dispatch group to signal your import completion, you need to block the outer block, typically, I'll use a dispatch_semaphore_t to block like so:

dispatch_semaphore_t waitForSave = dispatch_semaphore_create(0);
dispatch_group_async(save_group, save_queue, ^{

    //do your import here
    dispatch_semaphore_signal(waitForSave);
});

dispatch_semaphore_wait(waitForSave, DISPATCH_TIME_FOREVER);
// trigger your UI update here.

The reason I suggest this approach is because you're using [MagicalRecord saveWithBlock:] which, if you take a look at the source, is dispatching that block onto another background queue. That queue is most likely not the same one your using (since MagicalRecord creates its own private saving queue). As such, you have to basically wait for your operation to complete and perform your UI updates when the save operations are actually complete.

This is what I ended up doing.

I put a counter in the success block of the MagicalRecord save block and increment it for every import. Once the counter is equal to the number of newRecords, I know the last one was imported, and I can update my UI.

Maybe not the most efficient and elegant way, but for now it works. Maybe after some more reading, I come up with a GCD solution.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM