RESTKit: Objects are losing relationships - ios

JSON data received:
{
"meeting": [{
"meetingId": 506,
"ownerId": "John.Doe",
"startDate": "2014-07-27T13:15:07.000Z",
"activities": [{
"activityType": "Active Activity",
"activityId": 729,
"locationAddress": "1188 El Camino Real, San Bruno, CA, United States",
"startTime": "2014-07-28T04:45:00.000Z",
"customData": {
"title": "Active Activity"
},
"modified": "2014-07-23T13:26:41.000Z"
}],
"senderId": "Johnny.Appleseed",
"status": 8
}
SQLite File:
I have the following entities:
meeting
activity
customData
relationships:
meeting has to-many relationship with activity called activities
activity has to-many relationship with customData
Of course, there is an inverse relationship for each relationship.
customData entity only has one attribute called title
Here is my customData.h
#class Activity;
#interface customData : NSManagedObject
#property (nonatomic, retain) NSString * title;
#property (nonatomic, retain) Activity *activity;
#end
RESKit Mapping:
+(RKEntityMapping *)customDataMapping:(RKEntityMapping *)customDataMapping;
{
[customDataMapping addAttributeMappingsFromDictionary:#{#"title":#"title"}];
customDataMapping.identificationAttributes = #[#"title"];
return customDataMapping;
}
ObjectManager:
meetingMapping = [RESTMappingProvider meetingPutMapping:meetingMapping];
activityMapping = [RESTMappingProvider activityPutMapping:activityMapping];
customDataMapping = [RESTMappingProvider customDataMapping:customDataMapping];
[activityMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:kCustomDataRelationship
toKeyPath:kCustomDataRelationship
withMapping:customDataMapping]];
[meetingMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:kActivitiesRelationship
toKeyPath:kActivitiesRelationship
withMapping:activityMapping]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[meetingMapping inverseMapping] objectClass:[Meeting class] rootKeyPath:nil method:RKRequestMethodAny];
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:meetingMapping
method:RKRequestMethodAny
pathPattern:kMeetupKeyPath
keyPath:nil
statusCodes:statusCodeSet];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager addRequestDescriptor:requestDescriptor];
[objectManager addResponseDescriptor:responseDescriptor];
[objectManager.HTTPClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[objectManager putObject:anInvite path:kMeetupKeyPath parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog (#"******* OUTBOX OBJECT PUT **********");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
}
}
}
});
Problem:
For some reason, I'm losing relationships between an activity and customData object. As you can see, the internal ID (ZACTIVITY) for CoreData does not always assign Ids, thus, I lose objects because relationships are no longer there. I think its during the PUT call that objects are not assigned the ids.
I read on RESKit forums about using #parent and #metadata, but I'm not sure how to use it, or if that's even a right approach. Please advise.

I'm guessing that the titles of your custom data are not always unique, so, because you use customDataMapping.identificationAttributes = #[#"title"]; and a 1:many relationship some instances of custom data will be disconnected from their old relationship and connected to a new one.
Probably you should remove the identificationAttributes and add a fetch request block which purges out any orphan custom data objects (ones where the relationship is nil) after the data is collected.

Related

Mapping top level items and embedded arrays with RestKit

