Refactored object manager API's to de-emphasize delegates and removal duplication of blocks and delegates. Added block callbacks to RKObjectLoader.

This commit is contained in:
Blake Watters
2012-01-19 09:09:56 -05:00
parent 4bd12ea987
commit 1d27f7bbbe
12 changed files with 592 additions and 367 deletions

View File

@@ -25,6 +25,22 @@
@class RKObjectMappingProvider;
@class RKObjectLoader;
// Block Types
typedef void(^RKObjectLoaderBlock)(RKObjectLoader *loader);
typedef void(^RKObjectLoaderDidFailWithErrorBlock)(NSError *error);
typedef void(^RKObjectLoaderDidLoadObjectsBlock)(NSArray *objects);
typedef void(^RKObjectLoaderDidLoadObjectBlock)(id object);
typedef void(^RKObjectLoaderDidLoadObjectsDictionaryBlock)(NSDictionary *dictionary);
/**
The delegate of an RKObjectLoader object must adopt the RKObjectLoaderDelegate protocol. Optional
methods of the protocol allow the delegate to handle asynchronous object mapping operations performed
by the object loader. Also note that the RKObjectLoaderDelegate protocol incorporates the
RKRequestDelegate protocol and the delegate may provide implementations of methods from RKRequestDelegate
as well.
@see RKRequestDelegate
*/
@protocol RKObjectLoaderDelegate <RKRequestDelegate>
@required
@@ -32,7 +48,7 @@
/**
* Sent when an object loaded failed to load the collection due to an error
*/
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error;
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error;
@optional
@@ -41,7 +57,7 @@
and loaded a collection of objects. All objects mapped from the remote payload will be returned
as a single array.
*/
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray*)objects;
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects;
/**
When implemented, sent to the delegate when the object loader has completed succesfully.
@@ -49,19 +65,19 @@
in the collection will be sent with this delegate method. This method simplifies things
when you know you are working with a single object reference.
*/
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object;
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObject:(id)object;
/**
When implemented, sent to the delegate when an object loader has completed successfully. The
dictionary will be expressed as pairs of keyPaths and objects mapped from the payload. This
method is useful when you have multiple root objects and want to differentiate them by keyPath.
*/
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjectDictionary:(NSDictionary*)dictionary;
- (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjectDictionary:(NSDictionary *)dictionary;
/**
Invoked when the object loader has finished loading
*/
- (void)objectLoaderDidFinishLoading:(RKObjectLoader*)objectLoader;
- (void)objectLoaderDidFinishLoading:(RKObjectLoader *)objectLoader;
/**
Sent when an object loader encounters a response status code or MIME Type that RestKit does not know how to handle.
@@ -86,7 +102,7 @@
@optional
*/
- (void)objectLoaderDidLoadUnexpectedResponse:(RKObjectLoader*)objectLoader;
- (void)objectLoaderDidLoadUnexpectedResponse:(RKObjectLoader *)objectLoader;
/**
Invoked just after parsing has completed, but before object mapping begins. This can be helpful
@@ -97,7 +113,7 @@
Note that the mappable data is a pointer to a pointer to allow you to replace the mappable data
with a new object to be mapped. You must dereference it to access the value.
*/
- (void)objectLoader:(RKObjectLoader*)loader willMapData:(inout id *)mappableData;
- (void)objectLoader:(RKObjectLoader *)loader willMapData:(inout id *)mappableData;
@end
@@ -119,6 +135,49 @@
NSObject* _targetObject;
}
/**
The object that acts as the delegate of the receiving object loader.
@see RKRequestDelegate
*/
@property (nonatomic, assign) id<RKObjectLoaderDelegate> delegate;
/**
The block to invoke when the object loader fails due to an error.
@see [RKObjectLoaderDelegate objectLoader:didFailWithError:]
*/
@property (nonatomic, copy) RKObjectLoaderDidFailWithErrorBlock onDidFailWithError;
/**
The block to invoke when the object loader has completed object mapping and the consumer
wishes to retrieve a single object from the mapping result.
@see [RKObjectLoaderDelegate objectLoader:didLoadObject:]
@see RKObjectMappingResult
*/
@property (nonatomic, copy) RKObjectLoaderDidLoadObjectBlock onDidLoadObject;
/**
The block to invoke when the object loader has completed object mapping and the consumer
wishes to retrieve an collections of objects from the mapping result.
@see [RKObjectLoaderDelegate objectLoader:didLoadObjects:]
@see RKObjectMappingResult
*/
@property (nonatomic, copy) RKObjectLoaderDidLoadObjectsBlock onDidLoadObjects;
/**
The block to invoke when the object loader has completed object mapping and the consumer
wishes to retrieve the entire mapping result as a dictionary. Each key within the
dictionary will correspond to a mapped keyPath within the source JSON/XML and the value
will be the object mapped result.
@see [RKObjectLoaderDelegate objectLoader:didLoadObjects:]
@see RKObjectMappingResult
*/
@property (nonatomic, copy) RKObjectLoaderDidLoadObjectsDictionaryBlock onDidLoadObjectsDictionary;
/**
* The object mapping to use when processing the response. If this is nil,
* then RestKit will search the parsed response body for mappable keyPaths and
@@ -129,7 +188,7 @@
* @default nil
* @see RKObjectMappingProvider
*/
@property (nonatomic, retain) RKObjectMapping* objectMapping;
@property (nonatomic, retain) RKObjectMapping *objectMapping;
/**
A mapping provider containing object mapping configurations for mapping remote
@@ -142,14 +201,14 @@
/**
* The underlying response object for this loader
*/
@property (nonatomic, readonly) RKResponse* response;
@property (nonatomic, readonly) RKResponse *response;
/**
* The mapping result that was produced after the request finished loading and
* object mapping has completed. Provides access to the final products of the
* object mapper in a variety of formats.
*/
@property (nonatomic, readonly) RKObjectMappingResult* result;
@property (nonatomic, readonly) RKObjectMappingResult *result;
///////////////////////////////////////////////////////////////////////////////////////////
// Serialization
@@ -160,7 +219,7 @@
*
* @see RKObjectMappingProvider
*/
@property (nonatomic, retain) RKObjectMapping* serializationMapping;
@property (nonatomic, retain) RKObjectMapping *serializationMapping;
/**
* The MIME Type to serialize the targetObject into according to the mapping
@@ -169,7 +228,7 @@
*
* @see RKMIMEType
*/
@property (nonatomic, retain) NSString* serializationMIMEType;
@property (nonatomic, retain) NSString *serializationMIMEType;
/**
The object being serialized for transport. This object will be transformed into a
@@ -177,14 +236,14 @@
@see RKObjectSerializer
*/
@property (nonatomic, retain) NSObject* sourceObject;
@property (nonatomic, retain) NSObject *sourceObject;
/**
* The target object to map results back onto. If nil, a new object instance
* for the appropriate mapping will be created. If not nil, the results will
* be used to update the targetObject's attributes and relationships.
*/
@property (nonatomic, retain) NSObject* targetObject;
@property (nonatomic, retain) NSObject *targetObject;
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -47,6 +47,10 @@
@synthesize serializationMapping = _serializationMapping;
@synthesize serializationMIMEType = _serializationMIMEType;
@synthesize sourceObject = _sourceObject;
@synthesize onDidFailWithError;
@synthesize onDidLoadObject;
@synthesize onDidLoadObjects;
@synthesize onDidLoadObjectsDictionary;
+ (id)loaderWithURL:(RKURL *)URL mappingProvider:(RKObjectMappingProvider *)mappingProvider {
return [[[self alloc] initWithURL:URL mappingProvider:mappingProvider] autorelease];
@@ -75,7 +79,17 @@
[_result release];
_result = nil;
[_serializationMIMEType release];
_serializationMIMEType = nil;
[_serializationMapping release];
_serializationMapping = nil;
[onDidFailWithError release];
onDidFailWithError = nil;
[onDidLoadObject release];
onDidLoadObject = nil;
[onDidLoadObjects release];
onDidLoadObjects = nil;
[onDidLoadObjectsDictionary release];
onDidLoadObjectsDictionary = nil;
[super dealloc];
}
@@ -88,6 +102,14 @@
_result = nil;
}
- (void)informDelegateOfError:(NSError *)error {
[(NSObject<RKObjectLoaderDelegate>*)_delegate objectLoader:self didFailWithError:error];
if (self.onDidFailWithError) {
self.onDidFailWithError(error);
}
}
#pragma mark - Response Processing
// NOTE: This method is significant because the notifications posted are used by
@@ -121,19 +143,34 @@
NSAssert([NSThread isMainThread], @"RKObjectLoaderDelegate callbacks must occur on the main thread");
RKObjectMappingResult* result = [RKObjectMappingResult mappingResultWithDictionary:resultDictionary];
// Dictionary callback
if ([self.delegate respondsToSelector:@selector(objectLoader:didLoadObjectDictionary:)]) {
[(NSObject<RKObjectLoaderDelegate>*)self.delegate objectLoader:self didLoadObjectDictionary:[result asDictionary]];
}
if (self.onDidLoadObjectsDictionary) {
self.onDidLoadObjectsDictionary([result asDictionary]);
}
// Collection callback
if ([self.delegate respondsToSelector:@selector(objectLoader:didLoadObjects:)]) {
[(NSObject<RKObjectLoaderDelegate>*)self.delegate objectLoader:self didLoadObjects:[result asCollection]];
}
if (self.onDidLoadObjects) {
self.onDidLoadObjects([result asCollection]);
}
// Singular object callback
if ([self.delegate respondsToSelector:@selector(objectLoader:didLoadObject:)]) {
[(NSObject<RKObjectLoaderDelegate>*)self.delegate objectLoader:self didLoadObject:[result asObject]];
}
if (self.onDidLoadObject) {
self.onDidLoadObject([result asObject]);
}
[self finalizeLoad:YES error:nil];
}
@@ -257,7 +294,7 @@
}
if ([self.response isFailure]) {
[(NSObject<RKObjectLoaderDelegate>*)_delegate objectLoader:self didFailWithError:self.response.failureError];
[self informDelegateOfError:self.response.failureError];
[self finalizeLoad:NO error:self.response.failureError];
@@ -272,8 +309,8 @@
NSError* error = [NSError errorWithDomain:RKRestKitErrorDomain code:RKObjectLoaderUnexpectedResponseError userInfo:nil];
if ([_delegate respondsToSelector:@selector(objectLoaderDidLoadUnexpectedResponse:)]) {
[(NSObject<RKObjectLoaderDelegate>*)_delegate objectLoaderDidLoadUnexpectedResponse:self];
} else {
[(NSObject<RKObjectLoaderDelegate>*)_delegate objectLoader:self didFailWithError:error];
} else {
[self informDelegateOfError:error];
}
// NOTE: We skip didFailLoadWithError: here so that we don't send the delegate
@@ -301,7 +338,7 @@
RKLogError(@"Encountered an error while attempting to map server side errors from payload: %@", [error localizedDescription]);
}
[(NSObject<RKObjectLoaderDelegate>*)_delegate objectLoader:self didFailWithError:error];
[self informDelegateOfError:error];
[self finalizeLoad:NO error:error];
}
@@ -347,8 +384,7 @@
[_delegate request:self didFailLoadWithError:error];
}
[(NSObject<RKObjectLoaderDelegate>*)_delegate objectLoader:self didFailWithError:error];
[self informDelegateOfError:error];
[self finalizeLoad:NO error:error];
}
@@ -391,6 +427,16 @@
}
}
// Proxy the delegate property back to our superclass implementation. The object loader should
// really not be a subclass of RKRequest.
- (void)setDelegate:(id<RKObjectLoaderDelegate>)delegate {
[super setDelegate:delegate];
}
- (id<RKObjectLoaderDelegate>)delegate {
return (id<RKObjectLoaderDelegate>) [super delegate];
}
@end
@implementation RKObjectLoader (Deprecations)
@@ -399,10 +445,10 @@
return [[[self alloc] initWithResourcePath:resourcePath objectManager:objectManager delegate:delegate] autorelease];
}
- (id)initWithResourcePath:(NSString*)resourcePath objectManager:(RKObjectManager*)objectManager delegate:(id<RKObjectLoaderDelegate>)delegate {
- (id)initWithResourcePath:(NSString*)resourcePath objectManager:(RKObjectManager*)objectManager delegate:(id<RKObjectLoaderDelegate>)theDelegate {
if ((self = [self initWithURL:[objectManager.baseURL URLByAppendingResourcePath:resourcePath] mappingProvider:objectManager.mappingProvider])) {
[objectManager.client configureRequest:self];
_delegate = delegate;
_delegate = theDelegate;
}
return self;

View File

@@ -122,12 +122,12 @@ typedef enum {
/**
Return the shared instance of the object manager
*/
+ (RKObjectManager*)sharedManager;
+ (RKObjectManager *)sharedManager;
/**
Set the shared instance of the object manager
*/
+ (void)setSharedManager:(RKObjectManager*)manager;
+ (void)setSharedManager:(RKObjectManager *)manager;
/// @name Initializing an Object Manager
@@ -135,8 +135,8 @@ typedef enum {
Create and initialize a new object manager. If this is the first instance created
it will be set as the shared instance
*/
+ (RKObjectManager*)managerWithBaseURLString:(NSString *)baseURLString;
+ (RKObjectManager*)managerWithBaseURL:(NSURL *)baseURL;
+ (id)managerWithBaseURLString:(NSString *)baseURLString;
+ (id)managerWithBaseURL:(NSURL *)baseURL;
/**
Initializes a newly created object manager with a specified baseURL.
@@ -151,7 +151,7 @@ typedef enum {
/**
The underlying HTTP client for this manager
*/
@property (nonatomic, retain) RKClient* client;
@property (nonatomic, retain) RKClient *client;
/**
The base URL of the underlying RKClient instance. Object loader
@@ -185,191 +185,36 @@ typedef enum {
/**
The Mapping Provider responsible for returning mappings for various keyPaths.
*/
@property (nonatomic, retain) RKObjectMappingProvider* mappingProvider;
@property (nonatomic, retain) RKObjectMappingProvider *mappingProvider;
/**
Router object responsible for generating resource paths for
HTTP requests
*/
@property (nonatomic, retain) RKObjectRouter* router;
@property (nonatomic, retain) RKObjectRouter *router;
/**
A Core Data backed object store for persisting objects that have been fetched from the Web
*/
@property (nonatomic, retain) RKManagedObjectStore* objectStore;
@property (nonatomic, retain) RKManagedObjectStore *objectStore;
/**
The Default MIME Type to be used in object serialization.
*/
@property (nonatomic, retain) NSString* serializationMIMEType;
@property (nonatomic, retain) NSString *serializationMIMEType;
/**
The value for the HTTP Accept header to specify the preferred format for retrieved data
*/
@property (nonatomic, assign) NSString* acceptMIMEType;
/**
When YES, RestKit will auto-select the appropriate object mapping for a particular object
passed through getObject:, postObject:, putObject:, and deleteObject:.
This is useful when you are working with mappable data that is not identifiable via KVC
and you are sending/receiving objects of the same type. When YES, RestKit will search the
mappingProvider for an object mapping targeting the same type of object that you passed into
getObject:, postObject:, :putObject, or deleteObject: and configure the RKObjectLoader to map
the payload using that mapping. This is merely a convenience for users who are working entirely
with non-KVC mappable data and saves the added step of searching the mapping provider manually.
Default: NO
*/
@property (nonatomic, assign) BOOL inferMappingsFromObjectTypes;
@property (nonatomic, assign) NSString *acceptMIMEType;
////////////////////////////////////////////////////////
/// @name Registered Object Loaders
/// @name Building Object Loaders
/**
These methods are suitable for loading remote payloads that encode type information into the payload. This enables
the mapping of complex payloads spanning multiple types (i.e. a search operation returning Articles & Comments in
one payload). Ruby on Rails JSON serialization is an example of such a conformant system.
*/
/**
Create and send an asynchronous GET request to load the objects at the resource path and call back the delegate
with the loaded objects. Remote objects will be mapped to local objects by consulting the keyPath registrations
set on the mapping provider.
*/
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Load mappable objects at the specified resourcePath using the specified object mapping.
*/
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath objectMapping:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate;
////////////////////////////////////////////////////////
/// @name Mappable Object Loaders
/**
Fetch the data for a mappable object by performing an HTTP GET.
*/
- (RKObjectLoader*)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Create a remote mappable model by POSTing the attributes to the remote resource and loading the resulting objects from the payload
*/
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Update a remote mappable model by PUTing the attributes to the remote resource and loading the resulting objects from the payload
*/
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Delete the remote instance of a mappable model by performing an HTTP DELETE on the remote resource
*/
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
////////////////////////////////////////////////////////
/// @name Block Configured Object Loaders
#if NS_BLOCKS_AVAILABLE
/**
Load the objects at the specified resource path and perform object mapping on the response payload. Prior to sending the object loader, the
block will be invoked to allow you to configure the object loader as you see fit. This can be used to change the response type, set custom
parameters, choose an object mapping, etc.
For example:
- (void)loadObjectWithBlockExample {
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/monkeys.json" delegate:self block:^(RKObjectLoader* loader) {
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Monkey class]];
}];
}
*/
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
Configure and send an object loader after yielding it to a block for configuration. This allows for very succinct on-the-fly
configuration of the request without obtaining an object reference via objectLoaderForObject: and then sending it yourself.
For example:
- (BOOL)changePassword:(NSString*)newPassword error:(NSError**)error {
if ([self validatePassword:newPassword error:error]) {
self.password = newPassword;
[[RKObjectManager sharedManager] sendObject:self delegate:self block:^(RKObjectLoader* loader) {
loader.method = RKRequestMethodPOST;
loader.serializationMIMEType = RKMIMETypeJSON; // We want to send this request as JSON
loader.targetObject = nil; // Map the results back onto a new object instead of self
// Set up a custom serialization mapping to handle this request
loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
[mapping mapAttributes:@"password", nil];
}];
}];
}
}
*/
- (RKObjectLoader*)sendObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
GET a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
POST a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
*/
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
PUT a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
/**
DELETE a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
#endif
//////
/**
Fetch the data for a mappable object by performing an HTTP GET. The data returned in the response will be mapped according
to the object mapping provided.
*/
- (RKObjectLoader*)getObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Send the data for a mappable object by performing an HTTP POST. The data returned in the response will be mapped according
to the object mapping provided.
*/
- (RKObjectLoader*)postObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Send the data for a mappable object by performing an HTTP PUT. The data returned in the response will be mapped according
to the object mapping provided.
*/
- (RKObjectLoader*)putObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Delete a remote object representation by performing an HTTP DELETE. The data returned in the response will be mapped according
to the object mapping provided.
*/
- (RKObjectLoader*)deleteObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Return the class of object loader instances built through the manager. When Core Data has
been configured
Returns the class of object loader instances built through the manager. When Core Data has
been configured, instances of RKManagedObjectLoader will be emitted by the manager. Otherwise
RKObjectLoader is used.
@return RKObjectLoader OR RKManagedObjectLoader
*/
@@ -429,10 +274,140 @@ typedef enum {
*/
- (RKObjectPaginator *)paginatorWithResourcePathPattern:(NSString *)resourcePathPattern;
////////////////////////////////////////////////////////
/// @name Registered Object Loaders
+ (RKObjectManager*)objectManagerWithBaseURLString:(NSString *)baseURLString;
+ (RKObjectManager*)objectManagerWithBaseURL:(NSURL *)baseURL;
- (RKObjectLoader*)objectLoaderWithResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
- (RKObjectLoader*)objectLoaderForObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
/**
These methods are suitable for loading remote payloads that encode type information into the payload. This enables
the mapping of complex payloads spanning multiple types (i.e. a search operation returning Articles & Comments in
one payload). Ruby on Rails JSON serialization is an example of such a conformant system.
*/
/**
Create and send an asynchronous GET request to load the objects at the resource path and call back the delegate
with the loaded objects. Remote objects will be mapped to local objects by consulting the keyPath registrations
set on the mapping provider.
*/
- (void)loadObjectsAtResourcePath:(NSString *)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate;
////////////////////////////////////////////////////////
/// @name Mappable Object Loaders
/**
Fetch the data for a mappable object by performing an HTTP GET.
*/
- (void)getObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Create a remote mappable model by POSTing the attributes to the remote resource and loading the resulting objects from the payload
*/
- (void)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Update a remote mappable model by PUTing the attributes to the remote resource and loading the resulting objects from the payload
*/
- (void)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
/**
Delete the remote instance of a mappable model by performing an HTTP DELETE on the remote resource
*/
- (void)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate;
////////////////////////////////////////////////////////
/// @name Block Configured Object Loaders
#if NS_BLOCKS_AVAILABLE
/**
Load the objects at the specified resource path and perform object mapping on the response payload. Prior to sending the object loader, the
block will be invoked to allow you to configure the object loader as you see fit. This can be used to change the response type, set custom
parameters, choose an object mapping, etc.
For example:
- (void)loadObjectWithBlockExample {
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/monkeys.json" delegate:self block:^(RKObjectLoader* loader) {
loader.objectMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForClass:[Monkey class]];
}];
}
*/
- (void)loadObjectsAtResourcePath:(NSString *)resourcePath usingBlock:(RKObjectLoaderBlock)block;
/**
Configure and send an object loader after yielding it to a block for configuration. This allows for very succinct on-the-fly
configuration of the request without obtaining an object reference via objectLoaderForObject: and then sending it yourself.
For example:
- (BOOL)changePassword:(NSString*)newPassword error:(NSError**)error {
if ([self validatePassword:newPassword error:error]) {
self.password = newPassword;
[[RKObjectManager sharedManager] sendObject:self toResourcePath:@"/some/path" usingBlock:^(RKObjectLoader* loader) {
loader.delegate = self;
loader.method = RKRequestMethodPOST;
loader.serializationMIMEType = RKMIMETypeJSON; // We want to send this request as JSON
loader.targetObject = nil; // Map the results back onto a new object instead of self
// Set up a custom serialization mapping to handle this request
loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
[mapping mapAttributes:@"password", nil];
}];
}];
}
}
*/
- (void)sendObject:(id<NSObject>)object toResourcePath:(NSString *)resourcePath usingBlock:(RKObjectLoaderBlock)block;
/**
GET a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (void)getObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block;
/**
POST a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block;
*/
- (void)postObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block;
/**
PUT a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (void)putObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block;
/**
DELETE a remote object instance and yield the object loader to the block before sending
@see sendObject:method:delegate:block
*/
- (void)deleteObject:(id<NSObject>)object usingBlock:(RKObjectLoaderBlock)block;
#endif
//////
// Deprecations
+ (RKObjectManager *)objectManagerWithBaseURLString:(NSString *)baseURLString;
+ (RKObjectManager *)objectManagerWithBaseURL:(NSURL *)baseURL;
- (void)loadObjectsAtResourcePath:(NSString*)resourcePath objectMapping:(RKObjectMapping*)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
- (RKObjectLoader *)objectLoaderWithResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
- (RKObjectLoader *)objectLoaderForObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
/*
NOTE:
The mapResponseWith: family of methods have been deprecated by the support for object mapping selection
using resourcePath's
*/
- (void)getObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
- (void)postObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
- (void)putObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
- (void)deleteObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate DEPRECATED_ATTRIBUTE;
@end

View File

@@ -25,13 +25,13 @@
#import "Support.h"
#import "RKErrorMessage.h"
NSString* const RKDidEnterOfflineModeNotification = @"RKDidEnterOfflineModeNotification";
NSString* const RKDidEnterOnlineModeNotification = @"RKDidEnterOnlineModeNotification";
NSString * const RKDidEnterOfflineModeNotification = @"RKDidEnterOfflineModeNotification";
NSString * const RKDidEnterOnlineModeNotification = @"RKDidEnterOnlineModeNotification";
//////////////////////////////////
// Shared Instance
static RKObjectManager* sharedManager = nil;
static RKObjectManager *sharedManager = nil;
///////////////////////////////////
@@ -42,7 +42,6 @@ static RKObjectManager* sharedManager = nil;
@synthesize router = _router;
@synthesize mappingProvider = _mappingProvider;
@synthesize serializationMIMEType = _serializationMIMEType;
@synthesize inferMappingsFromObjectTypes = _inferMappingsFromObjectTypes;
- (id)initWithBaseURL:(RKURL *)baseURL {
self = [super init];
@@ -51,13 +50,12 @@ static RKObjectManager* sharedManager = nil;
_router = [RKObjectRouter new];
_client = [[RKClient alloc] initWithBaseURL:baseURL];
_onlineState = RKObjectManagerOnlineStateUndetermined;
_inferMappingsFromObjectTypes = NO;
self.acceptMIMEType = RKMIMETypeJSON;
self.serializationMIMEType = RKMIMETypeFormURLEncoded;
// Setup default error message mappings
RKObjectMapping* errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
RKObjectMapping *errorMapping = [RKObjectMapping mappingForClass:[RKErrorMessage class]];
errorMapping.rootKeyPath = @"errors";
[errorMapping mapKeyPath:@"" toAttribute:@"errorMessage"];
_mappingProvider.errorMapping = errorMapping;
@@ -76,22 +74,22 @@ static RKObjectManager* sharedManager = nil;
return self;
}
+ (RKObjectManager*)sharedManager {
+ (RKObjectManager *)sharedManager {
return sharedManager;
}
+ (void)setSharedManager:(RKObjectManager*)manager {
+ (void)setSharedManager:(RKObjectManager *)manager {
[manager retain];
[sharedManager release];
sharedManager = manager;
}
+ (RKObjectManager*)managerWithBaseURLString:(NSString *)baseURLString {
+ (RKObjectManager *)managerWithBaseURLString:(NSString *)baseURLString {
return [self managerWithBaseURL:[RKURL URLWithString:baseURLString]];
}
+ (RKObjectManager*)managerWithBaseURL:(NSURL *)baseURL {
RKObjectManager* manager = [[[self alloc] initWithBaseURL:baseURL] autorelease];
+ (RKObjectManager *)managerWithBaseURL:(NSURL *)baseURL {
RKObjectManager *manager = [[[self alloc] initWithBaseURL:baseURL] autorelease];
return manager;
}
@@ -120,7 +118,7 @@ static RKObjectManager* sharedManager = nil;
return ![self isOnline];
}
- (void)reachabilityChanged:(NSNotification*)notification {
- (void)reachabilityChanged:(NSNotification *)notification {
BOOL isHostReachable = [self.client.reachabilityObserver isNetworkReachable];
_onlineState = isHostReachable ? RKObjectManagerOnlineStateConnected : RKObjectManagerOnlineStateDisconnected;
@@ -132,11 +130,11 @@ static RKObjectManager* sharedManager = nil;
}
}
- (void)setAcceptMIMEType:(NSString*)MIMEType {
- (void)setAcceptMIMEType:(NSString *)MIMEType {
[_client setValue:MIMEType forHTTPHeaderField:@"Accept"];
}
- (NSString*)acceptMIMEType {
- (NSString *)acceptMIMEType {
return [self.client.HTTPHeaders valueForKey:@"Accept"];
}
@@ -181,209 +179,136 @@ static RKObjectManager* sharedManager = nil;
}
- (id)loaderForObject:(id<NSObject>)object method:(RKRequestMethod)method {
NSString* resourcePath = [self.router resourcePathForObject:object method:method];
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
NSString* resourcePath = (method == RKRequestMethodInvalid) ? nil : [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;
RKObjectMapping *objectMapping = [self.mappingProvider objectMappingForResourcePath:resourcePath];
if (objectMapping == nil || [object isMemberOfClass:[objectMapping objectClass]]) {
loader.targetObject = object;
} else {
loader.targetObject = nil;
}
return loader;
}
- (RKObjectLoader*)objectLoaderWithResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
loader.delegate = delegate;
return loader;
}
- (RKObjectLoader*)loadObjectsAtResourcePath:(NSString*)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
- (void)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];
- (void)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];
- (void)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];
- (void)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];
- (void)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 {
- (void)loadObjectsAtResourcePath:(NSString*)resourcePath usingBlock:(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]];
- (void)sendObject:(id<NSObject>)object toResourcePath:(NSString *)resourcePath usingBlock:(void(^)(RKObjectLoader*))block {
RKObjectLoader *loader = [self loaderForObject:object method:RKRequestMethodInvalid];
loader.URL = [self.baseURL URLByAppendingResourcePath:resourcePath];
// 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) {
- (void)sendObject:(id<NSObject>)object method:(RKRequestMethod)method usingBlock:(void(^)(RKObjectLoader*))block {
NSString *resourcePath = [self.router resourcePathForObject:object method:method];
[self sendObject:object toResourcePath:resourcePath usingBlock:^(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];
- (void)getObject:(id<NSObject>)object usingBlock:(void(^)(RKObjectLoader *))block {
[self sendObject:object method:RKRequestMethodGET usingBlock:block];
}
- (RKObjectLoader*)postObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodPOST delegate:delegate block:block];
- (void)postObject:(id<NSObject>)object usingBlock:(void(^)(RKObjectLoader *))block {
[self sendObject:object method:RKRequestMethodPOST usingBlock:block];
}
- (RKObjectLoader*)putObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodPUT delegate:delegate block:block];
- (void)putObject:(id<NSObject>)object usingBlock:(void(^)(RKObjectLoader *))block {
[self sendObject:object method:RKRequestMethodPUT usingBlock:block];
}
- (RKObjectLoader*)deleteObject:(id<NSObject>)object delegate:(id<RKObjectLoaderDelegate>)delegate block:(void(^)(RKObjectLoader*))block {
return [self sendObject:object method:RKRequestMethodDELETE delegate:delegate block:block];
- (void)deleteObject:(id<NSObject>)object usingBlock:(void(^)(RKObjectLoader *))block {
[self sendObject:object method:RKRequestMethodDELETE usingBlock: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;
- (void)getObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
[self sendObject:object method:RKRequestMethodGET usingBlock:^(RKObjectLoader *loader) {
loader.delegate = delegate;
loader.objectMapping = objectMapping;
}];
}
- (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;
- (void)postObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
[self sendObject:object method:RKRequestMethodPOST usingBlock:^(RKObjectLoader *loader) {
loader.delegate = delegate;
loader.objectMapping = objectMapping;
}];
}
- (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;
- (void)putObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
[self sendObject:object method:RKRequestMethodPUT usingBlock:^(RKObjectLoader *loader) {
loader.delegate = delegate;
loader.objectMapping = objectMapping;
}];
}
- (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;
- (void)deleteObject:(id<NSObject>)object mapResponseWith:(RKObjectMapping *)objectMapping delegate:(id<RKObjectLoaderDelegate>)delegate {
[self sendObject:object method:RKRequestMethodDELETE usingBlock:^(RKObjectLoader *loader) {
loader.delegate = delegate;
loader.objectMapping = objectMapping;
}];
}
- (RKRequestCache *)requestCache {
@@ -406,12 +331,34 @@ static RKObjectManager* sharedManager = nil;
#pragma mark - Deprecations
+ (RKObjectManager*)objectManagerWithBaseURLString:(NSString *)baseURLString {
+ (RKObjectManager *)objectManagerWithBaseURLString:(NSString *)baseURLString {
return [self managerWithBaseURLString:baseURLString];
}
+ (RKObjectManager*)objectManagerWithBaseURL:(NSURL *)baseURL {
+ (RKObjectManager *)objectManagerWithBaseURL:(NSURL *)baseURL {
return [self managerWithBaseURL:baseURL];
}
- (RKObjectLoader *)objectLoaderWithResourcePath:(NSString *)resourcePath delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader* loader = [self loaderWithResourcePath:resourcePath];
loader.delegate = delegate;
return loader;
}
- (RKObjectLoader*)objectLoaderForObject:(id<NSObject>)object method:(RKRequestMethod)method delegate:(id<RKObjectLoaderDelegate>)delegate {
RKObjectLoader *loader = [self loaderForObject:object method:method];
loader.delegate = delegate;
return loader;
}
- (void)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];
}
@end

View File

@@ -82,9 +82,13 @@ relationship. Relationships are processed using an object mapping as well.
@property (nonatomic, readonly) NSArray* mappedKeyPaths;
/**
The root keyPath for this object. When the object mapping is being used for serialization
and a root keyPath has been defined, the serialized object will be nested under this root keyPath
before being encoded for transmission to a remote system.
The root key path for the receiver.
Root key paths are handled differently depending on the context in which the mapping is
being used. If the receiver is used for object mapping, the rootKeyPath specifies a nested
root dictionary that all attribute and relationship mappings will be considered relative to. When
the mapping is used in a serialization context, the rootKeyPath specifies that the serialized content
should be stored in a dictionary nested with the rootKeyPath as the key.
@see RKObjectSerializer
*/

View File

@@ -22,7 +22,7 @@
#import "RKDynamicObjectMapping.h"
// Internal framework contexts
// see RKObjectMappingProvider+Contexts
// @see RKObjectMappingProvider+Contexts.h
typedef enum {
RKObjectMappingProviderContextObjectsByKeyPath = 1000,
RKObjectMappingProviderContextObjectsByType,