Implement a REST client in an iOS application using RestKit

RestKit is the ideal iOS framework for REST server-client communication in iOS mobile applications. It’s written in Objective-C, built on top of the very popular AFNetworking framework, and contains a very powerful object mapping engine to make our lives easier when sending or receiving objects to or from a server. The setup process might look scary and complex at first, but once you have the basic setup, things should run smoothly and you will enjoy a very simple and efficient REST client in your app. In this blog post, I will guide you through the basic setup process of the RestKit framework, give you a grasp about the object mapping functionality, and demonstrate a few REST API calls.

Before We Begin

This tutorial assumes you are familiar with the iOS platform, Objective-C, XCode, server-client REST communication, and working with the command line.
If you are completely new to any of these things (basic knowledge is enough), then you might get a little lost in this tutorial.

Prerequisites

Before we begin you will need to already have:

  • iOS App – An iOS application project you are currently developing (could even be a dummy project).
  • REST API – A REST server which either you are in control of, or at least know the API requests you will need to issue and the data that is supposed to pass between the 2 sides in each request.
  • Cocoapods – Cocoapods is a library dependency management tool. With it, you can easily install libraries and frameworks on your XCode project. You will need to install it as a Ruby gem (Ruby comes pre-installed on OSX) by entering this line in the terminal:
  • $ sudo gem install cocoapods
  • And afterwards:
  • $ pod setup
  • Once Cocoapods is installed, navigate to your project library (still in the terminal) and enter this line:
  • $ pod init
  • This will integrate Cocoapods into your project, and create, among other things, a file called Podfile which will be used to manage your project’s dependencies.
    For more information about Cocoapods and the installation process, you can refer to the Cocoapods homepage, or this tutorial.

Now that we have everything ready, we can get started!

RestKit Installation

In this example, we will use RestKit v0.23.3. It is recommended to check their website from time to time to see if there is a new stable release available.
Open the project’s Podfile with your favorite text editor and add the following line to the bottom of it:

pod 'RestKit', '~>  0.23.3'

Then go to the terminal, navigate to the project’s root directory and enter the following line:

$ pod install

Wait for the installation process to finish, and open the project with the ProjectName.xcworkspace file (NOT the ProjectName.xcodeproj file!!). You should see the “Pods” section under your project in the project navigator. Note that files in this section shouldn’t be edited. You control the content of the section ONLY by installing and removing pods via the Podfile and the “pod install” command in the terminal. It should look like this:
RestKit in cocoapods
Once you are done with the installation, you can proceed to the next step.

RestKit Initial Setup

The RestKit framework has a singleton instance of the RKObjectManager class, which is in charge of setting up the REST client and object mappings, as well as performing the HTTP requests. You can call it from anywhere with:

[RKObjectManager sharedManager];

Use the following example code to set up the RKObjectManager class:

[AFNetworkActivityIndicatorManager sharedManager].enabled = YES; //this will show the network activity indicator in the status bar while an HTTP request is being performed.
NSURL *baseURL = [NSURL URLWithString:@"http://your.server.baseUrl"];
[RKObjectManager sharedManager] = [RKObjectManager managerWithBaseURL:baseURL]; //set the object manager's base server URL
[[RKObjectManager sharedManager] setRequestSerializationMIMEType:RKMIMETypeJSON]; //set the MIME type for object serialization and accepted data type
[[RKObjectManager sharedManager] setAcceptHeaderWithMIMEType:RKMIMETypeJSON]; //this example code works with JSON objects. choose the MIME type that fits your needs
[[RKObjectManager sharedManager].HTTPClient setDefaultHeader:@"header-key" value:@"header-value"]; //you can set as many headers as you wish

As for caching, RestKit uses the NSURLConnection APIs for issuing HTTP requests. For caching to be enabled, your server must determine the caching headers with the appropriate Cache-Control, Expires, and/or ETag headers. RestKit will automatically obey the caching policy set by your server.
Cookies can also be set by your server and RestKit will store and use them automatically, and you can also manually set cookies in the client and add them as headers to your RKObjectManager instance.

Object Mapping

json object
As mentioned before, RestKit works with an object mapping engine. Meaning you can send and receive native Objective-C objects, and RestKit will take care of mapping and serialization for you. All you have to do is create the mapping for each object and add the appropriate descriptors to the RKObjectManager instance, as will be explained immediately.
Say you have a user object you would like to map. The object’s header file looks something like this:

@interface User : NSObject

@property (nonatomic, copy) NSString *address;
@property (nonatomic, copy) NSString *phone;
@property (nonatomic, copy) NSString *firstName;
@property (nonatomic, copy) NSString *lastName;
@property (nonatomic, copy) NSString *age;
@property (nonatomic, copy) NSString *gender;
@property (nonatomic, copy) NSString *email;

