





















































(For more resources related to this topic, see here.)
Let's take a base URL:
NSURL *baseURL = [NSURL URLWithString:@"http://example.com/v1/"];
Now:
[NSURL URLWithString:@"foo" relativeToURL:baseURL]; // Will give us http://example.com/v1/foo [NSURL URLWithString:@"foo?bar=baz" relativeToURL:baseURL]; // -> http://example.com/v1/foo?bar=baz [NSURL URLWithString:@"/foo" relativeToURL:baseURL]; // -> http://example.com/foo [NSURL URLWithString:@"foo/" relativeToURL:baseURL]; // -> http://example.com/v1/foo [NSURL URLWithString:@"/foo/" relativeToURL:baseURL]; // -> http://example.com/foo/ [NSURL URLWithString:@"http://example2.com/" relativeToURL:baseURL]; // -> http://example2.com/
Having the knowledge of what an object manager is, let's try to apply it in a real-life example.
Before proceeding, it is highly recommend that we check the actual documentation on REST API of MongoHQ. The current one is at the following link:
http://support.mongohq.com/mongohq-api/introduction.html
As there are no strict rules on REST API, every API is different and does a number of things in its own way. MongoHQ API is not an exception. In addition, it is currently in "beta" stage.
Some of the non-standard things one can find in it are as follows:
To use the API, one will need a valid API Key. You can easily get one for free following a simple guideline recommended by the MongoHQ team:
API Token on Account Info page
So let's set up our configuration using the following steps:
You can use the AppDelegate class for putting the code, while I recommend using a separate MongoHqApi class for such App/API logic separation.
First, let's set up our object manager with the following code:
- (void)setupObjectManager { NSString *baseUrl = @"https://api.mongohq.com"; AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:
[NSURL URLWithString:baseUrl]]; NSString *apiKey = @"MY_API_KEY"; [httpClient setDefaultHeader:@"MongoHQ-API-Token" value:apiKey]; RKObjectManager *manager = [[RKObjectManager alloc]
initWithHTTPClient:httpClient]; [RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class]
forMIMEType:@"text/plain"]; [manager.HTTPClient registerHTTPOperationClass:
[AFJSONRequestOperation class]]; [manager setAcceptHeaderWithMIMEType:RKMIMETypeJSON]; manager.requestSerializationMIMEType = RKMIMETypeJSON; [RKObjectManager setSharedManager:manager]; }
NSString *baseUrl = @"https://api.mongohq.com";
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:[NSURL
URLWithString:baseUrl]];
NSString *apiKey = @"MY_API_KEY"; [httpClient setDefaultHeader:@"MongoHQ-API-Token" value:apiKey];
For the real-world app, one can show an Enter Api Key view controller to the user, and use a NSUserDefaults or a keychain to store and retrieve it.
RKObjectManager *manager = [[RKObjectManager alloc]
initWithHTTPClient:httpClient];
[RKMIMETypeSerialization registerClass:[RKNSJSONSerialization class]
forMIMEType:@"text/plain"];
[manager.HTTPClient registerHTTPOperationClass:[AFJSONRequestOperation
class]];
[manager setAcceptHeaderWithMIMEType:RKMIMETypeJSON];
manager.requestSerializationMIMEType = RKMIMETypeJSON;
[RKObjectManager setSharedManager:manager];
Next, we want to query our databases. Let's first see how a database request will show us the output in JSON. To check this, go to http://api.mongohq.com/databases?_apikey=YOUR_API_KEY in your web browser YOUR_API_KEY. If a JSON-formatter extension (https://github.com/rfletcher/safari-json-formatter) is installed in your Safari browser, you will probably see the output shown in the following screenshot.
JSON response from API
As we see, the JSON representation of one database is:
[ { "hostname": "sandbox.mongohq.com", "name": "Test", "plan": "Sandbox", "port": 10097, "shared": true } ]
Therefore, our possible MDatabase class could look like:
@interface MDatabase : NSObject @property (nonatomic, strong) NSString *name; @property (nonatomic, strong) NSString *plan; @property (nonatomic, strong) NSString *hostname; @property (nonatomic, strong) NSNumber *port; @end
We can also modify the @implementation section to override the description method, which will help us while debugging the application and printing the object:
// in @implementation MDatabase - (NSString *)description { return [NSString stringWithFormat:@"%@ on %@ @ %@:%@", self.name, self.plan, self.hostname, self.port]; }
Now let's set up a mapping for it:
- (void)setupDatabaseMappings { RKObjectManager *manager = [RKObjectManager sharedManager]; Class itemClass = [MDatabase class]; NSString *itemsPath = @"/databases"; RKObjectMapping *mapping = [RKObjectMapping
mappingForClass:itemClass]; [mapping addAttributeMappingsFromArray:@[@"name", @"plan",
@"hostname", @"port"]]; NSString *keyPath = nil; NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass
(RKStatusCodeClassSuccessful); RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodGET pathPattern:itemsPath keyPath:keyPath statusCodes:statusCodes]; [manager addResponseDescriptor:responseDescriptor]; }
Let's look at the mapping setup line by line:
Class itemClass = [MDatabase class];
NSString *itemsPath = @"/databases";
RKObjectMapping *mapping = [RKObjectMapping mappingForClass:itemClass];
[mapping addAttributeMappingsFromArray:@[@"name", @"plan", @"hostname",
@"port"]];
NSString *keyPath = nil;
NSIndexSet *statusCodes = RKStatusCodeIndexSetForClass
(RKStatusCodeClassSuccessful);
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:mapping method:RKRequestMethodGET pathPattern:itemsPath keyPath:keyPath statusCodes:statusCodes];
RKObjectManager *manager = [RKObjectManager sharedManager]; [manager addResponseDescriptor:responseDescriptor];
Sometimes, depending on the architectural decision, it's nicer to put the mapping definition as part of a model object, and later call it like [MDatabase mapping], but for the sake of simplicity, we will put the mapping in line with RestKit configuration.
The actual code that loads the database list will look like:
RKObjectManager *manager = [RKObjectManager sharedManager]; [manager getObjectsAtPath:@"/databases" parameters:nil success:^(RKObjectRequestOperation *operation,
RKMappingResult *mappingResult) { NSLog(@"Loaded databases: %@", [mappingResult array]); } failure:^(RKObjectRequestOperation *operation,
NSError *error) { NSLog(@"Error: %@", [error localizedDescription]) }];
As you may have noticed, the method is quite simple to use and it uses block-based APIs for callbacks, which greatly improves the code readability, compared to using delegates, especially if there is more than one network request in a class. A possible implementation of a table view that loads and shows the list of databases will look like the following screenshot:
View of loaded Database items
In this article, we learned how to set up the RestKit library to work for our web service, we talked about sending requests, getting responses, and how to do object manipulations. We also talked about simplifying the requests by introducing routing. In addition, we discussed how integration with UI can be done and created forms.
Further resources on this subject: