Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RestKit GET query parameters

I've been using RestKit 0.10.0 for a while now and up until this point, I only posted serialized objects to my server:

[[RKObjectManager sharedManager] postObject:serializedObject
                                 usingBlock:^(RKObjectLoader *loader) {
                                     loader.delegate = self;
                                     loader.objectMapping = responseMapping;
                                     loader.serializationMIMEType = RKMIMETypeFormURLEncoded;
                                     loader.targetObject = nil;
                                 }];

So far, so good. But I now need to make a GET request to the server with a few query parameters. The first natural thing that came in mind was to do the same as I did for posting objects:

  • create a serialization mapping for the object encapsulating the query parameters
  • create a response mapping for the object being received from the server
  • define and use a router for RKRequestMethodGET (instead of RKRequestMethodPOST)
  • make the request using getObject:usingBlock (instead of postObject:usingBlock)

I soon found out this is not the way to do it, so after searching the available resources (RestKit Wiki, RestKit Google group) I now know of two solutions considered as valid:

  • Appending the query parameters to the resource path.

This works perfectly.

NSDictionary *queryParams = [NSDictionary dictionaryWithObjectsAndKeys:
                                          token, @"accessToken",
                                          [NSNumber numberWithInt:level], @"level",
                                          [NSNumber numberWithInt:count], @"count",
                                          nil];

NSString* resourcePath = [PEER_SUGGESTIONS_CONTROLLER_PATH stringByAppendingQueryParameters:queryParams];

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:resourcePath
                                                usingBlock:^(RKObjectLoader *loader) {
                                                    loader.delegate = self;
                                                    loader.objectMapping = responseMapping;
                                                }];
  • Setting the query parameters in the loader block.

This does not send the query parameters.

RKParams *params = [RKParams params];
[params setValue:token forParam:@"accessToken"];
[params setValue:[NSNumber numberWithInt:level] forParam:@"level"];
[params setValue:[NSNumber numberWithInt:count] forParam:@"count"];

[[RKObjectManager sharedManager] loadObjectsAtResourcePath:PEER_SUGGESTIONS_CONTROLLER_PATH
                                                usingBlock:^(RKObjectLoader *loader) {
                                                    loader.delegate = self;
                                                    loader.objectMapping = responseMapping;
                                                    loader.params = params;
                                                }];

My questions are:

  1. Why doesn't the second solution work?
  2. Why is the first solution working without having to set the loader.targetObject to nil, although I do not have any root key path in the JSON response?
  3. What are the cases where I should use the getObject:usingBlock method? What is its intended purpose?
  4. What should I use loader.params for? The object mapping tutorial from the wiki says this property can be used to encapsulate POST parameters, but I do not see the point since I can wrap the parameters in the serialized object that is being sent with the method postObject:usingBlock.

Thanks.

[LATER EDIT]

Regarding the answer to my second question: I've been setting the targetObject to nil in the loader block when making POST requests beacause otherwise RestKit will try use the send object mapping for the response (check this link for a related discussion). But since I am using loadObjectsAtResourcePath:usingBlock:, there is no object being sent, therefore the response will naturally map on the response mapping without having to the set targetObject to nil.

like image 265
sebastian.gherman Avatar asked Jun 27 '12 13:06

sebastian.gherman


2 Answers

  1. Why doesn't the second solution work?

params is used to create a HTTP body, which is not used in a GET/HEAD request.

  1. Why is the first solution working without having to set the loader.targetObject to nil, although I do not have any root key path in the JSON response?

I think targetObject is nil by default. You normally don't set it, the request will create it if needed. The only time I use it is when requesting objects without primary keys or other weird problems.

  1. What are the cases where I should use the getObject:usingBlock method? What is its intended purpose?

This is a convenience method so you don't have to remember all the correct syntax. Internally it just sends an object load request using GET.

EDIT:

Use this if you have an object you want to update.

  1. What should I use loader.params for? The object mapping tutorial from the wiki says this property can be used to encapsulate POST parameters, but I do not see the point since I can wrap the parameters in the serialized object that is being sent with the method postObject:usingBlock.

Whatever you put in params will be serialized to an HTTP body (or body stream). Again, postObject:usingBlock: is just a convenience method so you don't have to remember everything.

RestKit is open source. If you are not sure how it works you are free to follow the parameters internally. If you app and web service is well designed, you should be able to use the convenience methods. Sometimes you can not, and then you can use the raw forms like you have done.

EDIT: Q Hrm, quoting your bullet points messed up the numbers...

like image 103
Paul de Lange Avatar answered Sep 28 '22 09:09

Paul de Lange


I solved adding a Category to RKObjectLoader, that is:

for method

-(void)getObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block;

I added into the Category a modified method:

-(void)getObject:(id<NSObject>)object queryParameters:(NSDictionary*)queryParameters usingBlock:(void(^)(RKObjectLoader *))block;

Here it is the listing fpr file "RKObjectManager+QueryParameters":

//
//  RKObjectManager+QueryParameters.h
//  AlphaClient
//
//  Created by Antonio Rossi on 14/07/12.
//

#import <RestKit/RestKit.h>

@interface RKObjectManager (QueryParameters)

- (void)getObject:(id<NSObject>)object queryParameters:(NSDictionary*)queryParameters usingBlock:(void(^)(RKObjectLoader *))block;
- (void)sendObject:(id<NSObject>)object queryParameters:(NSDictionary*)queryParameters method:(RKRequestMethod)method usingBlock:(void(^)(RKObjectLoader *))block;

@end

Here is the listing for file "RKObjectManager+QueryParameters.m":

//
//  RKObjectManager+QueryParameters.m
//  AlphaClient
//
//  Created by Antonio Rossi on 14/07/12.
//

#import "RKObjectManager+QueryParameters.h"

@implementation RKObjectManager (QueryParameters)

- (void)getObject:(id<NSObject>)object queryParameters:(NSDictionary*)queryParameters usingBlock:(void(^)(RKObjectLoader *loader))block {
    [self sendObject:object queryParameters:queryParameters method:RKRequestMethodGET usingBlock:block];
}

- (void)sendObject:(id<NSObject>)object queryParameters:(NSDictionary*)queryParameters method:(RKRequestMethod)method usingBlock:(void(^)(RKObjectLoader *))block {
    NSString *resourcePath = [self.router resourcePathForObject:object method:method];
    [self sendObject:object toResourcePath:resourcePath usingBlock:^(RKObjectLoader *loader) {
        loader.method = method;

        // need to transform the original URL because when method is GET the additional paramentes don't get added
        RKURL *originalBaseURL = [RKURL URLWithBaseURL:[loader.URL baseURL]];
        NSString *resourcePath = [self.router resourcePathForObject:object method:RKRequestMethodGET];
        RKURL *authTokenURL = [originalBaseURL URLByAppendingResourcePath:resourcePath queryParameters:queryParameters];
        [loader setURL:authTokenURL];

        block(loader);
    }];
}

@end

One more step is to add #import "RKObjectManager+QueryParameters.h" in your implementation file. In this new method it is assumed that the router property of RKObjectManager has been defined before making a call to it.

like image 31
Antonio Rossi Avatar answered Sep 28 '22 08:09

Antonio Rossi