Question

I have an RKEntitiyMapping object that maps an object that has a one-to-one relation to a child.

when calling the following code:

[sessionMapping addConnectionForRelationship:@"dailyGoalDetail" connectedBy:@{ @"dailyGoalDetailId": @"dailyGoalDetailId" }];

and debugging throught the addConnectionForRelationship method, I see that the connection being added to the collection of connections is nil. I double checked by finding the connection with Core Data this way:

NSEntityDescription *parentEntity = [NSEntityDescription entityForName:@"Session" inManagedObjectContext:[MGRModel sharedModel].managedObjectStore.persistentStoreManagedObjectContext]; NSRelationshipDescription *childRelationship = [parentEntity relationshipsByName][@"dailyGoalDetail"]; [sessionMapping addConnectionForRelationship:childRelationship connectedBy:@{@"dailyGoalDetailId":@"dailyGoalDetailId"}];

And logging to console my relationship shows it. But the addConnectionForRelationship method still adds a nil connection to its collection.

What's wrong with this?

Update

Here's the relevant code of my Session object:

+ (RKEntityMapping *)responseMapping
{
    RKEntityMapping* sessionMapping = [RKEntityMapping mappingForEntityForName:@"Session" inManagedObjectStore:[[MGRModel sharedModel] managedObjectStore]];
    [sessionMapping addAttributeMappingsFromDictionary:@{
                                                         @"ActivityId": @"activityType",
                                                         @"DailyGoalDetailId": @"dailyGoalDetailId",
                                                         @"DeviceGuid": @"deviceId",
                                                         @"DeviceSessionId": @"entityId",
                                                         @"Frequency": @"frequency",
                                                         @"Pace": @"pace",
                                                         @"Start": @"start",
                                                         @"Stop": @"stop",
                                                         @"TotalBeats": @"totalBeats",
                                                         @"TotalKcal": @"totalCalories",
                                                         }];
    sessionMapping.identificationAttributes = @[ @"entityId" ];

    // Add relationship mapping with Measurement as "sessionDetails"
    [sessionMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:@"SessionDetails"
                                                                                   toKeyPath:@"sessionDetails"
                                                                                 withMapping:[MGRMeasurement responseMapping]]];

    NSEntityDescription *parentEntity = [NSEntityDescription entityForName:@"Session" inManagedObjectContext:[MGRModel sharedModel].managedObjectStore.persistentStoreManagedObjectContext];
    NSRelationshipDescription *childRelationship = [parentEntity relationshipsByName][@"dailyGoalDetail"];
    [sessionMapping addConnectionForRelationship:childRelationship connectedBy:@{@"dailyGoalDetailId":@"dailyGoalDetailId"}];

//    [sessionMapping addConnectionForRelationship:@"dailyGoalDetail" connectedBy:@{ @"dailyGoalDetailId": @"dailyGoalDetailId" }];

    return sessionMapping;
}

And here's a po of the relationship:

(lldb) po childRelationship
(<NSRelationshipDescription: 0xc266570>), name dailyGoalDetail, isOptional 1, isTransient 0, entity Session, renamingIdentifier dailyGoalDetail, validation predicates (
), warnings (
), versionHashModifier (null)
 userInfo {
}, destination entity DailyGoalDetail, inverseRelationship sessions, minCount 0, maxCount 1, isOrdered 0, deleteRule 1

Some more information. The JSON is like this:

{
  "DailyGoalDetails": [
    {
      "DailyGoal": {
        "ActivityUnit": {
          "ActivityUnitId": 1,
          "Name": "BEATS"
        },
        "DailyGoalId": 1,
        "ActivityUnitId": 1,
        "Name": "Un inizio facile",
        "Beats": 25,
        "Level": 1
      },
      "DailyGoalDetailId": 6,
      "DailyGoalId": 1,
      "UserId": "7a2e5b7c-9a3a-4ad1-ad79-946bcf2bf2f3",
      "Hit": true,
      "Value": 9,
      "GoalDate": "2014-04-16T00:00:00",
      "AvgActivityTime": 12
    }
  ],
  "UserSessions": [
    {
      "UserSessionId": 0,
      "DeviceGuid": "962d26a01cdef0c8",
      "DeviceSessionId": "2a0bb7a0-041d-4df6-abcb-e50483858cba",
      "DailyGoalId": 1,
      "DailyGoalDetailId": 6,
      "ActivityId": 1,
      "Start": "2014-04-16T16:10:11",
      "Stop": "2014-04-16T16:15:53",
      "TotalBeats": 0,
      "TotalKcal": 1,
      "Pace": 0.00,
      "Frequency": 1,
      "SessionDetails": [
        {
          "Speed": 0.0,
          "Step": 4,
          "Distance": 0.0,
          "Beats": 0.0033395835198462009,
          "Lat": 45.70608193,
          "Lng": 9.70917967,
          "Altitude": 298.0,
          "Orientation": 0.0,
          "Start": "2014-04-16T16:10:10",
          "Stop": "2014-04-16T16:10:11"
        },
        {
          "Speed": 0.0,
          "Step": 2,
          "Distance": 0.0,
          "Beats": 0.0033395835198462009,
          "Lat": 45.70608193,
          "Lng": 9.70917967,
          "Altitude": 298.0,
          "Orientation": 0.0,
          "Start": "2014-04-16T16:10:11",
          "Stop": "2014-04-16T16:10:12"
        },
      ]
    }
  ]
}

And the relevant code that initialize and calls the mapper (this is done over an NSString):

        // Json decoding
        NSError *decodingError = nil;
        id parsedData = [RKMIMETypeSerialization objectFromData:decodedUncompressedData MIMEType:RKMIMETypeJSON error:&decodingError];
        if (parsedData == nil && error) {
            NSLog(@"Error decoding json received from synchronization");
        }
        NSMutableDictionary *mappingsDictionary = [[NSMutableDictionary alloc] init];
        // Add DailyGoalDetails->MGRDailyGoalDetail mapping
        [mappingsDictionary setObject:[MGRDailyGoalDetail responseMapping] forKey:@"DailyGoalDetails"];
        // Add UserSessions->MGRSession mapping
        [mappingsDictionary setObject:[MGRSession responseMapping] forKey:@"UserSessions"];
        RKMapperOperation *mapper = [[RKMapperOperation alloc] initWithRepresentation:parsedData mappingsDictionary:mappingsDictionary];
        RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:[MGRModel sharedModel].managedObjectStore.persistentStoreManagedObjectContext cache:[MGRModel sharedModel].managedObjectStore.managedObjectCache];
        dataSource.parentOperation = mapper;
        mapper.mappingOperationDataSource = dataSource;
        NSError *mappingError = nil;
        BOOL isMapped = [mapper execute:&mappingError];
        if (isMapped && !mappingError) {
            NSLog(@"jsonMappingResult = %@",[mapper mappingResult]);
            [[[[MGRModel sharedModel] managedObjectStore] persistentStoreManagedObjectContext] save:&mappingError];
        }
Was it helpful?

Solution

RKMapperOperation doesn't actually do much work. Internally it uses RKMappingOperation and that is what makes the connections. To make it work you have 2 options:

  1. Use 2 RKMapperOperation instances that are dependent on each other, with the session mapper running second
  2. Use RKObjectManager to oversee the whole process

If you use mapper operation directly then you will need 2 of them so that you can explicitly control the order. In theory you can alternatively add the connection onto both mappings, so it doesn't matter which is run first (so long as they are both backed by the same MOC).

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top