Introduce a new heuristic based approach for determining if a response can skip the mapping process. Also introduces a new Network + CoreData logging component and reduces the chattiness of the debug logging level for Core Data Network events.

This commit is contained in:
Blake Watters
2013-03-07 19:25:01 -05:00
parent 02247695b1
commit a9ef1fe39d
4 changed files with 48 additions and 7 deletions

View File

@@ -35,13 +35,12 @@
// Set Logging Component
#undef RKLogComponent
#define RKLogComponent RKlcl_cRestKitCoreData
#define RKLogComponent RKlcl_cRestKitNetworkCoreData
@interface RKEntityMappingEvent : NSObject
@property (nonatomic, copy) id rootKey;
@property (nonatomic, copy) NSString *keyPath;
@property (nonatomic, strong) RKEntityMapping *entityMapping;
@property (nonatomic, readonly) BOOL canSkipMapping;
+ (instancetype)eventWithRootKey:(id)rootKey keyPath:(NSString *)keyPath entityMapping:(RKEntityMapping *)entityMapping;
@end
@@ -299,6 +298,8 @@ static NSURL *RKRelativeURLFromURLAndResponseDescriptors(NSURL *URL, NSArray *re
@property (nonatomic, strong, readwrite) RKMappingResult *mappingResult;
@property (nonatomic, copy) id (^willMapDeserializedResponseBlock)(id deserializedResponseBody);
@property (nonatomic, strong) NSArray *entityMappingEvents;
@property (nonatomic, strong) NSCachedURLResponse *cachedResponse;
@end
@implementation RKManagedObjectRequestOperation
@@ -312,6 +313,7 @@ static NSURL *RKRelativeURLFromURLAndResponseDescriptors(NSURL *URL, NSArray *re
if (self) {
self.savesToPersistentStore = YES;
self.deletesOrphanedObjects = YES;
self.cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:requestOperation.request];
}
return self;
}
@@ -372,14 +374,31 @@ static NSURL *RKRelativeURLFromURLAndResponseDescriptors(NSURL *URL, NSArray *re
// RKResponseHasBeenMappedCacheUserInfoKey is stored by RKObjectRequestOperation
- (BOOL)canSkipMapping
{
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:self.HTTPRequestOperation.request];
// Is the request cacheable
if (!self.cachedResponse) return NO;
NSURLRequest *request = self.HTTPRequestOperation.request;
if (! [[request HTTPMethod] isEqualToString:@"GET"] && ! [[request HTTPMethod] isEqualToString:@"HEAD"]) return NO;
NSHTTPURLResponse *response = (NSHTTPURLResponse *)self.HTTPRequestOperation.response;
if (! [RKCacheableStatusCodes() containsIndex:response.statusCode]) return NO;
// Check for a change in the Etag
NSString *cachedEtag = [[(NSHTTPURLResponse *)[self.cachedResponse response] allHeaderFields] objectForKey:@"Etag"];
NSString *responseEtag = [[response allHeaderFields] objectForKey:@"Etag"];
if (! [cachedEtag isEqualToString:responseEtag]) return NO;
// Response data has changed
NSData *responseData = self.HTTPRequestOperation.responseData;
if (! [responseData isEqualToData:[self.cachedResponse data]]) return NO;
// Check that we have mapped this response previously
NSCachedURLResponse *cachedResponse = [[NSURLCache sharedURLCache] cachedResponseForRequest:request];
return [[cachedResponse.userInfo objectForKey:RKResponseHasBeenMappedCacheUserInfoKey] boolValue];
}
- (RKMappingResult *)performMappingOnResponse:(NSError **)error
{
if (self.canSkipMapping) {
RKLogDebug(@"Managed object mapping requested for cached response: skipping mapping...");
if ([self canSkipMapping]) {
RKLogDebug(@"Managed object mapping requested for cached response which was previously mapped: skipping...");
NSURL *URL = RKRelativeURLFromURLAndResponseDescriptors(self.HTTPRequestOperation.response.URL, self.responseDescriptors);
NSArray *fetchRequests = RKArrayOfFetchRequestFromBlocksWithURL(self.fetchRequestBlocks, URL);
NSMutableArray *managedObjects = [NSMutableArray array];
@@ -470,7 +489,7 @@ static NSURL *RKRelativeURLFromURLAndResponseDescriptors(NSURL *URL, NSArray *re
RKLogTrace(@"Fetched local objects matching URL '%@' with fetch request '%@': %@", URL, fetchRequest, _blockObjects);
[localObjects addObjectsFromArray:_blockObjects];
} else {
RKLogDebug(@"Fetch request block %@ returned nil fetch request for URL: '%@'", fetchRequestBlock, URL);
RKLogTrace(@"Fetch request block %@ returned nil fetch request for URL: '%@'", fetchRequestBlock, URL);
}
}
@@ -489,7 +508,7 @@ static NSURL *RKRelativeURLFromURLAndResponseDescriptors(NSURL *URL, NSArray *re
return YES;
}
if (self.canSkipMapping) {
if ([self canSkipMapping]) {
RKLogDebug(@"Skipping deletion of orphaned objects: 304 (Not Modified) status code encountered");
return YES;
}

View File

@@ -78,6 +78,13 @@ NSRange RKStatusCodeRangeForClass(RKStatusCodeClass statusCodeClass);
*/
NSIndexSet *RKStatusCodeIndexSetForClass(RKStatusCodeClass statusCodeClass);
/**
Creates and returns a new index set including all HTTP response status codes that are cacheable.
@return A new index set containing all cacheable status codes.
*/
NSIndexSet *RKCacheableStatusCodes();
/**
Returns string representation of a given HTTP status code.

View File

@@ -32,6 +32,20 @@ NSIndexSet *RKStatusCodeIndexSetForClass(RKStatusCodeClass statusCodeClass)
return [NSIndexSet indexSetWithIndexesInRange:RKStatusCodeRangeForClass(statusCodeClass)];
}
NSIndexSet *RKCacheableStatusCodes()
{
NSMutableIndexSet *cacheableStatusCodes = [NSMutableIndexSet indexSet];
[cacheableStatusCodes addIndex:200];
[cacheableStatusCodes addIndex:304];
[cacheableStatusCodes addIndex:203];
[cacheableStatusCodes addIndex:300];
[cacheableStatusCodes addIndex:301];
[cacheableStatusCodes addIndex:302];
[cacheableStatusCodes addIndex:307];
[cacheableStatusCodes addIndex:410];
return cacheableStatusCodes;
}
NSString *RKStringFromRequestMethod(RKRequestMethod method)
{
switch (method) {

View File

@@ -54,6 +54,7 @@ _RKlcl_component(RestKit, "restkit",
_RKlcl_component(RestKitCoreData, "restkit.core_data", "RestKit/CoreData")
_RKlcl_component(RestKitCoreDataCache, "restkit.core_data.cache", "RestKit/CoreData/Cache")
_RKlcl_component(RestKitNetwork, "restkit.network", "RestKit/Network")
_RKlcl_component(RestKitNetworkCoreData, "restkit.network.core_data", "RestKit/Network/CoreData")
_RKlcl_component(RestKitObjectMapping, "restkit.object_mapping", "RestKit/ObjectMapping")
_RKlcl_component(RestKitSearch, "restkit.search", "RestKit/Search")
_RKlcl_component(RestKitSupport, "restkit.support", "RestKit/Support")