SimpleJ
SimpleJ

Reputation: 14768

Keep in-memory core-data objects from being deleted by unrelated code?

I'm working on an old incredibly poorly written Objective-C app. The feature I'm working on involves taking different record types from core-data and sending them to a server. And one record type (A) has to be sent before another one (B) can be sent.

EDIT: A refers to ActivitySession objects and B refers to ActivityRun objects.

Here's the problem: somewhere in the app, there's some kind of listener deleting all of the B records after the A records are sent. I've spent a while trying to figure out where this is happening, but I've had no luck. My solution was to then just load all of the B records into memory before sending the A records (thinking even if the B records are deleted from core data, they will still exist in memory). But I'm finding after the B records are deleted from core data, my array of records are also being cleared.

Here is the meat of the logic I'm talking about:

__block NSArray* activityRuns = [coreData fetchByEntitiyName:@"ActivityRun"];

NSLog(@"---> Sending %@ students", @([students count]));
return [self sendStudents:students withApiKey:apiKey]
.then(^{
  // This must happen before sending anything with activity references
  NSLog(@"---> Fetching activities");
  return [self fetchActivitiesWithApiKey:apiKey];
})
.then(^{
  NSArray* data = [coreData fetchByEntitiyName:@"ActivitySession"];
  NSLog(@"---> Sending %@ activity sessions", @([data count]));
  return [self sendActivitySessions:data withApiKey:apiKey].catch(failLogger(@"Failed to upload activity sessions", nil));
})
.then(^{
  NSLog(@"---> Sending %@ activity runs", @([activityRuns count]));
  return [self sendActivityRuns:activityRuns withApiKey:apiKey].catch(failLogger(@"Failed to upload activity runs", nil));
})

activityRuns is the in-memory data I'm trying to send (with [self sendActivityRuns:activityRuns withApiKey:apiKey]). Here is how fetchByEntitiyName is defined:

- (NSArray*) fetchByEntitiyName:(NSString*) entityName {
    NSFetchRequest* fetchRequest = [[NSFetchRequest alloc] initWithEntityName:entityName];
    NSError* error = nil;
    NSArray* results = [_mainQueueContext executeFetchRequest:fetchRequest error:&error];
    if (results == nil) {
            ELog(@"--- Failed to fetch %@: %@", entityName, error);
            return @[];
    }
    return results;
}

activityRuns isn't used/passed anywhere other than in the code snippet above where each activity run is sent using an HTTP request. I tried adding breakpoints to right after the array is created (the objects are valid and contain data) and right before they're sent (they're all nil).

How do I keep core data from shitting on my in-memory objects when something deletes records?

Upvotes: 0

Views: 183

Answers (2)

Mundi
Mundi

Reputation: 80265

Simple solution:

Why don't you prepare all the data you need to send to the server first, including format and serialisation? You can then store this in memory or elsewhere and send to your server in the right order.

Upvotes: 1

battlmonstr
battlmonstr

Reputation: 6280

There are a few ways for this to happen:

  • The obvious one are calls to NSManagedObjectContext.deleteObject, just look for deleteObject and put breakpoints there.
  • Another one is if A and B have a relation with cascade delete, and you delete the parent, then B is deleted. For example if a Student has a list of ActivityRun-s in a property like: student.activityRuns (array of child objects), or a reverse property - activityRun.student (single parent object), and in your CoreData model this property is set as cascade delete then if you delete a student, all activityRuns will be gone. If you use "on delete set null" instead of "on delete cascade", the children won't be deleted automatically, but you'll have to delete them manually when needed. Another way is to "detach" a child from the parent by setting activityRun.student = nil;, it should have the same effect (again have to delete manually when needed).
  • Another option is if you do raw SQL DELETE queries against this database

There's a way to see all the CoreData SQL commands that happen under the hood - see here.

Upvotes: 1

Related Questions