RestKit Doing a Post With Object and Getting Object Response

2.3k Views Asked by At

I am new to both IOS and RestKit and can not figure out how to do the following:

1. a simple POST to a webservice 
  A) with an object payload
  B) Have RestKit Auto Serialize the obj c object into Json

2. Receive the Response
  A) Parse response and extract only certain fields and load into obj c object

My setup is as follows:

Json response from webservice:

{
    "user": {
          "id":0,
          "email":"String",
     },
     "errorCode": 0
}

Response Object from Webservice in obj c:

@interface CreateAccountResponse : NSObject

@property (nonatomic, copy) NSString *id;
@property (nonatomic, copy) NSString *email;
@property (nonatomic) NSInteger   *errorCode;

@end

Request Object in obj C:

@interface CreateAccountRequestModel : NSObject

@property (nonatomic, copy) NSString *email;
@property (nonatomic, copy) NSString *password;
@property (nonatomic, copy) NSString *first_name;
@property (nonatomic, copy) NSString *last_name;

@end

Webservice expects this Json in POST Request:

{
    "email":"String",
    "password":"String",
    "first_name":"String",
    "last_name":"String"
}

URL is: www.hostname.com/api/AccountManager/createAccount and it only accepts POST requests.

What I have tried:

-(void)SendCreateAccountRequest:(CreateAccountRequestModel*)createActModel
{
    NSString *createAccountUrl = @"www.hostname.com/api/AccountManager/createAccount";
    NSLog(@"SendCreateAccountRequest");
    RKObjectMapping* articleMapping = [RKObjectMapping mappingForClass:  [CreateAccountResponse class]];
    [articleMapping addAttributeMappingsFromDictionary:@{
                                                         @"id": @"user.id",
                                                         @"email": @"user.email",
                                                         @"errorCode": @"errorCode"
                                                         }];

    RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:articleMapping method:RKRequestMethodAny pathPattern:nil keyPath:@"createAccount" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];

    NSURL *URL = [NSURL URLWithString:createAccountUrl];
    NSURLRequest *request = [NSURLRequest requestWithURL:URL];
    RKObjectRequestOperation *objectRequestOperation = [[RKObjectRequestOperation alloc] initWithRequest:request responseDescriptors:@[ responseDescriptor ]];

    [objectRequestOperation setCompletionBlockWithSuccess:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult)
    {
        RKLogInfo(@"Successful Response: %@", mappingResult.array);
    }
    failure:^(RKObjectRequestOperation *operation, NSError *error)
    {
        RKLogError(@"Operation failed with error: %@", error);
    }];

    [objectRequestOperation start];

I know I am not correctly mapping the response from the webservice to an object and I am not correctly creating the POST request. Like I said I am new, so the more details you give the better :)

3

There are 3 best solutions below

1
On BEST ANSWER

User Object

@interface User : NSObject

@property (nonatomic, copy) NSString *id;
@property (nonatomic, copy) NSString *email;
@property (nonatomic, copy) NSString *password;
@property (nonatomic, copy) NSString *first_name;
@property (nonatomic, copy) NSString *last_name;

@end

POST http://www.hostname.com/api/AccountManager/createAccount

response JSON

{
    "id":0,
    "email":"String",
}

Create On Server

- (void)createUserOnServer:(User *)user
{
    RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[User class]];
    [responseMapping addAttributeMappingsFromArray:@[@"id",@"email"]];
    NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful);
    RKResponseDescriptor *userResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping method:RKRequestMethodAny pathPattern:@"/createAccount" keyPath:@"article" statusCodes:statusCodes];

    RKObjectMapping *requestMapping = [RKObjectMapping requestMapping]; // objectClass == NSMutableDictionary
    [requestMapping addAttributeMappingsFromArray:@[@"email", @"password", @"first_name", @"last_name"]];

    RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping objectClass:[Article class] rootKeyPath:nil method:RKRequestMethodAny];

    RKObjectManager *manager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:@"http://www.hostname.com/api/AccountManager"];
    [manager addRequestDescriptor:requestDescriptor];
    [manager addResponseDescriptor:articleDescriptor];
    [manager postObject:user path:@"/createAccount" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                NSLog(@"Create User Success");
            } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                NSLog(@"Create User Error: %@", error);
            }];
}

PS: You'd better to read Restkit's README completely!!

PS2: error code by HTTP statusCode is better

1
On

This should make a post without the need for a third party library. Once you get this working, you could put a getter in the same class and call it your RestKit, or use the working example to debug the RestKit problem. (My preference is the former).

+ (void)postTo:(NSString *)urlString params:(NSDictionary *)params completion:(void (^)(id, NSError *))completion {

    NSURL *url = [NSURL URLWithString:urlString];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setHTTPMethod:@"POST"];

    if (params.count) {
        NSMutableArray *paramArray = [NSMutableArray array];
        for (NSString *key in [params allKeys]) {
            NSString *encodedKey = [key stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            NSString *encodedValue = [params[key] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            NSString *encodedPair = [[encodedKey stringByAppendingString:@"="] stringByAppendingString:encodedValue];
            [paramArray addObject:encodedPair];
        }

        NSString *paramString = [paramArray componentsJoinedByString:@"&"];

        NSData *data = [paramString dataUsingEncoding:NSASCIIStringEncoding allowLossyConversion:YES];
        [request setHTTPBody:data];
        [request setValue:[NSString stringWithFormat:@"%d", [data length]] forHTTPHeaderField:@"Content-Length"];
        [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
    }

    [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error) {

        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;

        if (!error && httpResponse.statusCode < 400) {
            NSError *parseError;
            id parse = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&parseError];
            if (!parseError) {
                completion(parse, nil);
            } else {
                NSLog(@"parse error %@", parseError);
                completion(nil, parseError);
            }
        } else {
            NSLog(@"http status %d, error %@", httpResponse.statusCode, error);
            completion(nil, error);
        }
    }];
}

Call it like this:

NSString *urlString = @"http://www.hostname.com/api/AccountManager/createAccount";
NSDictionary *params = @{ @"email": @"[email protected]" /* and so on */ };

// assume the method above is a class method in "ThePostingClass"
[ThePostingClass postTo:urlString params:params completion:^(id result, NSError *error) {
    if (!error) {
        NSLog(@"%@", result);
    } else {
        NSLog(@"%@", error);
    }
}];

Getter is easier, change the setHttpMethod to @"GET", and optionally move the params by appending paramString as a query string.

0
On

keyPath:@"createAccount" on your response descriptor appears to be wrong as your JSON does not contain this key (so it should be keyPath:nil). This should actually be set as the path pattern (pathPattern:@"createAccount").

You should really use an instance of RKObjectManager to create and send the request - this will allow you to easily serialise the request object to be sent. To enable this you need to create a request descriptor with a mapping for the keys in your object (the names aren't changing so it's a simple mapping).

When using RKObjectManager you register the request and response descriptors with it and then just GET / POST objects. The RKObjectManager will determine, from the path you specify in the request, what request and response mappings should be used.

When you create the RKObjectManager, use a base URL of www.hostname.com/api/AccountManager.

Finally, to send JSON you need to set the requestSerializationMIMEType on the RKObjectManager (because it doesn't default to JSON).