Mapping relationships from JSON array with Restkit

558 views Asked by At

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.

2

There are 2 answers

2
Wain On BEST ANSWER

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.

1
tmagalhaes On

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.