2012-04-17 115 views
2

我们必须支持一些使用ASIHTTPRequest运行的旧代码,但我们希望RestKit提供的对象映射和核心数据支持。有谁知道“粘合”这两种方法的任何方式吗?ASIHTTPRequest支持RestKit对象映射

我图片使用ASIHTTPRequest的请求和某人手动转发有效载荷到RestKit。

回答

4

好的,所以这毕竟不是太难。这是我为此写的一堂课(没有免责声明,它适用于我们,可能对其他人有用)。您可以使用它作为标准RKObjectLoader类的直接替换。

.h文件中

#import <RestKit/RestKit.h> 
#import "ASIFormDataRequest.h" 

@interface ASIHTTPObjectLoader : ASIFormDataRequest <RKObjectMapperDelegate> { 
    RKObjectManager* _objectManager; 
    RKObjectMapping* _objectMapping; 
    RKObjectMappingResult* _result; 
    RKObjectMapping* _serializationMapping; 
    NSString* _serializationMIMEType; 
    NSObject* _sourceObject; 
NSObject* _targetObject; 
} 

@property (nonatomic, retain) RKObjectMapping* objectMapping; 
@property (nonatomic, readonly) RKObjectManager* objectManager; 
@property (nonatomic, readonly) RKObjectMappingResult* result; 
@property (nonatomic, retain) RKObjectMapping* serializationMapping; 
@property (nonatomic, retain) NSString* serializationMIMEType; 
@property (nonatomic, retain) NSObject* sourceObject; 
@property (nonatomic, retain) NSObject* targetObject; 

- (void) setDelegate:(id<RKObjectLoaderDelegate>)delegate; 
+ (id)loaderWithResourcePath:(NSString*)resourcePath objectManager: (RKObjectManager*)objectManager delegate:(id<RKObjectLoaderDelegate>)delegate; 
- (id)initWithResourcePath:(NSString*)resourcePath objectManager:(RKObjectManager*)objectManager delegate:(id<RKObjectLoaderDelegate>)delegate;    
- (void)handleResponseError; 

@end 

.m文件

#import "ASIHTTPObjectLoader.h" 

@interface ASIFormDataRequest (here) 

- (void) reportFailure; 
- (void) reportFinished; 

@end 

@implementation ASIHTTPObjectLoader 
@synthesize objectManager = _objectManager; 
@synthesize targetObject = _targetObject, objectMapping = _objectMapping; 
@synthesize result = _result; 
@synthesize serializationMapping = _serializationMapping; 
@synthesize serializationMIMEType = _serializationMIMEType; 
@synthesize sourceObject = _sourceObject; 

- (void) setDelegate:(id<RKObjectLoaderDelegate>)_delegate { 
    [super setDelegate: _delegate]; 
} 

+ (id)loaderWithResourcePath:(NSString*)resourcePath objectManager:(RKObjectManager*)objectManager delegate:(id<RKObjectLoaderDelegate>)_delegate { 
    return [[[self alloc] initWithResourcePath:resourcePath objectManager:objectManager delegate:_delegate] autorelease]; 
} 

- (id)initWithResourcePath:(NSString*)resourcePath objectManager:(RKObjectManager*)objectManager delegate:(id<RKObjectLoaderDelegate>)_delegate { 

    self = [super initWithURL: [objectManager.client URLForResourcePath: resourcePath]]; 

    if (self) { 
     self.delegate = _delegate; 
     _objectManager = objectManager; 
    } 

    return self; 
} 

- (void)dealloc { 
    // Weak reference 
    _objectManager = nil; 

    [_sourceObject release]; 
    _sourceObject = nil; 
    [_targetObject release]; 
    _targetObject = nil; 
    [_objectMapping release]; 
    _objectMapping = nil; 
    [_result release]; 
    _result = nil; 
    [_serializationMIMEType release]; 
    [_serializationMapping release]; 

    [super dealloc]; 
} 

- (void) reset { 
    [_result release]; 
    _result = nil; 
} 

- (void)finalizeLoad:(BOOL)successful error:(NSError*)_error { 
    //_isLoading = NO; 

    if (successful) { 
     //_isLoaded = YES; 
     if ([self.delegate respondsToSelector:@selector(objectLoaderDidFinishLoading:)]) { 
      [self.delegate performSelectorOnMainThread:@selector(objectLoaderDidFinishLoading:) 
                       withObject:self waitUntilDone:YES];    
     } 

     [super reportFinished]; 

     /* 
     NSDictionary* userInfo = [NSDictionary dictionaryWithObject:_response 
                  forKey:RKRequestDidLoadResponseNotificationUserInfoResponseKey]; 
     [[NSNotificationCenter defaultCenter] postNotificationName:RKRequestDidLoadResponseNotification 
                  object:self 
                  userInfo:userInfo]; 
     */ 
    } else { 
     NSDictionary* _userInfo = [NSDictionary dictionaryWithObject:(_error ? _error : (NSError*)[NSNull null]) 
                  forKey:RKRequestDidFailWithErrorNotificationUserInfoErrorKey]; 
     [[NSNotificationCenter defaultCenter] postNotificationName:RKRequestDidFailWithErrorNotification 
                  object:self 
                  userInfo:_userInfo]; 
    } 
} 

// Invoked on the main thread. Inform the delegate. 
- (void)informDelegateOfObjectLoadWithResultDictionary:(NSDictionary*)resultDictionary { 
    NSAssert([NSThread isMainThread], @"RKObjectLoaderDelegate callbacks must occur on the main thread"); 

    RKObjectMappingResult* result = [RKObjectMappingResult mappingResultWithDictionary:resultDictionary]; 

    if ([self.delegate respondsToSelector:@selector(objectLoader:didLoadObjectDictionary:)]) { 
     [self.delegate objectLoader: (RKObjectLoader*)self didLoadObjectDictionary:[result asDictionary]]; 
    } 

    if ([self.delegate respondsToSelector:@selector(objectLoader:didLoadObjects:)]) { 
     [self.delegate objectLoader: (RKObjectLoader*)self didLoadObjects:[result asCollection]]; 
    } 

    if ([self.delegate respondsToSelector:@selector(objectLoader:didLoadObject:)]) { 
     [self.delegate objectLoader: (RKObjectLoader*)self didLoadObject:[result asObject]]; 
    } 

    [self finalizeLoad:YES error:nil]; 
} 

#pragma mark - Subclass Hooks 

/** 
Overloaded by ASIHTTPManagedObjectLoader to serialize/deserialize managed objects 
at thread boundaries. 

@protected 
*/ 
- (void)processMappingResult:(RKObjectMappingResult*)result { 
    NSAssert(isSynchronous || ![NSThread isMainThread], @"Mapping result processing should occur on a background thread"); 
    [self performSelectorOnMainThread:@selector(informDelegateOfObjectLoadWithResultDictionary:) withObject:[result asDictionary] waitUntilDone:YES]; 
} 

#pragma mark - Response Object Mapping 

- (RKObjectMappingResult*)mapResponseWithMappingProvider:(RKObjectMappingProvider*)mappingProvider toObject:(id)targetObject error:(NSError**)_error { 
    NSString* MIMEType = [[self responseHeaders] objectForKey: @"Content-Type"]; 
    id<RKParser> parser = [[RKParserRegistry sharedRegistry] parserForMIMEType: MIMEType]; 
    NSAssert1(parser, @"Cannot perform object load without a parser for MIME Type '%@'", MIMEType); 

    // Check that there is actually content in the response body for mapping. It is possible to get back a 200 response 
    // with the appropriate MIME Type with no content (such as for a successful PUT or DELETE). Make sure we don't generate an error 
    // in these cases 
    id bodyAsString = [self responseString]; 
    if (bodyAsString == nil || [[bodyAsString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]] length] == 0) { 
     RKLogDebug(@"Mapping attempted on empty response body..."); 
     if (self.targetObject) { 
      return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionaryWithObject:self.targetObject forKey:@""]]; 
     } 

     return [RKObjectMappingResult mappingResultWithDictionary:[NSDictionary dictionary]]; 
    } 

    id parsedData = [parser objectFromString:bodyAsString error:_error]; 
    if (parsedData == nil && _error) { 
     return nil; 
    } 

    // Allow the delegate to manipulate the data 
    if ([self.delegate respondsToSelector:@selector(objectLoader:willMapData:)]) { 
     parsedData = [[parsedData mutableCopy] autorelease]; 
     [self.delegate objectLoader: (RKObjectLoader*)self willMapData:&parsedData]; 
    } 

    RKObjectMapper* mapper = [RKObjectMapper mapperWithObject:parsedData mappingProvider:mappingProvider]; 
    mapper.targetObject = targetObject; 
    mapper.delegate = self; 
    RKObjectMappingResult* result = [mapper performMapping]; 

    // Log any mapping errors 
    if (mapper.errorCount > 0) { 
     RKLogError(@"Encountered errors during mapping: %@", [[mapper.errors valueForKey:@"localizedDescription"] componentsJoinedByString:@", "]); 
    } 

    // The object mapper will return a nil result if mapping failed 
    if (nil == result) { 
     // TODO: Construct a composite error that wraps up all the other errors. Should probably make it performMapping:&error when we have this? 
     if (_error) *_error = [mapper.errors lastObject]; 
     return nil; 
    } 

    return result; 
} 