Given the following JSON structure:
{
"foo": {
"anno": "blah",
"domini": null,
"locations": [
{
"data": {
"lat": null,
"lon": null
},
"data": {
"lat": null,
"lon": null
}
}
]
}
}
How do I set up RestKit mappings for this scenario? I though I had it, but I'm unable to map the top-level foo items anno, and domini. I can successfully map locations on its own, but not in coordination with foo.
I've done this successfully in the past, but something is escaping me now.
Foo.h
#interface Foo : NSObject
#property (nonatomic, strong) NSString *anno;
#property (nonatomic, strong) NSString *domini;
#end
Location.h
#interface LocationData : NSObject
#property NSString *lat;
#property NSString *lon;
#end
Controller.m
RKObjectMapping *fooMapping = [RKObjectMapping mappingForClass:[Foo class]];
[fooMapping addAttributeMappingsFromArray:#[#"anno", #"domini"]];
RKObjectMapping *locationMapping = [RKObjectMapping mappingForClass:[Location class]];
[locationMapping addAttributeMappingsFromArray:#[#"lat",#"lon"]];
[fooMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"location" toKeyPath:#"location" withMapping: locationMapping]];
RKResponseDescriptor *fooReponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dataMapping method:RKRequestMethodGET pathPattern:#"foo" keyPath:#"foo" statusCodes:[NSIndexSet indexSetWithIndex:200]];
RKResponseDescriptor *locationResponseDescriptor =
[RKResponseDescriptor responseDescriptorWithMapping:locationdMapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"foo.location"
statusCodes:[NSIndexSet indexSetWithIndex:200]];
I think that's all of the important stuff. Hopefully in my zeal to pare down how much text I was posting I didn't leave anything important out.
EDIT 2015-03-29
- (void)loadChildren {
NSDictionary *queryParams = #{#"sort" : #"new"};
[[RKObjectManager sharedManager] getObjectsAtPath:redditPath
parameters:queryParams
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
_children = mappingResult.array;
[self.tableView reloadData];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"You mean YOU'RE the lunatic who's responsible for almost destroying my ship? : %#", error);
}];
}
redditpath is set earlier using...
redditPath = [NSString stringWithFormat:#"/r/%#/new.json", subRedditToLoad];
Where subRedditToLoad is, in this case, aww.
In your XCDataModel Take an Entity with named Foo.
Set Property of anno and vomini.
Also Create another Entity Location.
Set a property named data with type Transformable.
Add Relationship at Foo for Location entity put it many relationship.
Call mapping for Foo Like:
[mapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"foo" toKeyPath:#"Foo" withMapping:[Location objectMappingForLocation:Enum]]];

RestKit using RKConnectionDescription to sideload data [duplicate]

I'm totally new to RestKit and am struggling somewhat.
JSON:
{
"teams": [
{
"id": 1,
"name": "Team A"
},
{
"id": 2,
"name": "Team B"
}
],
"users": [
{
"id": 1,
"name": "cameron",
"teamId": 1
},
{
"id": 2,
"name": "paul",
"teamId": 2
}
]
}
CoreData:
#interface Team : NSManagedObject
#property (nonatomic, retain) NSNumber * teamId;
#property (nonatomic, retain) NSString * name;
#end
#interface User : NSManagedObject
#property (nonatomic, retain) NSNumber * userId;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) Team * team;
#end
My application logic looks like this:
// team mapping
RKEntityMapping *teamMapping = [RKEntityMapping mappingForEntityForName:#"Team" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
teamMapping.identificationAttributes = #[#"teamId"];
[teamMapping addAttributeMappingsFromDictionary:#{
#"id": #"teamId",
#"name": #"name"
}];
// Register our mappings with the provider
RKResponseDescriptor *teamResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:teamMapping
pathPattern:nil
keyPath:#"teams"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager addResponseDescriptor:teamResponseDescriptor];
// user mapping
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
userMapping.identificationAttributes = #[#"userId"];
[userMapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"name": #"name"
}];
// Register our mappings with the provider
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping
pathPattern:nil
keyPath:#"users"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager addResponseDescriptor:userResponseDescriptor];
I can't for the life of me work out how to get RestKit to populate the team Property of the user objects.
I've look at so many posts but nothing I try works, is this not a usual use case?
Does anyone know how to do this, help would be very appreciated!
Thanks.
You need to add a transient attribute to your User entity which holds the associated teamId. This needs to be added to your userMapping.
Then, you need to add a relationship definition to your userMapping:
[userMapping addConnectionForRelationship:#"team" connectedBy:#{ #"teamId": #"teamId" }];
This gives RestKit the information it needs to make the connection and instructs it to make the connection as part of the mapping operation.

RestKit additional data in response

I am using RestKit for mapping data from my api to CoreData entities and i wonder how can i get additional data from response. For example my api return structure like:
{
"items": [
{
"id": 1,
"title": "Title"
},
{
"id": 2,
"title": "Title 2"
}
],
"someParameter": "someValue"
}
i already have right mappings for shared object manager so i just send request:
[[RKObjectManager sharedManager] getObjectsAtPath:#"_api/items"
parameters:parameters
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//handle success
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
//handle error
}];
How can i get someParameter value in success block? Is this possible?
You will need to tweak your mapping slightly. If you changed it as follows you should be able to get RESTkit to parse the 'someParameter' attribute for you. You need to have two classes (Parent and Child).
The Parent class has 2 attributes (someParameter and an array of Child objects). The addRelationshipMappingWithSourceKeyPath is what ties the Parent and Child object mappings together.
Code:
RKObjectMapping *parentMapping = [RKObjectMapping mappingForClass:[Parent class]];
[beaconActionMapping addAttributeMappingsFromDictionary:#{
#"someParameter" : #"someParameter"
}];
RKObjectMapping *childMapping = [RKObjectMapping mappingForClass:[Child class]];
[beaconMapping addAttributeMappingsFromDictionary:#{
#"id" : #"childId",
#"title" : #"title"
}];
[parentMapping addRelationshipMappingWithSourceKeyPath:#"items" mapping:childMapping];
Class hierarchy:
#interface Parent : NSObject
#property(nonatomic,strong) NSString *someParameter;
#property(nonatomic,strong) NSArray *items; // Array of Child objects
#end
#interface Child : NSObject
#property(nonatomic,strong) NSString *childId;
#property(nonatomic,strong) NSString *title
#end
You can add an additional response descriptor with a key path of someParameter. You can use that with a nil key path mapping to extract the string value into an object of your choice (usually a custom class).

POSTing large objects as JSON using RestKit

Is there a way to POST large NSObject-derived object structures without having to manually specify every property and property collection to RestKit?
Here is a simple example, with a single class DABUser But imagine it contained properties which were also objects, collections, and those had more of the same to represent some larger object tree.
The class to POST:
#interface DABUser : NSObject
#property (nonatomic) int age;
#property (copy, nonatomic) NSString *name;
#end
POST a DABUser object:
RKObjectMapping *userMapping = [RKObjectMapping requestMapping];
[userMapping addAttributeMappingsFromArray:#[ #"age", #"name"]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:userMapping objectClass:[DABUser class] rootKeyPath:nil method:RKRequestMethodPOST];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:#"http://localhost:3000"]];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager addRequestDescriptor:requestDescriptor];
DABUser *user = [[DABUser alloc] init];
user.age = 20;
user.name = #"Charlie Brown";
[objectManager postObject:user path:#"users/123" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success!");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failed!");
}];
The JSON generated from the above code being and sent via the request body is:
{ "age":20,"name":"Charlie Brown" }
When I have a large object tree, defining the property mappings can get tiresome (and error-prone), with many lines of similar code to this example's:
RKObjectMapping *userMapping = [RKObjectMapping requestMapping];
[userMapping addAttributeMappingsFromArray:#[ #"age", #"name"]];
Is there a way that I could just get RestKit to generate the JSON from the objects, without all this setup?
"When I have a large object tree, defining the property mappings can get tiresome (and error-prone), with many lines of similar code to this example's:"
I personally think this is the easiest way and a good approach.I have done object mapping to large objects with so many object mapping and multiple object linking and found this is the easiest way to deal with it correctly

Foreign key relationship mapping with RestKit

I'm totally new to RestKit and am struggling somewhat.
JSON:
{
"teams": [
{
"id": 1,
"name": "Team A"
},
{
"id": 2,
"name": "Team B"
}
],
"users": [
{
"id": 1,
"name": "cameron",
"teamId": 1
},
{
"id": 2,
"name": "paul",
"teamId": 2
}
]
}
CoreData:
#interface Team : NSManagedObject
#property (nonatomic, retain) NSNumber * teamId;
#property (nonatomic, retain) NSString * name;
#end
#interface User : NSManagedObject
#property (nonatomic, retain) NSNumber * userId;
#property (nonatomic, retain) NSString * name;
#property (nonatomic, retain) Team * team;
#end
My application logic looks like this:
// team mapping
RKEntityMapping *teamMapping = [RKEntityMapping mappingForEntityForName:#"Team" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
teamMapping.identificationAttributes = #[#"teamId"];
[teamMapping addAttributeMappingsFromDictionary:#{
#"id": #"teamId",
#"name": #"name"
}];
// Register our mappings with the provider
RKResponseDescriptor *teamResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:teamMapping
pathPattern:nil
keyPath:#"teams"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager addResponseDescriptor:teamResponseDescriptor];
// user mapping
RKEntityMapping *userMapping = [RKEntityMapping mappingForEntityForName:#"User" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
userMapping.identificationAttributes = #[#"userId"];
[userMapping addAttributeMappingsFromDictionary:#{
#"id": #"userId",
#"name": #"name"
}];
// Register our mappings with the provider
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:userMapping
pathPattern:nil
keyPath:#"users"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[self.objectManager addResponseDescriptor:userResponseDescriptor];
I can't for the life of me work out how to get RestKit to populate the team Property of the user objects.
I've look at so many posts but nothing I try works, is this not a usual use case?
Does anyone know how to do this, help would be very appreciated!
Thanks.
You need to add a transient attribute to your User entity which holds the associated teamId. This needs to be added to your userMapping.
Then, you need to add a relationship definition to your userMapping:
[userMapping addConnectionForRelationship:#"team" connectedBy:#{ #"teamId": #"teamId" }];
This gives RestKit the information it needs to make the connection and instructs it to make the connection as part of the mapping operation.

Resources