@end

You will need to create a mapping and descriptors for the object, as described in the following code:

NSDictionary *mappingDict = @{@"address": @"address",
             @"phone": @"phone",
             @"firstName": @"firstName",
             @"lastName": @"lastName",
             @"age": @"age",
             @"gender": @"gender",
             @"email": @"email"} //define the object mapping

RKObjectMapping *responseMapping = [RKObjectMapping mappingForClass:[User class]]; //create response and request mapping object for the object
[responseMapping addAttributeMappingsFromDictionary:mappingDict];
RKObjectMapping *requestMapping = [RKObjectMapping requestMapping];
[mapping addAttributeMappingsFromDictionary:mappingDict];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
                                                                                            method:RKRequestMethodAny
                                                                                       pathPattern:nil
                                                                                           keyPath:nil
                                                                                       statusCodes:[NSIndexSet indexSetWithIndex:200]];

RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:requestMapping
                                                                                   objectClass:[User class]
                                                                                   rootKeyPath:nil
                                                                                        method:RKRequestMethodAny];
[[RKObjectManager sharedManager] addResponseDescriptor:responseDescriptor];
[[RKObjectManager sharedManager] addRequestDescriptor:requestDescriptor];

descriptors
You can add as many descriptors as you wish, and set different parameters for each one of them. For example:

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:responseMapping
                                                                                            method:RKRequestMethodAny
                                                                                       pathPattern:@"user/" // or @"user/:id" for adding a user id variable to the url
                                                                                           keyPath:@"user" // if the User object will be under the "user" field of the response
                                                                                       statusCodes:[NSIndexSet indexSetWithIndex:200]];

If you have different objects with one (or more) distinct parameter distinguishing between them (e.g. Boy object and Girl object, with the “gender” parameter set to “male” or “female” respectively), you can create one dynamic mapping object to handle all of them:

RKDynamicMapping *dynamicMapping = [RKDynamicMapping new];
    [dynamicMapping setObjectMappingForRepresentationBlock:^RKObjectMapping *(id representation) {
        if ([representation isKindOfClass:[NSDictionary class]]) {
            if ([[representation valueForKey:@"gender"] isEqualToString:@"male"]) {
                return boyResponseMapping;
            } else {
                return girlResponseMapping;
            }
        } else {
            return nil;
        }
    }];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:dynamicMapping
                                                                                            method:RKRequestMethodAny
                                                                                       pathPattern:@"user"
                                                                                           keyPath:@"user"
                                                                                       statusCodes:[NSIndexSet indexSetWithIndex:200]];

Now all that we have to do is issue requests to the server. RestKit has wrapped the popular requests into convenience functions with blocks to handle the responses, i.e.:

[[RKObjectManager sharedManager] getObjectsAtPath:@"request-url"] //the request url must not include the server base URL
                         parameters:@{@"key": @"value"} // you can set as many request params as you wish
                            success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                                  // do stuff
                                  // the objects returned will be in the NSArray object mappingResult.array
                                  // the response in string format will be in operation.HTTPRequestOperation.responseString
                                  // the response in NSData format will be in operation.HTTPRequestOperation.responseData
                            }
                            failure:^(RKObjectRequestOperation *operation, NSError *error) {
                                 // do stuff
                            }];

[[RKObjectManager sharedManager] postObject:someObject
                         path:@"request-url"
                   parameters:@{@"key": @"value"}
                      success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                          // do stuff
                      } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                          // do stuff
                      }];

[[RKObjectManager sharedManager] putObject:someObject
                        path:@"request-url"
                  parameters:@{@"key": @"value"}
                     success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                         // do stuff
                     } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                         // do stuff
                     }];

[self.objectManager deleteObject:someObject
                           path:@"request-url"
                     parameters:@"request-url"
                        success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
                            // do stuff
                        } failure:^(RKObjectRequestOperation *operation, NSError *error) {
                            // do stuff
                        }];

That’s it! You have your basic, working, REST client!
For more detailed information, refer to the RestKit homepage, the RestKit GitHub project page, and the RestKit reference docs.

Feel free to ask any questions or share your thoughts in the comments below.
Enjoy! 🙂

4 thoughts on “Implement a REST client in an iOS application using RestKit

  1. I would just like to tell that I really liked your blog post. In fact I am going to bookmark your blog and will regularly visit the site. You come up with such an amazing articles thank you for sharing this your site.

Leave a Reply

Your email address will not be published. Required fields are marked *