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.
Related
I am having NSString parameters in my object getting set to NSNull when null is returned in the JSON. I would like the the NSString to be set to nil. Any Ideas?
I have tried setting [mapping setAssignsDefaultValueForMissingAttributes:NO]; but that does not seem to work even when I implement changes from Fix 1714. I'm really just spinning my wheels at this point.
Here is everything I have for making this call so far.
Returned JSON
{
val1 = "something";
val2 = "<null>";
}
Class cMyClass
#interface cMyClass : NSObject {
NSString *val1;
NSString *val2;
}
RKObjectMapping
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[cMyClass class]];
[mapping addAttributeMappingsFromDictionary:#{#"val1":#"val1", #"val2":#"val2"}];
RKResponseDescriptor
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodAny pathPattern:#"GetMyClass" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
RKObjectManager
NSURL *baseURL = [NSURL URLWithString:#"http://www.domain.com/MyAPI.svc/rest"];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
[objectManager addResponseDescriptor:responseDescriptor];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[objectManager.HTTPClient setDefaultHeader:#"Accept" value:RKMIMETypeJSON];
[objectManager.HTTPClient setParameterEncoding:AFJSONParameterEncoding];
API Call
NSDictionary *params = ...
objectManager getObjectsAtPath:#"GetMyClass" parameters:params success:(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
completionBlock:(mappingResult.array[0]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
failureBlock(error);
}];
I'm grasping at straws since I am just starting out with Restkit myself...but, do you perhaps think there might be an issue with the mappings?
It looks like the values in your cMyClass are ivars instead of properties. If I am not mistaken, unless otherwise stated, those are "protected" so you can't really access those if you are calling an instance of the class. Maybe move them to properties and see if it works from there!
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;
}
RestKitObjectMapping Array off null objects
I want to map CapitalImage object in Capital images object property.
//------------------------ The mapping I try to
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class]
forMIMEType:#"text/html"];
RKObjectMapping *CapitalImageMap = [RKObjectMapping mappingForClass:[CapitalImage class]];
[CapitalImageMap addAttributeMappingsFromDictionary:#{
#"src": #"src"
}];
RKObjectMapping *CapitalMap = [RKObjectMapping mappingForClass:[Capital class]];
[CapitalMap addAttributeMappingsFromDictionary:#{
#"name": #"name",
#"text": #"text"
}];
[CapitalMap addPropertyMapping:[RKRelationshipMapping relationshipMappingFromKeyPath:#"images" toKeyPath:#"images" withMapping:CapitalImageMap]];
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:CapitalMap
method:RKRequestMethodAny
pathPattern:nil
keyPath:nil
statusCodes:statusCodes];
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:#"http://www.a10073.de4.dp10.ru/icapitals/capital.php"]];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *result) {
Capital *article = [result firstObject];
NSLog(#"Mapped the article: %# , %#", article.name,article.images.description);
} failure:^(RKObjectRequestOperation *operation, NSError *error) { NSLog(#"Failed with error: %#", [error localizedDescription]); }];
[operation start];
I get this result
2013-09-27 23:20:49.028 iCapitals v2[5099:c07] Mapped the article: London , (
(null),
(null),
(null),
)
LOGS - http://www.a10073.de4.dp10.ru/icapitals/consoleresult.txt
Please check the code and tell what i do wrong, Thanks!!!
Your mappings look correct. The log shows the mapping proceeding correctly. The issue appears to be with the CapitalImage class. Why is it giving a nil description? It could be that that is the only problem. So your log of the array is a list of nil, but the objects do exist.
Try logging the src of each objects. Are you seeing other issues? Did you implement the description method?
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]];