RESTKit: Deleting an Object with Body Parameters - ios

RESTKit 0.20.x
I need to send the following DELETE request:
URL: http://rest.domain.com/invite
body: { #"inviteId" : "1234" }
In trying to build that request, below is the code that I'm using:
AppDelegate *appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
self.objectManager = [self getObjectManager];
self.objectManager.managedObjectStore = appDelegate.managedObjectStore;
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[InviteDelete class]];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[mapping inverseMapping] objectClass:[NSMutableDictionary class] rootKeyPath:nil method:RKRequestMethodDELETE];
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodAny
pathPattern:#"/invites"
keyPath:nil
statusCodes:statusCodeSet];
self.objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[self.objectManager addRequestDescriptor:requestDescriptor];
[self.objectManager addResponseDescriptor:responseDescriptor];
[self.objectManager.HTTPClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
InviteDelete *objectToDelete = [[InviteDelete alloc]init];
objectToDelete.inviteId = [NSNumber numberWithInt:294];
[self.objectManager deleteObject:objectToDelete path:#"/invites" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
And Charles Log shows the following request being sent (RAW):
DELETE /invites HTTP/1.1
Host: rest.domain.com
Accept: application/json
Connection: keep-alive
Cookie: connect.sid=PLv05FHG8Al7A84x84mMd.mjlxE3ff3Map
User-Agent: App/1.0 (iPhone Simulator; iOS 7.1; Scale/2.00)
Accept-Language: en;q=1, fr;q=0.9, de;q=0.8, zh-Hans;q=0.7, zh-Hant;q=0.6, ja;q=0.5
Content-Length: 0
Accept-Encoding: gzip, deflate
I know it's easier to create a DELETE when embedded in the URL, where I can just simply add parameters to the objectManager. I wish I had that choice! I have to create a request with body parameters to DELETE.
Requirement: How can I create a simple JSON DELETE request that has the following in the body?
{ #"inviteId" : "1234" }
Optional: It would be nice the RESTKit can also delete the local object upon success.

Your request descriptor is wrong because you use objectClass:[NSMutableDictionary class] so it only applies when you try to delete an NSMutableDictionary instance. You should be using:
... objectClass:[InviteDelete class] ...
RestKit has no built in way to understand the response and automatically delete the source object so you need to verify the response content and then perform and save the deletion in the success callback block.

You can't have a body for delete requests by object mapping.
Rest API design supports DELETE body
So does HTTP 1.1 standards
Rest kit does't support object mapping but it surely allows you to create custom request and operations
NSDictionary *params = YOUR_JSON_BODY
NSMutableURLRequest *request = [self requestWithPathForRouteNamed:path
object:objectToDelete
parameters:nil];
[request setHTTPBody:[NSJSONSerialization dataWithJSONObject:params options:NSJSONReadingMutableLeaves error:nil]];
[request setHTTPMethod:#"DELETE"];
RKObjectRequestOperation *operation = [self objectRequestOperationWithRequest:request success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
if (success) ...
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
if (failure) failure(error);
}];
[self enqueueObjectRequestOperation:operation];

Related

RestKit simple JSON, bad pathPAttern?

JSONs:
http://krzysztofkurzawa.com/articles/
http://krzysztofkurzawa.com/articles/1
Code:
NSURL *baseURL = [NSURL URLWithString:#"http://www.krzysztofkurzawa.com"];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[mapping addAttributeMappingsFromArray:#[#"title"]];
RKResponseDescriptor *rk = [RKResponseDescriptor responseDescriptorWithMapping:mapping pathPattern:#"articles" keyPath:#"articles" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:rk];
RKObjectMapping *mapping2 = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[mapping addAttributeMappingsFromArray:#[#"title"]];
RKResponseDescriptor *rk2 = [RKResponseDescriptor responseDescriptorWithMapping:mapping2 pathPattern:#"articles/:id" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:rk2];
[[RKObjectManager sharedManager] getObjectsAtPath:#"articles" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#", [mappingResult firstObject]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
[[RKObjectManager sharedManager] getObjectsAtPath:#"articles/1" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#", [mappingResult firstObject]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
Error: No mappable object representations were found at the key paths
searched.", keyPath=null, NSLocalizedDescription=No response
descriptors match the response loaded.}
I haven't got any ideas. The first mapping works, when pathPattern is set to nil. Why must it be nil? Maybe it is needed for creating rest path patterns.
Your paths were not correct, also, when running your code I had to use the method:
+ (instancetype)responseDescriptorWithMapping:(RKMapping *)mapping
method:(RKRequestMethod)method
pathPattern:(NSString *)pathPattern
keyPath:(NSString *)keyPath
statusCodes:(NSIndexSet *)statusCodes
The one you are using it says it's deprecated. I guess you might be using an old version of restkit.
Here is the code that works:
NSURL *baseURL = [NSURL URLWithString:#"http://www.krzysztofkurzawa.com"];
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
RKLogConfigureByName("RestKit/ObjectMapping", RKLogLevelTrace);
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[mapping addAttributeMappingsFromArray:#[#"title"]];
RKResponseDescriptor *rk = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodGET pathPattern:#"/articles/" keyPath:#"articles" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:rk];
RKObjectMapping *mapping2 = [RKObjectMapping mappingForClass:[NSMutableDictionary class]];
[mapping2 addAttributeMappingsFromArray:#[#"title"]];
RKResponseDescriptor *rk2 = [RKResponseDescriptor responseDescriptorWithMapping:mapping2 method:RKRequestMethodGET pathPattern:#"/articles/:id/" keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:rk2];
[[RKObjectManager sharedManager] getObjectsAtPath:#"/articles/" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#", [mappingResult firstObject]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
[[RKObjectManager sharedManager] getObjectsAtPath:#"/articles/1/" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"%#", [mappingResult firstObject]);
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
}];
May be this halp you
NSString *pathNotation = #"/Notations/:notation_id";
RKRoute *notationRoute = [RKRoute routeWithClass:[Notation class] pathPattern:pathNotation method:RKRequestMethodGET];
[self.objectManager.router.routeSet addRoute:notationRoute];
RKResponseDescriptor *notationMappingDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:notationMapping method:RKRequestMethodGET pathPattern:pathNotation keyPath:nil statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
and GET:
[[RKObjectManager sharedManager] getObject:nil path:path parameters:params success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
//do something
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
//do something
}];

iOS RestKit loggging doesn't print anything

I would like to user RestKit's network logging with the following code:
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
I am creating GET/POST requests and nothing is printed to the console. I am using Xcode 6.1.1.
Anybody has any clues why it isn't printing anything to the console?
Edit:
RestKit initialization:
RKLogConfigureByName("RestKit/Network", RKLogLevelTrace);
[RKObjectManager setSharedManager:[RKObjectManager managerWithBaseURL:self.baseURL]];
[[RKObjectManager sharedManager] setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[RKObjectManager sharedManager].requestSerializationMIMEType = RKMIMETypeJSON;
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithPersistentStoreCoordinator:[IPCoreDataHelper sharedInstance].persistentStoreCoordinator];
[managedObjectStore createManagedObjectContexts];
[RKManagedObjectStore setDefaultStore:managedObjectStore];
[RKObjectManager sharedManager].managedObjectStore = [RKManagedObjectStore defaultStore];
[self setMappings];
After this, for example, the user will press the login button and the following code is executed
Login:
// ... Setup the user object
NSMutableURLRequest *urlRequest = [[RKObjectManager sharedManager] requestWithObject:user method:RKRequestMethodPOST path:#"login/" parameters:nil];
urlRequest.timeoutInterval = 300;
RKManagedObjectRequestOperation *operation = [[RKObjectManager sharedManager] managedObjectRequestOperationWithRequest:urlRequest managedObjectContext:[RKManagedObjectStore defaultStore].mainQueueManagedObjectContext success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// Do something with login success
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Failed
}];
[operation start];

RestKit 2 PUT request for some Object

Server team wants to separate out 2 PUT requests for one Item class like this -
PUT '<server>/item/:itemId'
and
PUT '<server>/item/:itemId/like'
I am using the following code in the initializer:
RKObjectManager *sharedRKObjectManager = [RKObjectManager sharedManager];
RKManagedObjectStore *managedObjectStore = [sharedRKObjectManager managedObjectStore];
// Create the mapping for the News entity.
RKEntityMapping *responseMapping = [RKEntityMapping mappingForEntityForName:ENTITY_ITEM inManagedObjectStore:managedObjectStore];
[responseMapping addAttributeMappingsFromDictionary:#{
#"id": #"itemId",
}];
// Create a response descriptor and add it to the RKObjectManager object.
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
method:RKRequestMethodAny
pathPattern:UPDATE_ITEM_URL
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[sharedRKObjectManager.router.routeSet addRoute:[RKRoute routeWithClass:[Item class] pathPattern:UPDATE_ITEM_URL method:RKRequestMethodAny]];
[sharedRKObjectManager addResponseDescriptor:responseDescriptor];
And then the following is the call:
+(void)updateItem:(Item*)item
withParams:(NSDictionary*)params
success:(void (^)(RKObjectRequestOperation *operation, RKMappingResult *mappingResult))success
failure:(void (^)(RKObjectRequestOperation *operation, NSError *error))failure
{
[CMLRKSharedManager setUpHeaders];
[[RKObjectManager sharedManager] putObject:item path:nil parameters:params success:success failure:failure];
}
My ques is - can i use the same functions for these 2 requests? Can i append the url any other way?
TIA!
Just don't use the RKRoute in this case. Explicitly supply the appropriate path when you call put....
Note that you should also have 2 response descriptors because the path patterns are different.
in case it helps anyone.. here is the code to do this
NSString *path = [NSString stringWithFormat:#"item/%#/like", itemId];
[CMLRKSharedManager updateItem:item params:nil path:path success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Displaying user info %#", [mappingResult firstObject]);
} failure:nil];

RESTKit 2.0: Pattern string must not be empty in order to perform pattern matching

I use the code below to login to the server:
//First Block Code
-(RKObjectManager *)getObjectManager
{
NSURL *baseURL = [NSURL URLWithString:#"http://api.domain.com"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc]initWithBaseURL:baseURL];
RKObjectManager *manager = [[RKObjectManager alloc]initWithHTTPClient:httpClient];
[manager.HTTPClient registerHTTPOperationClass:[AFJSONRequestOperation class]];
[manager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[manager.HTTPClient setParameterEncoding:AFJSONParameterEncoding];
[RKMIMETypeSerialization registeredMIMETypes];
return [RKObjectManager sharedManager];
}
- (void)loginUserwithUsername:(NSString *)username andPassword:(NSString *)password requestByNewUser:(BOOL)newRegistration
{
[self getObjectManager];
RKObjectManager *objectManager = [RKObjectManager sharedManager];
[objectManager.HTTPClient setAuthorizationHeaderWithUsername:username password:password];
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKMapping *mapping = [RESTMappingProvider profileMapping];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:nil statusCodes:statusCodeSet];
NSMutableURLRequest *request = [objectManager.HTTPClient requestWithMethod:#"POST"
path:#"/login"
parameters:#{#"username": username,
#"password": password
}];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[responseDescriptor]];
[objectManager.HTTPClient registerHTTPOperationClass:[AFHTTPRequestOperation class]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"mappingResults Error %#", error);
}
}];
[operation start];
}
AFTER I login, I try to make a Google Places API request and get an error:
//Second Block of Code
- (void)fetchPlaces:(NSString *)input;
{
NSIndexSet *statusCodeSet = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
RKMapping *mapping = [RESTMappingProvider googleAutoCompleteMapping];
NSString *urlString = [NSString stringWithFormat:#"https://maps.googleapis.com/maps/api/place/json?input=%#&sensor=true&key=%#&location=0.000000,0.000000", input, self.key];
NSString *urlStringEncoded = [urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
NSURL *url = [NSURL URLWithString:urlStringEncoded];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping
method:RKRequestMethodGET
pathPattern:nil
keyPath:#"predictions" statusCodes:statusCodeSet];
RKObjectRequestOperation *operation = [[RKObjectRequestOperation alloc] initWithRequest:request
responseDescriptors:#[responseDescriptor]];
[operation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
self.responseObjects = mappingResult.array;
[operation start];
}
Error:
2014-04-02 14:11:17.865 App[1247:60b] *** Assertion failure in +[RKPathMatcher pathMatcherWithPattern:], /Users/App
Time/Pods/RestKit/Code/Support/RKPathMatcher.m:74
2014-04-02 14:11:17.868 App[1247:60b] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Pattern string must not be empty in order to perform pattern matching.'
However, if I never Login (meaning that I skip the first code in this question) and go straight to call Google API, there is no crash API works great.
I think there is something that I'm doing to RESTKit (perhaps by creating an ObjectManager), by Logging-in that's causing Google's API call to cause a crash.
I tried to run Charles Web Debug Proxy, but the crash seems to happen even before making the API call.
*EDIT *
I found out what is causing the crash:
[[RKObjectManager sharedManager] cancelAllObjectRequestOperationsWithMethod:RKRequestMethodAny matchingPathPattern:nil];
This was an attempt to Cancel all previous requests.
I replaced it with:
[[RKObjectManager sharedManager] cancelAllObjectRequestOperationsWithMethod:RKRequestMethodAny matchingPathPattern:#"maps/api/place/autocomplete"];
and it seems to work.
Question: Does this code cancel any previous request to: https://maps.googleapis.com/maps/api/place/autocomplete/json ?
When you create responseDescriptor this is added to the RKObjectManager you use pathPattern:nil. This is not permitted. You must specify a path pattern as RestKit must lookup the appropriate response descriptor to apply to the received response.
Later, you again use pathPattern:nil, but this is directly with an RKObjectRequestOperation. In this case it is allowed (and thus works) because you have provided an explicit list and no lookup is required.

Post JSON Body + MultiPart RestKit 0.2x fails

I am trying to post an object with an attached file.
NSMutableURLRequest *request =
[objectManager multipartFormRequestWithObject:reqDocObj
method:RKRequestMethodPOST
path:#"syncDocument.json"
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:UIImagePNGRepresentation([UIImage imageNamed:#"graybox.png"])
name:#"image"
fileName:#"some_file"
mimeType:#"image/jpeg"];
}];
RKObjectRequestOperation *operation =
[objectManager
objectRequestOperationWithRequest:request
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"WS: errore operazione di richiesta %#",error);
}
];
[objectManager enqueueObjectRequestOperation:operation];
The objectManager is configured as:
[objectManager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
[objectManager setRequestSerializationMIMEType:RKMIMETypeJSON];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
[EDIT]
My mapepd object is SynchDocObj:
requestDocMapping = [RKObjectMapping mappingForClass:[SynchDocObj class]];
[requestDocMapping addAttributeMappingsFromDictionary:mappingDocDict];
The problem is:
1) In the RKlogs, the request.body = null and the JSON object is put into the form-data
2) The server cannot decode the body because it is null
My question is:
1) Am I sending the JSON object in the wrong way?
2) If yes, how can I send a JSON object with a file upload, i.e. as a multipart request?
Regards!
[SOLUTION]
Following the suggestion of the answer, I think the solution is 1) retrieve the mapped object from the form-data and not the body ; 2) OR post a nil object and putting a JSON string within the form-data.
This:
[objectManager setRequestSerializationMIMEType:RKMIMETypeJSON];
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
is 2 different ways of saying the same thing - and you don't want to have either of them. You need to send the request as form URL encoded (the default value).
The easiest thing to do is to use the same form as in your current code to create the request, generate the JSON earlier and then use appendPartWithFormData:name: to add it to the request (just before you add the file).
To generate the JSON you can use RestKit (RKMappingOperation) or you can just create a dictionary / array of content and then use NSJSONSerialization to serialise that object to be added to the request.
Analyze my code, it works like a charm:
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[FilledExamCard defineMapping] objectClass:[MappedClassName class] rootKeyPath:nil method:RKRequestMethodPUT];
NSData *jsonPayload = [self getJSONpayloadFromObject:mappedClassObject requestDescriptor:requestDescriptor];
NSURL *baseURL = [NSURL URLWithString:[ZDR_BASE_URL stringByAppendingString:#"PutExamCards"]];
AFHTTPClient *client = [AFHTTPClient clientWithBaseURL:baseURL];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class] forMIMEType:#"text/plain"];
[client setDefaultHeader:#"Accept" value:#"text/plain"];
RKObjectManager *objectManager = [[RKObjectManager alloc] initWithHTTPClient:client];
[objectManager setRequestSerializationMIMEType: RKMIMETypeJSON];
[objectManager addRequestDescriptor:requestDescriptor];
NSMutableURLRequest *request = [objectManager multipartFormRequestWithObject:nil method:RKRequestMethodPUT path:#"" parameters:nil constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
// Name may vary depending from server settings
[formData appendPartWithFormData:jsonPayload name:#"model"];
}];
RKObjectRequestOperation *operation = [objectManager objectRequestOperationWithRequest:request success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// Process data
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// An error occurred
}];
-(NSData*)getJSONpayloadFromObject:(NSObject*)object requestDescriptor:(RKRequestDescriptor*)requestDescriptor
{
NSDictionary *paramObject = [RKObjectParameterization parametersWithObject:object requestDescriptor:requestDescriptor error:nil];
NSError *error;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:paramObject options:NSJSONWritingPrettyPrinted error:&error];
return jsonData;
}

Resources