Files
RestKit/Code/ObjectMapping/RKObjectPaginator.m
Blake Watters 6abbb34ef0 Introduced mapping contexts support.
Extended RKObjectMappingProvider to store collections of object mappings for different use cases. The framework
now stores object mappings, serialization mappings, an error mapping and a pagination mapping using the context
support. Contexts can be added to the provider via method calls or extension via a category.
2012-01-20 10:21:19 -05:00

204 lines
6.4 KiB
Objective-C

//
// RKObjectPaginator.m
// RestKit
//
// Created by Blake Watters on 12/29/11.
// Copyright (c) 2011 RestKit. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#import "RKObjectPaginator.h"
#import "RKManagedObjectLoader.h"
#import "RKObjectMappingOperation.h"
#import "SOCKit.h"
#import "RKLog.h"
static NSUInteger RKObjectPaginatorDefaultPerPage = 25;
// Private interface
@interface RKObjectPaginator () <RKObjectLoaderDelegate>
@property (nonatomic, retain) RKManagedObjectLoader *objectLoader;
@end
@implementation RKObjectPaginator
@synthesize patternURL;
@synthesize currentPage;
@synthesize perPage;
@synthesize loaded;
@synthesize pageCount;
@synthesize objectCount;
@synthesize mappingProvider;
@synthesize delegate;
@synthesize objectStore;
@synthesize objectLoader;
@synthesize configurationDelegate;
+ (id)paginatorWithPatternURL:(RKURL *)aPatternURL mappingProvider:(RKObjectMappingProvider *)aMappingProvider {
return [[[self alloc] initWithPatternURL:aPatternURL mappingProvider:aMappingProvider] autorelease];
}
- (id)initWithPatternURL:(RKURL *)aPatternURL mappingProvider:(RKObjectMappingProvider *)aMappingProvider {
self = [super init];
if (self) {
patternURL = [aPatternURL copy];
mappingProvider = [aMappingProvider retain];
currentPage = NSUIntegerMax;
pageCount = NSUIntegerMax;
objectCount = NSUIntegerMax;
perPage = RKObjectPaginatorDefaultPerPage;
loaded = NO;
}
return self;
}
- (void)dealloc {
delegate = nil;
configurationDelegate = nil;
objectLoader.delegate = nil;
[patternURL release];
patternURL = nil;
[mappingProvider release];
mappingProvider = nil;
[objectStore release];
objectStore = nil;
[objectLoader cancel];
objectLoader.delegate = nil;
[objectLoader release];
objectLoader = nil;
[super dealloc];
}
- (RKObjectMapping *)paginationMapping {
return [mappingProvider paginationMapping];
}
- (RKURL *)URL {
return [patternURL URLByInterpolatingResourcePathWithObject:self];
}
// Private. Public consumers can rely on isLoaded
- (BOOL)hasCurrentPage {
return currentPage != NSUIntegerMax;
}
- (BOOL)hasPageCount {
return pageCount != NSUIntegerMax;
}
- (BOOL)hasObjectCount {
return objectCount != NSUIntegerMax;
}
- (NSUInteger)currentPage {
// Referenced during initial load, so we don't rely on isLoaded.
NSAssert([self hasCurrentPage], @"Current page has not been initialized.");
return currentPage;
}
- (NSUInteger)pageCount {
NSAssert([self hasPageCount], @"Page count not available.");
return pageCount;
}
- (BOOL)hasNextPage {
NSAssert(self.isLoaded, @"Cannot determine hasNextPage: paginator is not loaded.");
NSAssert([self hasPageCount], @"Cannot determine hasNextPage: page count is not known.");
return self.currentPage < self.pageCount;
}
- (BOOL)hasPreviousPage {
NSAssert(self.isLoaded, @"Cannot determine hasPreviousPage: paginator is not loaded.");
return self.currentPage > 1;
}
#pragma mark - RKObjectLoaderDelegate methods
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects {
self.objectLoader = nil;
loaded = YES;
RKLogInfo(@"Loaded objects: %@", objects);
[self.delegate paginator:self didLoadObjects:objects forPage:self.currentPage];
if ([self hasPageCount] && self.currentPage == 1) {
if ([self.delegate respondsToSelector:@selector(paginatorDidLoadFirstPage:)]) {
[self.delegate paginatorDidLoadFirstPage:self];
}
}
if ([self hasPageCount] && self.currentPage == self.pageCount) {
if ([self.delegate respondsToSelector:@selector(paginatorDidLoadLastPage:)]) {
[self.delegate paginatorDidLoadLastPage:self];
}
}
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error {
RKLogError(@"Paginator error %@", error);
[self.delegate paginator:self didFailWithError:error objectLoader:self.objectLoader];
self.objectLoader = nil;
}
- (void)objectLoader:(RKObjectLoader *)loader willMapData:(inout id *)mappableData {
NSError *error = nil;
RKObjectMappingOperation *mappingOperation = [RKObjectMappingOperation mappingOperationFromObject:*mappableData toObject:self withMapping:[self paginationMapping]];
BOOL success = [mappingOperation performMapping:&error];
if (!success) {
pageCount = currentPage = 0;
RKLogError(@"Paginator didn't map info to compute page count. Assuming no pages.");
} else if (self.perPage && [self hasObjectCount]) {
float objectCountFloat = self.objectCount;
pageCount = ceilf(objectCountFloat / self.perPage);
RKLogInfo(@"Paginator objectCount: %d pageCount: %d", self.objectCount, self.pageCount);
} else {
NSAssert(NO, @"Paginator perPage set is 0.");
RKLogError(@"Paginator perPage set is 0.");
}
}
#pragma mark - Action methods
- (void)loadNextPage {
[self loadPage:currentPage + 1];
}
- (void)loadPreviousPage {
[self loadPage:currentPage - 1];
}
- (void)loadPage:(NSUInteger)pageNumber {
NSAssert(self.mappingProvider, @"Cannot perform a load with a nil mappingProvider.");
NSAssert(! objectLoader, @"Cannot perform a load while one is already in progress.");
currentPage = pageNumber;
self.objectLoader = [[[RKManagedObjectLoader alloc] initWithURL:self.URL mappingProvider:self.mappingProvider objectStore:self.objectStore] autorelease];
if ([self.configurationDelegate respondsToSelector:@selector(configureObjectLoader:)]) {
[self.configurationDelegate configureObjectLoader:objectLoader];
}
self.objectLoader.method = RKRequestMethodGET;
self.objectLoader.delegate = self;
if ([self.delegate respondsToSelector:@selector(paginator:willLoadPage:objectLoader:)]) {
[self.delegate paginator:self willLoadPage:pageNumber objectLoader:self.objectLoader];
}
[self.objectLoader send];
}
@end