I have a weird issue with restkit mapping to CoreData. All works great in simulator but not in real device. After successful request to server, mapping of results is successful in both cases (i can access every value in RKMappingResult *mappingResult ). New values are stored in simulator but they arent stored in device. I did a lot of googling but without solution.
I do restkit requst this way:
RKObjectMapping *requestMapping = [[hcUpdateRequest defineUpdateRequestMapping] inverseMapping];
[self.objectManager addRequestDescriptor: [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[hcUpdateRequest class] rootKeyPath:nil method:RKRequestMethodPOST]];
RKEntityMapping *storyMapping = [hcUpdateResponse storyMapping:self.objectManager.managedObjectStore];
{..moreMapping..}
[storyMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"pieces" toKeyPath:#"pieces" withMapping:pieceMapping]];
{..moreMapping..}
[self.objectManager setRequestSerializationMIMEType: RKMIMETypeFormURLEncoded];
[self.objectManager postObject:self.dataUpdateRequst
path:#"storyAll"
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {…}
+ (RKEntityMapping*) storyMapping:(RKManagedObjectStore*)managedObjectStore {
RKEntityMapping* storyMapping = [RKEntityMapping mappingForEntityForName:#"Story" inManagedObjectStore:managedObjectStore];
[storyMapping addAttributeMappingsFromDictionary:#{
#"id":#"idRemoteDatabase",
#"name":#"name",
#"position_x": #"positionX",
#"position_y": #"positionY",
#"state": #"state",
#"gender": #"gender"
}];
storyMapping.identificationAttributes = #[#"idRemoteDatabase"];
return storyMapping;
}
Related
I'm trying to post new managed object with image to the web database as below and everything works fine for me except the little weird bug. When the managed object is uploaded successfully, 2 rows are added instead of one to the UITableView. When I'm refreshing the UITableView the one of the previously added UITableViewCell is populating with uploaded image. When I rebuild the app everything works fine. Previously added managed object is displayed correctly without duplication. Does anyone can take a look at my code and tell me what is the reason for that bug and how can I fix it? Let me know if you need more code.
-(void)postRequest{
NSEntityDescription *watchEntityDesc = [NSEntityDescription entityForName:#"Watches" inManagedObjectContext:[[RKObjectManager sharedManager]managedObjectStore].mainQueueManagedObjectContext];
Watches *watch = [[Watches alloc]initWithEntity:watchEntityDesc insertIntoManagedObjectContext:[[RKObjectManager sharedManager]managedObjectStore].mainQueueManagedObjectContext];
watch.phonewatchno = #"124512";
watch.latitude = [NSNumber numberWithDouble:-33.856870];
watch.longitude = [NSNumber numberWithDouble:151.215279];
NSEntityDescription *wearersEntityDesc = [NSEntityDescription entityForName:#"Wearers" inManagedObjectContext:[[RKObjectManager sharedManager]managedObjectStore].mainQueueManagedObjectContext];
Wearers *wearer = [[Wearers alloc]initWithEntity:wearersEntityDesc insertIntoManagedObjectContext:[[RKObjectManager sharedManager]managedObjectStore].mainQueueManagedObjectContext];
wearer.name =_nameTextField.text,
wearer.watches =[NSSet setWithObject:watch];
RKEntityMapping *watchesMapping = [RKEntityMapping mappingForEntityForName:#"Watches" inManagedObjectStore:[[EFDateModel sharedDataModel]objectStore]];
[watchesMapping addAttributeMappingsFromDictionary:#{
#"id": #"watch_id",
#"latitude":#"latitude",
#"longitude":#"longitude",
#"phonewatchno":#"phonewatchno",
}
];
[watchesMapping addConnectionForRelationship:#"wearer" connectedBy:#{
#"wearer_id":#"wearer_id"
}];
[watchesMapping setIdentificationAttributes:#[#"watch_id"]];
RKResponseDescriptor *responseDescr = [RKResponseDescriptor responseDescriptorWithMapping:watchesMapping method:RKRequestMethodPOST pathPattern:#"/watches.json" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager]addResponseDescriptor:responseDescr];
RKEntityMapping *wearersMapping = [RKEntityMapping mappingForEntityForName:#"Wearers" inManagedObjectStore:[[EFDateModel sharedDataModel] objectStore]];
[wearersMapping addAttributeMappingsFromDictionary:#{
#"id":#"wearer_id",
#"name":#"name",
}
];
wearersMapping.identificationAttributes = #[#"wearer_id"];
[wearersMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"watch" toKeyPath:#"watches" withMapping:watchesMapping]];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:wearersMapping method:RKRequestMethodPOST pathPattern:#"/wearers.json" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[wearersMapping inverseMapping] objectClass:[Wearers class] rootKeyPath:nil method:RKRequestMethodPOST ];
[[RKObjectManager sharedManager]addResponseDescriptor:responseDescriptor];
[[RKObjectManager sharedManager]addRequestDescriptor:requestDescriptor];
[[RKObjectManager sharedManager]setRequestSerializationMIMEType:RKMIMETypeJSON];
[[RKObjectManager sharedManager]setAcceptHeaderWithMIMEType:#"application/json"];
UIImage *image =[UIImage imageWithCGImage:_wearerImage.CGImage scale:0.4 orientation:UIImageOrientationUp];
NSMutableURLRequest *request = [[RKObjectManager sharedManager] multipartFormRequestWithObject:wearer method:RKRequestMethodPOST path:#"/wearers.json" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:UIImagePNGRepresentation(image)
name:#"wearer_photo"
fileName:#"photo.png"
mimeType:#"image/png"];
}];
NSManagedObjectContext *moc =[[[RKObjectManager sharedManager]managedObjectStore]mainQueueManagedObjectContext];
RKManagedObjectRequestOperation *managedObjectOperation = [[RKObjectManager sharedManager]managedObjectRequestOperationWithRequest:request managedObjectContext:moc success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Mapping result = %#",mappingResult.array);
[[RKObjectManager sharedManager]removeResponseDescriptor:responseDescriptor];
[[RKObjectManager sharedManager]removeRequestDescriptor:requestDescriptor];
[[NSNotificationCenter defaultCenter]
postNotificationName:#"ReloadTable"
object:self];
[self dismissViewControllerAnimated:YES completion:nil];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Error : \n %#",error.description);
}];
[[RKObjectManager sharedManager] enqueueObjectRequestOperation:managedObjectOperation];
}
Cheers
Every time you are calling this method you are calling alloc]initWithEntity:... insertIntoManagedObjectContext: twice and creating two new entity instances. You should probably be passing an entity instance to this method that it uses and not creating any new instances.
Also, it is inefficient to keep creating new descriptors and adding and removing them. And if you try to make 2 requests at the same time you will get an error. Configure the object manager once before you first use it and then just use it repeatedly without reconfiguring.
I'm sending out a request to get an object with an nested array of data. However, RestKit is only saving the last item in the nested array of data.
For example, here's the JSON response the server is delivering
{
"nutrition_day": {
"id": "5342e9e13138610012420100",
"start_on": "2014-04-07",
"meal_goals": [
{
"id": "5342e9e13138610012410100",
"name": "Breakfast",
"food_entries": [
{
"id": "535429513663320008210000",
"consumed_on": "2014-04-20",
"food_id": 33801,
"serving_id": 29457,
}
]
},
{
"id": "5342e9e13138610012430100",
"name": "Morning Snack",
"food_entries": []
}
]
}
}
However, using the code below, when I attempt to access nutrition_data.meal_goals, I am left with only the last item in the array "Morning Snack" and am not getting "Breakfast"
Mapping:
+ (void)mapping
{
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
RKEntityMapping *responseMapping = [RKEntityMapping mappingForEntityForName:#"NutritionDay" inManagedObjectStore:[[RKObjectManager sharedManager] managedObjectStore]];
[requestMapping addAttributeMappingsFromArray:#[#"date"]];
[responseMapping addAttributeMappingsFromArray:#[#"start_on", #"protein_target"]];
[responseMapping setIdentificationAttributes:#[#"start_on"]];
[responseMapping addRelationshipMappingWithSourceKeyPath:#"meal_goals" mapping:[MealGoal responseMapping]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping
objectClass:[NutritionDay class]
rootKeyPath:#"nutrition_day"
method:RKRequestMethodGET];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodAny
pathPattern:#"nutrition/days/:date"
keyPath:#"nutrition_day"
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addRequestDescriptor:requestDescriptor];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
}
Request:
[[RKObjectManager sharedManager] getObjectsAtPath:#"nutrition/days/2014-12-12" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NutritionDay *nut = ((NutritionDay *)mappingResult.firstObject);
NSLog(#"%lu", (long)nut.meal_goals.count); // this always returns 1 when it should be 2
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"fail");
}];
Is there something I need to do so that RestKit recognizes the entire array of the relationship and saves it?
I would suggest that you should include the id in your mapping and use that as the identification attribute.
meal_goals should be a to-many relationship (with inverse).
Your log should be:
NSLog(#"%lu", (long)nut.meal_goals.count);
Other than those things, the code you show looks correct. Log the entire mapping result to verify the contents.
i just update a content using restkit patchobject..when first time the method call invoked it leads success.but during second time call the same method ,the app crashes with NSInternal inconsistecy error.Cannot add adddescriptor for same class.thanks. below link also have same problem,but i dont know how to solve.
Restkit + Objective-c - Multiple calls to same web service
here is my method code
-(void)setContact:(int)_orgID :(int)_personID :(Person *)p1
{
AddressScreenViewController *addressView= [[AddressScreenViewController alloc]init];
addressView.mobileno = p1.mobile_phone;
addressView.workno = p1.work_phone;
addressView.homeno = p1.home_phone;
addressView.address1=p1.address1;
addressView.address2=p1.address2;
addressView.city=p1.city;
addressView.zip=p1.zip;
[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;
LoginAppDelegate * appDelegate = [[UIApplication sharedApplication] delegate];
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager setRequestSerializationMIMEType:RKMIMETypeJSON];
RKObjectMapping *personRequestMapping = [RKObjectMapping requestMapping];
[personRequestMapping addAttributeMappingsFromDictionary:#{ #"mobileno" : #"phone_numbers.mobile_number", #"workno" : # "phone_numbers.work_number" , #"homeno" :#"phone_numbers.home_number",#"address1":#"mailing_address.address1",#"address2":#"mailing_address.address2",#"city":#"mailing_address.city",#"zip":#"mailing_address.zip"}];
RKLogConfigureByName("RestKit", RKLogLevelWarning);
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKRequestDescriptor *requestDescriptor =[RKRequestDescriptor requestDescriptorWithMapping:personRequestMapping objectClass:[AddressScreenViewController class] rootKeyPath:#"person"];
[objectManager addRequestDescriptor:requestDescriptor];
NSString * orgPath = [NSString stringWithFormat:myurl];
[objectManager patchObject:addressView path:orgPath parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result)
{
NSLog(#"result: %#", result);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"failuer function");
}];
}
The issue you are facing is because you are adding same request descriptor multiple times. You should set all request and response descriptor only once for example, in app delegate.
+ (void)setupDescriptors {
RKObjectManager *objectManager = [[AppDelegate appDelegate] objectManager];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
// Add Request Descriptors
//
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[Human rkEntityMappingForResponse:YES] method:RKRequestMethodAny pathPattern:nil keyPath:#"human" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKRequestDescriptor *userRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[User rkObjectMappingForRequest:YES] objectClass:[User class] rootKeyPath:#"user" method:RKRequestMethodAny];
RKRequestDescriptor *signupUserRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[User rkObjectMappingForSignupRequest:YES] objectClass:[User class] rootKeyPath:#"user" method:RKRequestMethodAny];
[objectManager addRequestDescriptorsFromArray:#[signupUserRequestDescriptor,userRequestDescriptor]];
RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[User rkEntityMappingForResponse:YES] method:RKRequestMethodAny pathPattern:nil keyPath:#"user" statusCodes:statusCodes];
[objectManager addResponseDescriptorsFromArray:#[userResponseDescriptor,responseDescriptor]];
}
But in some scenarios, you still want to add multiple request descriptor then this is done by dynamic mapping as answered by Author of REST Kit. Please see following link.
Adding two request descriptors for a given class in Restkit 0.2
I hope this helps.
You can't add a descriptor for the same key twice. You haven't added any code, but I know that you're adding all of your mappings and descriptors in the method where you're calling 'patch'. Don't...
Separate your configuration from your usage. Put all of your mapping and descriptor setup code into one method and call it once before you do anything else. Then, when you want to 'patch', call a different method which just does that and doesn't include any additional mappings.
I'm struggling a bit with RestKit and CoreData, especially since there are so little examples and documentation out there for RestKit 0.20.
I have an (managed) object Song with a many-to-one relationship with Album. The following code can post JSON, but not in the flattened format, which the server excepts.
// Defined elsewhere
Album *theAlbum;
RKObjectManager *objMan = [self objectManager];
// Response Mapping
RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[Song class]];
[responseMapping addAttributeMappingsFromDictionary:#{ #"song": #"songID" }];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
pathPattern:#"/api/song"
keyPath:nil
statusCodes:statusCodes];
// Request Mapping
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
RKEntityMapping *albumRelationshipMapping = [RKEntityMapping mappingForEntityForName:#"Album" inManagedObjectStore:[objMan managedObjectStore]];
[albumRelationshipMapping addAttributeMappingsFromDictionary:#{#"id": #"albumID", }];
[requestMapping addAttributeMappingsFromDictionary:#{ #"title": #"title", #"length": #"length" }];
[requestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"album"
toKeyPath:#"album"
withMapping:albumRelationshipMapping]];
requestMapping = [requestMapping inverseMapping];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[Song class] rootKeyPath:nil];
[objMan addRequestDescriptor:requestDescriptor];
[objMan addResponseDescriptor:responseDescriptor];
// Create a new temporary song object
Song *song = [NSEntityDescription insertNewObjectForEntityForName:#"Song"
inManagedObjectContext:[[objMan managedObjectStore] mainQueueManagedObjectContext]];
song.title = #"Some Title";
song.length = 123;
song.album = theAlbum;
// Post operation
objMan.requestSerializationMIMEType = RKMIMETypeJSON;
RKManagedObjectRequestOperation *operation = [objMan appropriateObjectRequestOperationWithObject:song
method:RKRequestMethodPOST
path:#"/api/song"
parameters:nil];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// Success
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Failure
}];
[objMan enqueueObjectRequestOperation:operation];
This code will post a JSON body like this:
{"title":"Some Title","length":"123","album":{"id":"6e32ae476815f365"}}
However, the server expects a JSON body like this:
{"title":"Some Title","length":"123","album":"6e32ae476815f365"}
I.e. the relationship album should be a flattened foreign key instead of a nested object. But when I try to alter the albumRelationshipMapping like this:
[albumRelationshipMapping setIdentificationAttributes:#[ #"albumID" ]];
[albumRelationshipMapping addAttributeMappingToKeyOfRepresentationFromAttribute:#"albumID"];
it throws an exception.
(NSInvalidArgumentException', reason: '*** -[NSProxy doesNotRecognizeSelector:allKeys] called!')
Anybody knows what I'm doing wrong here? Or is there example code out there, which can steer me in the right direction?
Sorry if this question was already answered somewhere else. I searched all of stackoverflow and the google groups, but couldn't find a specific solution for my case (RestKit 0.20, CoreData, relationship with just a FK).
Thanks, Dirk
In this case, I think you can simply use the dot notation to get albumID directly (no need to use RKRelationshipMapping)
Try updating your code this way :
// Request Mapping
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
RKEntityMapping *albumRelationshipMapping = [RKEntityMapping mappingForEntityForName:#"Album" inManagedObjectStore:[objMan managedObjectStore]];
//[albumRelationshipMapping addAttributeMappingsFromDictionary:#{#"id": #"albumID", }];
//[requestMapping addAttributeMappingsFromDictionary:#{ #"title": #"title", #"length": #"length" }];
[requestMapping addAttributeMappingsFromDictionary:#{ #"title": #"title", #"length": #"length", #"album" : #"album.albumID" }];
//[requestMapping addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"album"
// toKeyPath:#"album"
// withMapping:albumRelationshipMapping]];
Heres my routing setup:
[manager.router.routeSet addRoute:[RKRoute routeWithClass:[UserProfile class] pathPattern:#"/api/v1/users" method:RKRequestMethodPOST]];
[manager.router.routeSet addRoute:[RKRoute routeWithClass:[UserProfileAndStatistics class] pathPattern:#"/api/v1/profile/:userId" method:RKRequestMethodGET]];
[manager.router.routeSet addRoute:[RKRoute routeWithClass:[PrivateUserProfile class] pathPattern:#"/api/v1/privateprofile/" method:RKRequestMethodGET]];
[manager.router.routeSet addRoute:[RKRoute routeWithClass:[PrivateUserProfile class] pathPattern:#"/api/v1/privateprofile/" method:RKRequestMethodPOST]];
And here is how I register the mappings:
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:nil keyPath:nil statusCodes:nil];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
//the serialisation step
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[mapping inverseMapping] objectClass:[mapping objectClass] rootKeyPath:nil];
[[RKObjectManager sharedManager] addRequestDescriptor:requestDescriptor];
Finally I call:
[[RKObjectManager sharedManager] postObject:userProfile path:nil parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *result) { ... }
Which posts correctly, I get a HTTP status code 200, and the data goes to the server. This means that the serialisation step is working. When the response comes back I get an error:
Adding mapping error: Expected an object mapping for class of type 'PrivateUserProfile', provider returned one for 'UserProfileAndStatistics'
Now the mappings for PrivateUserProfile and UserProfileAndStatistics are very similar
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[PrivateUserProfile class]];
[mapping addAttributeMappingsFromDictionary:#{
#"UserIdentifier" : #"userId",
#"UserName" : #"userName",
#"Name" : #"name",
#"Email" : #"emailAddress",
#"Bio" : #"bio",
#"Website" : #"webSite",
#"Avatar" : #"avatar"
}];
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[UserProfileAndStatistics class]];
[mapping addAttributeMappingsFromDictionary:#{
#"UserIdentifier" : #"userId",
#"UserName" : #"userName",
#"Name" : #"name",
#"Bio" : #"bio",
#"Website" : #"webSite",
#"Avatar" : #"avatar",
#"Posts" : #"posts",
#"Followers" : #"followers",
#"Following" : #"following"
}];
But why does RestKit choose one over the other? How do I debug this successfully? Is there a relationship between RKRoute and RKObjectMapping. The mapping for PrivateUserProfile is being used for the serialisation step, so why isn't it being used for the counterpart deserialisation, and how do I make it get used?
You need two response descriptors here - one with path /api/v1/privateprofile/ for class PrivateUserProfile and the other with path /api/v1/profile/:userId for class UserProfileAndStatistics:
[manager addResponseDescriptorsFromArray:#[
[RKResponseDescriptor responseDescriptorWithMapping:privateUserProfileMapping
pathPattern:#"/api/v1/privateprofile/"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)],
[RKResponseDescriptor responseDescriptorWithMapping:userProfileAndStatisticsMapping
pathPattern:#"/api/v1/profile/:userId"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]
]];