More than one RKObjectManager at a time (RestKit)
I am testing out RestKit and need to access different BaseUrls and also sometimes acces开发者_如何转开发s a web service with the same baseUrl from different places "at once", lastly I also need to access the same baseUrl with different ressourcePaths in the same controller.
In my app delegate I set up the RKObjectManager singleton like this.
RKObjectManager *objectManager = [RKObjectManager objectManagerWithBaseURL:kBaseUrl];
[objectManager registerClass:[EntityClass1 class] forElementNamed:@"element1"];
[objectManager registerClass:[EntityClass2 class] forElementNamed:@"element2"];
.
.
.
etc.
The singleton approach is really easy to work with, I however can't figure out how to separate the different web service calls.
In MyViewController, which implement the RKObjectLoaderDelegate, I will have the two methods:
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
//stuff with result
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
//stuff with error
}
This causes no problems when MyViewController uses one RKObjectManager singleton to access one ressourcePath with one baseUrl.
If I start different requests in this way:
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:FLICKRPath delegate:self]
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:FOURSQUAREPath delegate:self]
and so on, within the same MyController, my problem is that FLICKRPath and FOURSQUAREPath of course has different baseUrl, but the RKObjectManager only has one?
If I get this working and can have different RKObjectManagers another problem arises. The delegate methods didLoadObjects and didFailWithError will receive results from both RKObjectManagers and I can't see any other way to tell them apart than from their baseUrls. Potentially comparing each return value with a baseUrl and, even worse, a ressourcePath, in the delegate method does not appeal to me at all.
If I have different RKObjectManagers I guess I could pass them different delegates and build classes dedicated to deal with the return values from different baseUrls and ressourcePaths. This would mean I had to build yet another abstraction on top of MyController and RestKit, which also seems messy.
I have a strong feeling I am going about this in the wrong way, the RestKit source is very impressive which indicates that is me fighting the framework. I would really appreciate some best practice insights on the subject. I have been through all the resources and examples that I could find but have not seen the above use case. It is always one RKObjectManager, one baseUrl and one ressourcePath.
Thank you in advance.
Since there is no accepted answer yet: using multiple object managers is quite simple using RestKit
.
From the Wiki (Using Multiple Base URLs (and Multiple Object Managers):
The first object manager you create will be the shared singleton RestKit uses by default. But by creating additional object managers, you can pull from their BaseURLs as needed, just be sure to retain these new managers.
RKObjectManager *flickrManager =
[RKObjectManager objectManagerWithBaseURL:flickrBaseUrl]; // <-- shared singleton
RKObjectManager *foursquareManager =
[[RKObjectManager objectManagerWithBaseURL:foursquareBaseUrl] retain]; // <-- you must retain every other instance.
Depending on your application, you may want to put this second object manager in a more accessible place, like a retained property on the AppDelegate, so that it's easy to pull from as needed. In the event that you need to differentiate between the results from your two (or more) object managers, simply set an identifier in the userData for the queries.
- (void)someAction(id)sender {
// .......
RKObjectLoader* loader = [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/whatever" delegate:self];
loader.userData = @"foursquare";
// or do this, if you need a number instead of a string
loader.userData = [NSNumber numberWithInt:1234];
// .......
}
//Then when the delegate comes back you can cast it into a string or number as appropriate:
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects {
// .......
NSString* source = (NSString*) objectLoader.userData;
// or, if you did the NSNumber instead:
NSNumber* source = (NSNumber*) objectLoader.userData;
// .......
}
API change:
RKObjectLoader* loader = [[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/whatever" delegate:self];
doesn't compile in RestKit v.0.10.3 (loadObjectsAtResourcePath:delegate: returns void). That method just wraps a few lines of code, though, so you can still get at the loader, and add userData, with the following:
RKObjectLoader *loader = [[RKObjectManager sharedManager] loaderWithResourcePath:resourcePath];
loader.userData = @"SOMEDATA";
loader.delegate = self;
loader.method = RKRequestMethodGET;
[loader send];
(adding note in case other new users run into the same issues I did).
And by the way, since userData property is also available on RKRequest, you can use the same approach for loading/identifying requests.
For example, some post request:
RKClient * client = [RKClient sharedClient];
[client post:@"/your-api-path" usingBlock:^(RKRequest *request) {
request.userData = @"<some-object-you-can-check-in-delegate-callback>";
request.params = someParamsForRequest;
request.delegate = <delegate you want to call when request is finished>;
}];
How about using objectLoader.
You'll find the mapped object type/Class objectLoader.objectMapping.objectClass
and add your conditions based on it instead of the url
-(void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
// your condition based on -> objectLoader.objectMapping.objectClass
}
Hope it will help
Possible approach is to introduce one singletone for each base url.
You can instantiate as many RKObjectManager
objects as you want. However, only the first one will become shared. Look into initWithHTTPClient:
sources.
if (nil == sharedManager) {
[RKObjectManager setSharedManager:self];
}
We can't use default sharedManager
method to target specific object manager but we can easily implement our own singleton. Here's an example for Google Maps object manager:
@implementation GMObjectManager
+ (GMObjectManager*)sharedManager
{
static GMObjectManager *manager; // keep reference
if (!manager) {
// init with custom base url
NSURL *baseUrl = [NSURL URLWithString:kGMBaseUrl];
manager = [GMObjectManager managerWithBaseURL:baseUrl];
}
return manager;
}
- (id)initWithHTTPClient:(AFHTTPClient *)client
{
self = [super initWithHTTPClient:client];
if (self) {
// additional initialization
}
return self;
}
@end
Usage:
CGObjectManager *googleMapsManager = [GMObjectManager sharedInstance];
精彩评论