I am looking for some solution that could help me reuse methods that use Restkit. I have these methods in my controller:
RKObjectManager *restManager = [RKObjectManager sharedManager];
[restManager getObjectsAtPath:#"somePath"
parameters: ... some parameters ...
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
{
...
}
failure:^(RKObjectRequestOperation *operation, NSError *error)
{
...
}];
and I want to create some helper class that would contains these methods and give simpler methods. Something like this:
- (someResultClass *)getResultsFromSomePath:(NSDictionary *)parameters;
And I would check if in someResultClass are results or it ends with failure. I hope it's clear what I want. I am looking for some solution to move code with RestKit to some container class and just have one line of code instead. I think it could be problem that there are blocks but I hope it solution exists. One more thing, it's not thing that would be needed I am just looking for solution that could make controller class more simple and move more code to other classes.
Related
[[RKObjectManager sharedManager] getObjectsAtPath:#"/mypath/objects" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
// Use objects that are returned.
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
// Handle error
}];
This method of RestKit will perform a request, mapping, saving to core data, and performing the success callback AFTER saving. What I want to know is the method I can use to get the response and perform a callback BEFORE RestKit automatically saves it in core data. I still want to be able to use the ability of RestKit to do the mapping and saving but I want to intercept or at least get a callback BEFORE it performs saving of data.
I found a way to do it by implementing the callback block of the RKManagedObjectRequestOperation before the mapping begins since this will be called after a successful HTTP response and before the automatic saving to core data:
[managedObjectRequestOperation setWillMapDeserializedResponseBlock:^id(id deserializedResponseBody) {
//do something
return deserializedResponseBody;
}];
how to i set response mapping to manager with path pattern ..if the getobjects at path is different from path pattern that is used to map the response.
[manager addResponseDescriptorsFromArray:
#[[RKResponseDescriptor responseDescriptorWithMapping:categoryMapping
pathPattern:A
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]];
[manager getObjectsAtPath:A/ID
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#" Category success");
[self.delegate didReceiveAssignedCategories];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Category failure");
}];
response mapping path ie:A must be set to dynamic path used to getobject ie:A/ID .
ex:
Call 1)
A = /getAllCategories
A/ID = /getAllCategories/123
call 2)
A = /getAllCategories
A/ID = /getAllCategories/456
response mapping is same for 123, 456
only while getting the objects i am using different urls ie: with id's attached.
how to do that ?
If you have 2 path patters which both return the same type of data then you can use the same mapping with 2 different response descriptors.
If you have 1 path pattern which can return 2 different types of data then you need to use RKDynamicMapping to 'intercept' the incoming data and decide which mapping is actually required.
From your edited question, 'pattern' is the important thing that you have misunderstood. You need to use a path pattern, not a static path:
#"getAllCategories/:identity"
1) First create response mapping like
[manager addResponseDescriptorsFromArray:
#[[RKResponseDescriptor responseDescriptorWithMapping:categoryMapping
pathPattern:#"getAllCategories/:categoryID"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]];
2)Create Class with categoryID in it.
[CategoryRequest class]
3) create object of that class and set categoryID
CategoryRequest *categoryRequest = [CategoryRequest alloc] init];
categoryRequest.categoryID = #"123";
4)call getobject using that object
[manager getObject:categoryRequest
path:#"getAllCategories/123"
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
if another call is required to be made for same mapping create object of of category request class set new category id and call get object using that categoryresquest and required path patter.
I am using RestKit 0.2.x with Core Data and following the standard tutorials, ie:
Create Core Data model and use mogenerator to make code
Instantiate object manager with base URL
Create managed object context and persistent store
Create entity mappings for all entities returned by my web service
Create response descriptors for all web service endpoints and entities
Add response descriptor to object manager
Everything seems to be "working" just fine ... I can call
[[RKObjectManager sharedManager] getObjectsAtPath:_requestPath parameters:_requestParameters success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[self requestSuccess];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
[self requestError:error];
}];
... all day long, and I then I keep handling with (as shown in the tutorials)
- (void)requestSuccess {
NSManagedObjectContext *managedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:_entityName];
fetchRequest.sortDescriptors = #[_defaultSortDescriptor];
NSError *error = nil;
requestData = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
[_delegate handleRequestSuccess:self withData:requestData];
//[self cleanupRequestBeforeSuccessWithData:requestData];
[self completeRequest];
}
Now the problem is that at least by default, RestKit+CoreData actually persists your GET'ted objects to its own persistence store, or something like that. I'll explain the "cleanupRequest..." in a moment.
That kind of defeats the purpose of trying to allow the users to specify parameters at the level of the web service client, because all of the objects seem to end up in the same place anyway.
For instance, let's say I have a method /api/endpoint?queryString and I call it with two different sets of parameters:
[[RKObjectManager sharedManager] getObjectsAtPath:#"/api/endpoint" parameters:PARAMS_ONE success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[self requestSuccess];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
[self requestError:error];
}];
[[RKObjectManager sharedManager] getObjectsAtPath:#"/api/endpoint" parameters:PARAMS_TWO success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
[self requestSuccess];
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
[self requestError:error];
}];
If I then blindly follow the tutorials about how to retrieve my objects, my callbacks are then identical!
NSManagedObjectContext *managedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:#"EndpointDataTransferObject"];
fetchRequest.sortDescriptors = #["endpointDataTransferObjectID"];
NSError *error = nil;
requestData = [managedObjectContext executeFetchRequest:fetchRequest error:&error];
The result, of course, is that the my delegate gets sent (pseudocode) requestData WHERE PARAMS_ONE on the first call, and then requestData WHERE PARAMS_ONE UNION requestData WHERE PARAMS_TWO on the second call.
Now all I really want is to be able to conduct the NSFetchRequest on only those items mapped from the web service. I think this is a totally reasonable expectation, so clearly I am missing something because whoever wrote this library is much more clever than I.
For instance, if I could somehow get an NSArray of all the objects from the two parameters it provides in the success block (RKRequestRequestOperation *o, RKMappingResult *m) - and if I can, please tell me how!!! - then my problem would be solved, and I could enjoy the caching without having to worry about whether my filters are being ignored.
What I do not want to do, however, is this:
Call getObjectsAtPath: parameters: success: failure: with parameters and/or path representing a sort of "server-side" predicate
On success, create a NSFetchRequest and a client-side predicate that mirrors my server-side predicate
This approach seems really really dumb, and yet, I don't know any better. But I refuse to do that. It is error-prone, redundant, and potentially resource-intensive.
So instead, I've opted to add a little method cleanupRequestBeforeSuccessWithData at the end of my success callback before calling completion:
- (void)cleanupRequestBeforeSuccessWithData:(NSArray *)managedObjects {
NSManagedObjectContext *managedObjectContext = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
for (NSManagedObject *o in managedObjects) {
[managedObjectContext deleteObject:o];
}
NSError *error = nil;
[managedObjectContext save:&error];
}
This is ugly but it sure gets the job done. Now it totally empties the cache, but I'd rather have to make requests over and over again than to form "server-side" predicates with URL's and then form client-side NSPredicates.
What am I missing about how this is supposed to work? Clearly, I'm missing something big.
If I then blindly follow the tutorials
Never a good idea, you need to take what you've learnt from the tutorials and apply it to your problem space.
The items are indeed persisted as you're using Core Data. You don't need to but it does help with memory management. Technically you don't need to run a fetch because the mapping result (RKMappingResult) contains all the mapped objects, so you can just extract them and pass them on.
The other approach you shun of running a local fetch with filters is actually perfectly acceptable, and I'd say it's the usual approach as it's how a fetched results controller based approach works... To facilitate that you would add the query parameters to your mapping so that the mapped objects are updated. You do need to be cautious though as multiple requests returning the same objects could overwrite the data (assuming you're using unique identifiers).
I am posting to my rails server using the post object method.
[manager postObject:recipe path:#"/api/recipes" parameters:#{ #"auth_token": fbAccessToken } success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"mappingResult: %#", mappingResult);
}];
This works as intended, however my Rails app is returning the json of the Recipe. How do I access this value?
All returned values are in mappingResult variable.
RKMappingResult object has many methods that will return the serialized JSON when set-up properly. Such methods are: dictionary and array. You can look at the header file for other methods.
Also be sure to read documentation that can be found on RestKit website and a lot of things will be easier for you.
how to i set response mapping to manager with path pattern ..if the getobjects at path is different from path pattern that is used to map the response.
[manager addResponseDescriptorsFromArray:
#[[RKResponseDescriptor responseDescriptorWithMapping:categoryMapping
pathPattern:A
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]];
[manager getObjectsAtPath:A/ID
parameters:nil
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#" Category success");
[self.delegate didReceiveAssignedCategories];
}
failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Category failure");
}];
response mapping path ie:A must be set to dynamic path used to getobject ie:A/ID .
ex:
Call 1)
A = /getAllCategories
A/ID = /getAllCategories/123
call 2)
A = /getAllCategories
A/ID = /getAllCategories/456
response mapping is same for 123, 456
only while getting the objects i am using different urls ie: with id's attached.
how to do that ?
If you have 2 path patters which both return the same type of data then you can use the same mapping with 2 different response descriptors.
If you have 1 path pattern which can return 2 different types of data then you need to use RKDynamicMapping to 'intercept' the incoming data and decide which mapping is actually required.
From your edited question, 'pattern' is the important thing that you have misunderstood. You need to use a path pattern, not a static path:
#"getAllCategories/:identity"
1) First create response mapping like
[manager addResponseDescriptorsFromArray:
#[[RKResponseDescriptor responseDescriptorWithMapping:categoryMapping
pathPattern:#"getAllCategories/:categoryID"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]];
2)Create Class with categoryID in it.
[CategoryRequest class]
3) create object of that class and set categoryID
CategoryRequest *categoryRequest = [CategoryRequest alloc] init];
categoryRequest.categoryID = #"123";
4)call getobject using that object
[manager getObject:categoryRequest
path:#"getAllCategories/123"
parameters:params
success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
NSLog(#"Success");
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
NSLog(#"Failure");
}];
if another call is required to be made for same mapping create object of of category request class set new category id and call get object using that categoryresquest and required path patter.