This episode covers some additional pieces of RestKit, abstracting network requests even further by providing a route & mapping for a given object and utilizing RKObjectManager to perform the work for us. Instead of using a live API, we verify the behavior using SenTestingKit.
Episode Links Episode Source Code RKObjectManager documentation Our Data Model For this example we will work with a simple domain model representing a wiki. @interface WKCategory : NSObject @property (nonatomic, copy) NSString *name; @end @interface WKWikiPage : NSObject @property (nonatomic, assign) NSInteger pageID; @property (nonatomic, strong) NSString *title; @property (nonatomic, strong) NSString *body; @property (nonatomic, strong) WKSlug *slug; @property (nonatomic, strong) WKCategory *category; @end @interface WKSlug : NSObject @property (nonatomic, copy) NSString *value; @end With this model in place, we can create a simple test class to assert some behaviors. Testing Path Patterns Using a path pattern, we can generate URLs for resources, easily substituting tokens in the pattern with values from keypaths on our objects. - (void)testPathBuilding { WKWikiPage *page = [[WKWikiPage alloc] init]; page.pageID = 15; page.slug = [[WKSlug alloc] init]; page.slug.value = @"my-new-page"; page.category = [[WKCategory alloc] init]; page.category.name = @"RestKit"; NSString *path = RKPathFromPatternWithObject(@"/wiki/:category.name/:pageID/:slug.value\\.html", page); NSLog(@"PATH: %@", path); STAssertEqualObjects(@"/wiki/RestKit/15/my-new-page.html", path, nil); } Here you can see that path segments are used as keypaths in the provided objects in order to construct the desired URL. Testing Routes RKRoute is a class that uses Path Patterns in order to dynamically figure out what URL to use for a given HTTP request. For this test we'll need to provide a hook to allow for intercepting the actual HTTP requests so they are not sent, but also to add assertions on the request sent. @interface TestObjectManager : RKObjectManager @property (nonatomic, strong) NSMutableArray *enqueuedOperations; @end @implementation TestObjectManager - (void)enqueueObjectRequestOperation:(RKObjectRequestOperation *)objectRequestOperation { if (!self.enqueuedOperations) { self.enqueuedOperations = [NSMutableArray array]; } [self.enqueuedOperations addObject:objectRequestOperation]; } @end Now that this is in place, we can use it instead of RKObjectManager when in our tests. The requests will be caught by this method and never sent to super. We can now write our test: - (void)testRouting { RKRoute *route = [RKRoute routeWithClass:[WKWikiPage class] pathPattern:@"pages/:pageID\\.json" method:RKRequestMethodAny]; [self.objectManager.router.routeSet addRoute:route]; WKWikiPage *page = [[WKWikiPage alloc] init]; page.pageID = 15; [self.objectManager getObject:page path:nil parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) { } failure:^(RKObjectRequestOperation *operation, NSError *error) { }]; STAssertEquals(1, (int)self.objectManager.enqueuedOperations.count, nil); RKObjectRequestOperation *op = [self.objectManager.enqueuedOperations objectAtIndex:0]; NSURL *url = op.HTTPRequestOperation.request.URL; NSString *urlString = [url description]; STAssertEqualObjects(@"http://api.example.com/v2/pages/15.json", urlString, nil); } Testing Request Descriptors RKRequestDescriptor can be added to an RKObjectManager to automatically convert an object to it's desired representation for a request. For example if we wanted to create a new WKWikiPage and save it on the server via an HTTP POST, we'd have to convert the page object to a form-encoded or json structure for the request. - (void)testRequestDescriptor { RKRoute *route = [RKRoute routeWithClass:[WKWikiPage class] pathPattern:@"pages/:pageID\\.json" method:RKRequestMethodAny]; [self.objectManager.router.routeSet addRoute:route]; WKWikiPage *page = [[WKWikiPage alloc] init]; page.pageID = 15; page.slug = [[WKSlug alloc] init]; page.slug.value = @"my-new-page"; page.title = @"This is my new page"; page.body = @"This is the body"; page.category = [[WKCategory alloc] init]; page.category.name = @"RestKit"; RKObjectMapping *categoryRequestMapping = [RKObjectMapping requestMapping]; [categoryRequestMapping addAttributeMappingsFromArray:@[@"name"]]; RKObjectMapping *reqMapping = [RKObjectMapping requestMapping]; [reqMapping addAttributeMappingsFromArray:@[@"title", @"body"]]; [reqMapping addAttributeMappingsFromDictionary:@{@"slug.value": @"slug"}]; [reqMapping addRelationshipMappingWithSourceKeyPath:@"category" mapping:categoryRequestMapping]; RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:reqMapping objectClass:[WKWikiPage class] rootKeyPath:@"page"]; [self.objectManager addRequestDescriptor:requestDescriptor]; self.objectManager.requestSerializationMIMEType = RKMIMETypeJSON; [self.objectManager postObject:page path:nil parameters:nil success:nil failure:nil]; STAssertEquals(1, (int)self.objectManager.enqueuedOperations.count, nil); RKObjectRequestOperation *op = [self.objectManager.enqueuedOperations objectAtIndex:0]; NSData *data = op.HTTPRequestOperation.request.HTTPBody; NSString *json = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; NSLog(@"BODY: %@", json); }