mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-05 22:40:21 +08:00
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.
399 lines
14 KiB
Objective-C
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
|