- (RKObjectMappingResult*)performMapping:(NSError**)_error { 
    NSAssert(isSynchronous || ![NSThread isMainThread], @"Mapping should occur on a background thread"); 

    RKObjectMappingProvider* mappingProvider; 
    if (self.objectMapping) { 
     NSString* rootKeyPath = self.objectMapping.rootKeyPath ? self.objectMapping.rootKeyPath : @""; 
     RKLogDebug(@"Found directly configured object mapping, creating temporary mapping provider for keyPath %@", rootKeyPath); 
     mappingProvider = [[RKObjectMappingProvider new] autorelease];   
     [mappingProvider setMapping:self.objectMapping forKeyPath:rootKeyPath]; 
    } else { 
     RKLogDebug(@"No object mapping provider, using mapping provider from parent object manager to perform KVC mapping"); 
     mappingProvider = self.objectManager.mappingProvider; 
    } 

    return [self mapResponseWithMappingProvider:mappingProvider toObject:self.targetObject error:_error]; 
} 


- (void)performMappingOnBackgroundThread { 
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; 

    NSError* _error = nil; 
    _result = [[self performMapping:&_error] retain]; 
    NSAssert(_result || _error, @"Expected performMapping to return a mapping result or an error."); 
    if (self.result) { 
     [self processMappingResult:self.result]; 
    } else if (_error) { 
     [self failWithError: _error]; 
    } 

    [pool drain]; 
} 

- (BOOL)canParseMIMEType:(NSString*)MIMEType { 
    if ([[RKParserRegistry sharedRegistry] parserForMIMEType: MIMEType]) { 
     return YES; 
    } 

    RKLogWarning(@"Unable to find parser for MIME Type '%@'", MIMEType); 
    return NO; 
} 

- (BOOL)isResponseMappable { 
    if ([self responseStatusCode] == 503) { 
     [[NSNotificationCenter defaultCenter] postNotificationName:RKServiceDidBecomeUnavailableNotification object:self]; 
    } 

    NSString* MIMEType = [[self responseHeaders] objectForKey: @"Content-Type"]; 

    if (error) { 
     [self.delegate objectLoader: (RKObjectLoader*)self didFailWithError: error]; 

     [self finalizeLoad:NO error: error]; 

     return NO; 
    } else if ([self responseStatusCode] == 204) { 
     // The No Content (204) response will never have a message body or a MIME Type. Invoke the delegate with self 
     [self informDelegateOfObjectLoadWithResultDictionary:[NSDictionary dictionaryWithObject:self forKey:@""]]; 
     return NO; 
    } else if (NO == [self canParseMIMEType: MIMEType]) { 
     // We can't parse the response, it's unmappable regardless of the status code 
     RKLogWarning(@"Encountered unexpected response with status code: %ld (MIME Type: %@)", (long) [self responseStatusCode], MIMEType); 
     NSError* _error = [NSError errorWithDomain:RKRestKitErrorDomain code:RKObjectLoaderUnexpectedResponseError userInfo:nil]; 
     if ([self.delegate respondsToSelector:@selector(objectLoaderDidLoadUnexpectedResponse:)]) { 
      [self.delegate objectLoaderDidLoadUnexpectedResponse: (RKObjectLoader*)self]; 
     } else {    
      [self.delegate objectLoader: (RKObjectLoader*)self didFailWithError: _error]; 
     } 

     // NOTE: We skip didFailLoadWithError: here so that we don't send the delegate 
     // conflicting messages around unexpected response and failure with error 
     [self finalizeLoad:NO error:_error]; 

     return NO; 
    } else if (([self responseStatusCode] >= 400 && [self responseStatusCode] < 500) || 
       ([self responseStatusCode] >= 500 && [self responseStatusCode] < 600)) { 
     // This is an error and we can map the MIME Type of the response 
     [self handleResponseError]; 
     return NO; 
    } 

    return YES; 
} 

