0

I am reading an XML file that basically looks as follows:

<teams>
    <team id = "A1">
        <player>Tom</player>
        <player>Dick</player>
        <player>Harry</player>
    </team>
    <team id = "A2">
        <player>John</player>
        <player>Tom</player>
    </team>
</teams>

I use NSXMLParser and create Core Data entities in the didEndElement delegate method as follows:

if ([elementName isEqualToString:@"player"]) {
    if ([nodeContent length] != 0) {
        player = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:savedContext];            
        player.name = nodeContent;
    }
}

And do something similar for 'team' on didStartElement. 'nodeContent' is what I get from foundCharacters. So far so good. Everything works ok. However, in this example, I have a player (Tom) who plays in two teams. This is possible but I do not want two entities of Tom in my Core Data. So, I check whether a player with that name already exists:

    Player *player = nil;

    NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:@"Player"];
    request.predicate = [NSPredicate predicateWithFormat:@"name = %@", nodeContent];
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES];
    request.sortDescriptors = [NSArray arrayWithObject:sortDescriptor];

    NSError *error = nil;
    NSArray *players = [savedContext executeFetchRequest:request error:&error];

    if (!players || ([players count] > 1)) {
        NSLog(@"WTF?!");
    } else if ([players count] == 0) {
        player = [NSEntityDescription insertNewObjectForEntityForName:@"Player" inManagedObjectContext:savedContext];            
        player.name = nodeContent;
    } else {
        player = [players lastObject];
    }            
    [player addPlaysInTeamObject:team];

... and if that is the case, I just use that entity. If not, I create a new one as before.

Now, this all works when I run the parsing synchronously in the foreground, but I do not want to freeze my app while it is downloading stuff. So I put all this in a thread. It sometimes works ok, but occasionally I get the following error on the executeFetchRequest:

*** Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <__NSCFSet: 0x8177a90> was mutated while being enumerated.'
*** First throw call stack:
(0x12d3022 0x1784cd6 0x12d2bf1 0x41314 0xd5b9 0xceaa25 0x19118f6 0x191dab0 0xce901d 0xcf8d 0xc3a5 0x1f31330 0x1f32439 0x908b9b24 0x908bb6fe)
terminate called throwing an exception(lldb)

It happens sometimes when I don't use the (foreground) UI, but always when I do touch any UI element, even if that UI element does not use the data that I am loading in the background. I checked this forum and concluded that it might be caused by the parser filling up the database while at the same time the NSFetchRequest uses the database to search for a certain name. (Although I do not really understand that because all this happens in the same thread.)

Is there anyone who can tell me what I am doing wrong?

Thank you!

--GB

gbroekstg
  • 1,055
  • 1
  • 10
  • 19

1 Answers1

0

Apparently, you cannot access core data entities while another thread is still creating / updating the core data database.

In this case I ended up separating the XML parsing and the core data creation. So basically, I parse the entire XML stuff and store it in an NSDictionary. After that, I use the algorithm above to find or create the core data entities with that NSDictionary.

gbroekstg
  • 1,055
  • 1
  • 10
  • 19