Files
RestKit/Code/ObjectMapping/RKObjectManager.m
Ray Fix 3d03959d06 Polish RKObjectPaginator.
Initialization
I need the object paginator's object loaders to 
know about the various header settings of 
the RKClient when it constructs them.  However,
the paginator should not depend on an RKObjectManager.
The proposed solution is to pass a block that lets
an outsider custom configure the object loaders
that get created.  The object manager shows how
it is done.

Error Handling
We don't want to have a separate set of error handling
if using a paginator versus using an RKObjectLoader.
When an error is encountered, the paginator should 
allow access to the underlying RKObjectRequest that
produced the error.

Cleanup
The contained object loader needs to nil out the delegate
before going away.

NSLogs changed to RKLog

Pagination computation
The page count can be computed when the pagination 
parameters are mapped.  Note you must use the ceil
operation to compute this value.  If there are 3.1 pages
that means there are 4 pages.
2012-01-20 10:21:18 -05:00

399 lines
14 KiB
Objective-C

//
// RKObjectManager.m
// RestKit
//
// Created by Jeremy Ellison on 8/14/09.
// Copyright 2009 Two Toasters
//
// 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 "RKObjectManager.h"
#import "RKObjectSerializer.h"
#import "RKManagedObjectStore.h"
#import "RKManagedObjectLoader.h"
#import "Support.h"
#import "RKErrorMessage.h"
NSString* const RKDidEnterOfflineModeNotification = @"RKDidEnterOfflineModeNotification";
NSString* const RKDidEnterOnlineModeNotification = @"RKDidEnterOnlineModeNotification";
//////////////////////////////////
// Shared Instance
static RKObjectManager* sharedManager = nil;
///////////////////////////////////
@implementation RKObjectManager
@synthesize client = _client;
@synthesize objectStore = _objectStore;
@synthesize router = _router;
@synthesize mappingProvider = _mappingProvider;
@synthesize serializationMIMEType = _serializationMIMEType;
@synthesize inferMappingsFromObjectTypes = _inferMappingsFromObjectTypes;
- (id)initWithBaseURL:(NSString*)baseURL {
self = [super init];
if (self) {
_mappingProvider = [RKObjectMappingProvider new];
_router = [RKObjectRouter new];
_client = [[RKClient clientWithBaseURL:baseURL] retain];
_onlineState = RKObjectManagerOnlineStateUndetermined;
_inferMappingsFromObjectTypes = NO;
self.acceptMIMEType = RKMIMETypeJSON;
self.serializationMIMEType = RKMIMETypeFormURLEncoded;
// Setup default error message mappings
RKObjectMapping* errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
[errorMapping mapKeyPath:@"" toAttribute:@"errorMessage"];
[_mappingProvider setMapping:errorMapping forKeyPath:@"error"];
[_mappingProvider setMapping:errorMapping forKeyPath:@"errors"];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reachabilityChanged:)
name:RKReachabilityDidChangeNotification
object:_client.reachabilityObserver];
}
return self;
}
+ (RKObjectManager*)sharedManager {
return sharedManager;
}
+ (void)setSharedManager:(RKObjectManager*)manager {
[manager retain];
[sharedManager release];
sharedManager = manager;
}
+ (RKObjectManager*)objectManagerWithBaseURL:(NSString*)baseURL {
RKObjectManager* manager = [[[self alloc] initWithBaseURL:baseURL] autorelease];
if (nil == sharedManager) {
[RKObjectManager setSharedManager:manager];
}
return manager;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[_router release];
_router = nil;
[_client release];
_client = nil;
[_objectStore release];
_objectStore = nil;
[_serializationMIMEType release];
_serializationMIMEType = nil;
[_mappingProvider release];
_mappingProvider = nil;
[super dealloc];
}
- (BOOL)isOnline {
return (_onlineState == RKObjectManagerOnlineStateConnected);
}
- (BOOL)isOffline {
return ![self isOnline];
}
- (void)reachabilityChanged:(NSNotification*)notification {
BOOL isHostReachable = [self.client.reachabilityObserver isNetworkReachable];
_onlineState = isHostReachable ? RKObjectManagerOnlineStateConnected : RKObjectManagerOnlineStateDisconnected;
if (isHostReachable) {
[[NSNotificationCenter defaultCenter] postNotificationName:RKDidEnterOnlineModeNotification object:self];
} else {
[[NSNotificationCenter defaultCenter] postNotificationName:RKDidEnterOfflineModeNotification object:self];
}
}
- (void)setAcceptMIMEType:(NSString*)MIMEType {
[_client setValue:MIMEType forHTTPHeaderField:@"Accept"];
}
- (NSString*)acceptMIMEType {
return [self.client.HTTPHeaders valueForKey:@"Accept"];
}
/////////////////////////////////////////////////////////////
#pragma mark - Object Collection Loaders
- (Class)objectLoaderClass {
Class managedObjectLoaderClass = NSClassFromString(@"RKManagedObjectLoader");
if (self.objectStore && managedObjectLoaderClass) {
return managedObjectLoaderClass;
}
return [RKObjectLoader class];
}
- (id)loaderWithResourcePath:(NSString *)resourcePath {
RKURL *URL = [RKURL URLWithBaseURLString:self.client.baseURL resourcePath:resourcePath];
return [self loaderWithURL:URL];
}
- (id)loaderWithURL:(NSURL *)URL {
RKObjectLoader *loader = [[self objectLoaderClass] loaderWithURL:URL mappingProvider:self.mappingProvider];
if ([loader isKindOfClass:[RKManagedObjectLoader class]]) {
[(RKManagedObjectLoader *)loader setObjectStore:self.objectStore];
}
[self.client setupRequest:loader];
return loader;
}
- (NSURL *)baseURL {
// TODO: Turn RKClient baseURL into an NSURL and proxy...
return [NSURL URLWithString:self.client.baseURL];
}
- (RKObjectPaginator *)paginatorWithResourcePathPattern:(NSString *)resourcePathPattern {
return [RKObjectPaginator paginatorWithBaseURL:[self baseURL]
resourcePathPattern:resourcePathPattern
mappingProvider:self.mappingProvider
customObjectLoaderSetup:^(RKObjectLoader *loader) {
[self.client setupRequest:loader];
}];
}
- (id)loaderForObject:(id<NSObject>)object method:(RKRequestMethod)method {
NSString* resourcePath = [self.router resourcePathForObject:object method:method];
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
loader.method = method;
loader.sourceObject = object;
loader.targetObject = object;
loader.serializationMIMEType = self.serializationMIMEType;
loader.serializationMapping = [self.mappingProvider serializationMappingForClass:[object class]];
if (self.inferMappingsFromObjectTypes) {
RKObjectMapping* objectMapping = [self.mappingProvider objectMappingForClass:[object class]];
RKLogDebug(@"Auto-selected object mapping %@ for object of type %@", objectMapping, NSStringFromClass([object class]));
loader.objectMapping = objectMapping;
}
return loader;
}
- (RKObjectLoader*)objectLoaderWithResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* objectLoader = nil;
Class managedObjectLoaderClass = NSClassFromString(@"RKManagedObjectLoader");
if (self.objectStore && managedObjectLoaderClass) {
objectLoader = [managedObjectLoaderClass loaderWithResourcePath:resourcePath objectManager:self delegate:delegate];
} else {
// TODO: add newPaginator and newObjectLoader methods to RKObjectManager???
objectLoader = [RKObjectLoader loaderWithResourcePath:resourcePath objectManager:self delegate:delegate];
}
return objectLoader;
}
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
loader.delegate = delegate;
loader.method = RKRequestMethodGET;
[loader send];
return loader;
}
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath objectMapping:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
loader.delegate = delegate;
loader.method = RKRequestMethodGET;
loader.objectMapping = objectMapping;
[loader send];
return loader;
}
/////////////////////////////////////////////////////////////
#pragma mark - Object Instance Loaders
- (RKObjectLoader*)objectLoaderForObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader *loader = [self loaderForObject:object method:method];
loader.delegate = delegate;
return loader;
}
- (RKObjectLoader*)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodGET];
loader.delegate = delegate;
[loader send];
return loader;
}
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodPOST];
loader.delegate = delegate;
[loader send];
return loader;
}
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodPUT];
loader.delegate = delegate;
[loader send];
return loader;
}
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodDELETE];
loader.delegate = delegate;
[loader send];
return loader;
}
#if NS_BLOCKS_AVAILABLE
#pragma mark - Block Configured Object Loaders
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
loader.delegate = delegate;
loader.method = RKRequestMethodGET;
// Yield to the block for setup
block(loader);
[loader send];
return loader;
}
- (RKObjectLoader*)sendObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
RKObjectLoader* loader = [self loaderWithResourcePath:nil];
loader.delegate = delegate;
loader.sourceObject = object;
loader.targetObject = object;
loader.serializationMIMEType = self.serializationMIMEType;
loader.serializationMapping = [self.mappingProvider serializationMappingForClass:[object class]];
// Yield to the block for setup
block(loader);
if (loader.resourcePath == nil) {
loader.resourcePath = [self.router resourcePathForObject:object method:loader.method];
}
if (loader.objectMapping == nil) {
if (self.inferMappingsFromObjectTypes) {
RKObjectMapping* objectMapping = [self.mappingProvider objectMappingForClass:[object class]];
RKLogDebug(@"Auto-selected object mapping %@ for object of type %@", objectMapping, NSStringFromClass([object class]));
loader.objectMapping = objectMapping;
}
}
[loader send];
return loader;
}
- (RKObjectLoader*)sendObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object delegate:delegate block:^(RKObjectLoader* loader) {
loader.method = method;
block(loader);
}];
}
- (RKObjectLoader*)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodGET delegate:delegate block:block];
}
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodPOST delegate:delegate block:block];
}
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodPUT delegate:delegate block:block];
}
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodDELETE delegate:delegate block:block];
}
#endif // NS_BLOCKS_AVAILABLE
#pragma mark - Object Instance Loaders for Non-nested JSON
- (RKObjectLoader*)getObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodGET];
loader.delegate = delegate;
if ([object isMemberOfClass:[objectMapping objectClass]]) {
loader.targetObject = object;
} else {
loader.targetObject = nil;
}
loader.objectMapping = objectMapping;
[loader send];
return loader;
}
- (RKObjectLoader*)postObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodPOST];
loader.delegate = delegate;
if ([object isMemberOfClass:[objectMapping objectClass]]) {
loader.targetObject = object;
} else {
loader.targetObject = nil;
}
loader.objectMapping = objectMapping;
[loader send];
return loader;
}
- (RKObjectLoader*)putObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodPUT];
loader.delegate = delegate;
if ([object isMemberOfClass:[objectMapping objectClass]]) {
loader.targetObject = object;
} else {
loader.targetObject = nil;
}
loader.objectMapping = objectMapping;
[loader send];
return loader;
}
- (RKObjectLoader*)deleteObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderForObject:object method:RKRequestMethodDELETE];
loader.delegate = delegate;
if ([object isMemberOfClass:[objectMapping objectClass]]) {
loader.targetObject = object;
} else {
loader.targetObject = nil;
}
loader.objectMapping = objectMapping;
[loader send];
return loader;
}
- (RKRequestCache *)requestCache {
return self.client.requestCache;
}
- (RKRequestQueue *)requestQueue {
return self.client.requestQueue;
}
@end