- (void)handleResponseError { 
    // Since we are mapping what we know to be an error response, we don't want to map the result back onto our 
    // target object 
    NSError* _error = nil; 
    RKObjectMappingResult* result = [self mapResponseWithMappingProvider:self.objectManager.mappingProvider toObject:nil error:&_error]; 
    if (result) { 
     _error = [result asError]; 
    } else { 
     RKLogError(@"Encountered an error while attempting to map server side errors from payload: %@", [_error localizedDescription]); 
    } 

    [self.delegate objectLoader: (RKObjectLoader*)self didFailWithError:_error]; 
    [self finalizeLoad:NO error:_error];  
} 

#pragma mark - RKRequest & RKRequestDelegate methods 
- (void) reportFailure { 
    [self.delegate objectLoader: (RKObjectLoader*)self didFailWithError:error]; 

    [super reportFailure]; 
} 

- (void)reportFinished { 
    NSAssert([NSThread isMainThread], @"RKObjectLoaderDelegate callbacks must occur on the main thread"); 

    if ([self isResponseMappable]) { 
     // Determine if we are synchronous here or not. 
     if (isSynchronous) { 
      NSError* _error = nil; 
      _result = [[self performMapping:&_error] retain]; 
      if (self.result) { 
       [self processMappingResult:self.result]; 
      } else { 
       [self performSelectorInBackground:@selector(failWithError:) withObject:_error]; 
      } 

      [super reportFinished]; 
     } else { 
      [self performSelectorInBackground:@selector(performMappingOnBackgroundThread) withObject:nil]; 
     } 
    } 
} 
1

我在我的单元测试代码如下,以确保我的对象映射工作

NSDictionary *headers = [NSDictionary dictionaryWithObjectsAndKeys:@"application/json", @"X-RESTKIT-CACHED-MIME-TYPE", 
          @"200", @"X-RESTKIT-CACHED-RESPONSE-CODE", 
          @"application/json; charset=utf-8", @"Content-Type", 
          nil]; 

    NSURL *url = [[NSURL alloc] initWithString:@""]; //need a url to create a dummy RKRequest 
    RKRequest *request = [RKRequest requestWithURL:url]; 
    [url release]; 
    //Create a dummy response with the data payload 
    RKResponse *response = [[[RKResponse alloc] initWithRequest:request 
                  body:myData //myData is NSData loaded from my file on disk in this case 
                 headers:headers] autorelease]; 
    RKURL *rkURL = [[RKURL alloc] initWithString:@"https://api.twitter.com"]; 
    RKManagedObjectLoader *loader = [[RKManagedObjectLoader alloc] initWithURL:rkURL 
             mappingProvider:self.objectManager.mappingProvider 
             objectStore:self.objectManager.objectStore]; 
    loader.delegate = self; 
    loader.objectMapping = self.objectMapping; //I pass the object mapping to use here. 
    [loader didFinishLoad:response]; //Given a response and request, Restkit will parse the response and call the usual delegates 

你可能能够做类似的事情以抓取来自ASIHTTPRequest的响应数据并将其传递给RestKit

+0

仍然搞清楚StackOverflow :)我做的编辑的原因似乎不可见。我更改了代码以使用RKManagedObjectLoader并指定了一个对象存储 - 之前,我使用的是RKObjectLoader,并且在我需要实际使用时从对象存储中检索数据时遇到了问题(我试图在我的实际应用程序中执行类似的操作而不仅仅是单元测试映射) – georgemp 2012-05-07 06:58:22