2

I will try to be as descriptive as possible with this issue...

Scenario

Let's say i have a NSManagedObject 'User'

@class Premise;

@interface User : NSManagedObject

@property (nonatomic, retain) NSNumber * identifier;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSSet *premises;
@end

@interface User (CoreDataGeneratedAccessors)

- (void)addPremisesObject:(Premise *)value;
- (void)removePremisesObject:(Premise *)value;
- (void)addPremises:(NSSet *)values;
- (void)removePremises:(NSSet *)values;

@end

And i also have a NSManagedObject 'Premise'

@class User;

@interface Premise : NSManagedObject

@property (nonatomic, retain) NSNumber * identifier;
@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) User *user;

@end

Based on it, i am creating a relationship route to map a JSON array of 'Premise' to the 'premises' attribute on the 'User' object.

Here's the route:

 let getPremisesRoute = RKRoute(relationshipName: "premises", 
                                objectClass: User.self, 
                                pathPattern: "user/:identifier/premises", 
                                method: .GET)

Here's the JSON response (/user/1/premises):

[
    {
        "id": 35,
        "name": "Icaraí"
    },
    {
        "id": 32,
        "name": "Remanso"
    }
]

Here's the response descriptor:

  let getPremisesResponseDescriptor = RKResponseDescriptor(mapping: premiseMapping, method: .GET, pathPattern: "user/:identifier/premises", keyPath: nil, statusCodes: RKStatusCodeIndexSetForClass(.Successful))

And here are the respective mappings of 'User' and 'Premise'

let userMapping = RKEntityMapping(forEntityForName: "User", inManagedObjectStore: moc)
userMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name"])      
userMapping.identificationAttributes = ["identifier"]
userMapping.addPropertyMapping(RKRelationshipMapping(fromKeyPath: nil, toKeyPath: "premises", withMapping: premiseMapping))


let premiseMapping = RKEntityMapping(forEntityForName: "Premise", inManagedObjectStore: moc)
premiseMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name"])
premiseMapping.identificationAttributes = ["identifier"]

Now to my problem

Apparently, Restkit is getting a little bit confused during the mapping process. Here's the database after the request:

User Table: enter image description here

Premise Table:

enter image description here

Note the the relationship is not being created between the entities.

Now, if I change the response descriptor's mapping from premise to user mapping, the database changes to this:

Users Table:

enter image description here

Premise Table:

enter image description here

I'm really confused on what's going on and I've tried a lot of solutions with no success.

Is the JSON response out of pattern or am I doing something wrong? The JSON response seems to be on a common pattern, with a nil key path.

tmagalhaes
  • 944
  • 9
  • 12

2 Answers2

1

You're approaching the mapping incorrectly, or at least your mappings are wrong for what you're doing. Consider that the response is a user, but only the premises for a user, instead of considering it as a simple list of premises as you are now. Then you map to a user and insert the premises. Something like:

RKResponseDescriptor(mapping: userMapping, method: .GET, pathPattern: "user/:identifier/premises", keyPath: nil, statusCodes: RKStatusCodeIndexSetForClass(.Successful))

And here are the respective mappings of 'User' and 'Premise'

let userMapping = RKEntityMapping(forEntityForName: "User", inManagedObjectStore: moc)
userMapping.addAttributeMappingsFromDictionary(["@metadata.routing.parameters.idEntities":"identifier"])      
userMapping.identificationAttributes = ["identifier"]
userMapping.addPropertyMapping(RKRelationshipMapping(fromKeyPath: nil, toKeyPath: "premises", withMapping: premiseMapping))


let premiseMapping = RKEntityMapping(forEntityForName: "Premise", inManagedObjectStore: moc)
premiseMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name"])
premiseMapping.identificationAttributes = ["identifier"]

You don't have a user name in the response so you can't map it, and the user id is actually in the request so you need to use metadata to extract it.

Wain
  • 118,658
  • 15
  • 128
  • 151
  • I get the metadata part but this solution comes with another problem. If i change the 'user' mapping like you are suggesting, the moment i request my user object from the server (like a login for instance), Restkit won't map the 'id' attribute passed as JSON to the 'identifier', it will instead look for metadata and won't find it, thus equalling 'id' to 'nil'. Do I have to create multiple mappings for the 'user' entity? – tmagalhaes Jun 23 '15 at 11:40
  • 1
    Using this approach you have multiple mappings, yes. There is no restriction to that. – Wain Jun 23 '15 at 13:06
1

Ok, I found a possible solution based on @Wain's solution using foreign keys.

I added a new property 'userID' to the 'Premise' entity and mapped it to the identifier on the URL using metadata

let premiseMapping = RKEntityMapping(forEntityForName: "Premise", inManagedObjectStore: moc)

 premiseMapping.addAttributeMappingsFromDictionary(["id":"identifier", "name":"name", "@metadata.routing.parameters.identifier":"userID"])

Then I added a relationship connection to the 'premiseMapping'

premiseMapping.addConnectionForRelationship("user", connectedBy: ["userID":"identifier"])

If anyone has a more elegant solution please share with us.

tmagalhaes
  • 944
  • 9
  • 12
  • 1
    This is generally the other option (there are 2 ends you can configure the relationship from and these are the 2 techniques to do so...) – Wain Jun 23 '15 at 13:07