mirror of
https://github.com/zhigang1992/RestKit.git
synced 2026-04-01 22:42:51 +08:00
277 lines
9.6 KiB
Objective-C
277 lines
9.6 KiB
Objective-C
//
|
|
// RKObjectLoader.m
|
|
// RestKit
|
|
//
|
|
// Created by Blake Watters on 8/8/09.
|
|
// Copyright 2009 Two Toasters. All rights reserved.
|
|
//
|
|
|
|
#import <CoreData/CoreData.h>
|
|
#import "../CoreData/RKManagedObjectStore.h"
|
|
#import "RKObjectLoader.h"
|
|
#import "RKObjectManager.h"
|
|
#import "Errors.h"
|
|
#import "RKManagedObject.h"
|
|
#import "RKURL.h"
|
|
|
|
@interface RKObjectLoader (Private)
|
|
- (void)loadObjectsFromResponse:(RKResponse*)response;
|
|
@end
|
|
|
|
@implementation RKObjectLoader
|
|
|
|
@synthesize mapper = _mapper, delegate = _delegate, request = _request, response = _response,
|
|
objectClass = _objectClass, source = _source, keyPath = _keyPath, managedObjectStore = _managedObjectStore;
|
|
|
|
+ (id)loaderWithMapper:(RKObjectMapper*)mapper request:(RKRequest*)request delegate:(NSObject<RKObjectLoaderDelegate>*)delegate {
|
|
return [[[self alloc] initWithMapper:mapper request:request delegate:delegate] autorelease];
|
|
}
|
|
|
|
- (id)initWithMapper:(RKObjectMapper*)mapper request:(RKRequest*)request delegate:(NSObject<RKObjectLoaderDelegate>*)delegate {
|
|
if (self = [self init]) {
|
|
_mapper = [mapper retain];
|
|
self.request = request;
|
|
self.delegate = delegate;
|
|
self.managedObjectStore = nil;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)dealloc {
|
|
_request.delegate = nil;
|
|
[_mapper release];
|
|
[_request release];
|
|
[_response release];
|
|
[_keyPath release];
|
|
self.managedObjectStore = nil;
|
|
[super dealloc];
|
|
}
|
|
|
|
- (void)setRequest:(RKRequest *)request {
|
|
[request retain];
|
|
[_request release];
|
|
_request = request;
|
|
|
|
_request.delegate = self;
|
|
_request.callback = @selector(loadObjectsFromResponse:);
|
|
}
|
|
|
|
#pragma mark RKRequest Proxy Methods
|
|
|
|
- (NSURL*)URL {
|
|
return self.request.URL;
|
|
}
|
|
|
|
- (RKRequestMethod)method {
|
|
return self.request.method;
|
|
}
|
|
|
|
- (void)setMethod:(RKRequestMethod)method {
|
|
self.request.method = method;
|
|
}
|
|
|
|
- (NSObject<RKRequestSerializable>*)params {
|
|
return self.request.params;
|
|
}
|
|
|
|
- (void)setParams:(NSObject<RKRequestSerializable>*)params {
|
|
self.request.params = params;
|
|
}
|
|
|
|
- (void)send {
|
|
[self retain];
|
|
[self.request send];
|
|
}
|
|
|
|
- (void)sendSynchronously {
|
|
[self retain];
|
|
RKResponse* response = [self.request sendSynchronously];
|
|
[self loadObjectsFromResponse:response];
|
|
}
|
|
|
|
#pragma mark Response Processing
|
|
|
|
- (BOOL)encounteredErrorWhileProcessingRequest:(RKResponse*)response {
|
|
if ([response isFailure]) {
|
|
[_delegate objectLoader:self didFailWithError:response.failureError];
|
|
[self release];
|
|
return YES;
|
|
} else if ([response isError]) {
|
|
if ([response isJSON]) {
|
|
[_delegate objectLoader:self didFailWithError:[_mapper parseErrorFromString:[response bodyAsString]]];
|
|
} else {
|
|
if ([_delegate respondsToSelector:@selector(objectLoaderDidLoadUnexpectedResponse:)]) {
|
|
[_delegate objectLoaderDidLoadUnexpectedResponse:self];
|
|
}
|
|
}
|
|
[self release];
|
|
return YES;
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
- (void)informDelegateOfObjectLoadWithInfoDictionary:(NSDictionary*)dictionary {
|
|
NSArray* models = [dictionary objectForKey:@"models"];
|
|
[dictionary release];
|
|
|
|
// NOTE: The models dictionary may contain NSManagedObjectID's from persistent objects
|
|
// that were model mapped on a background thread. We look up the objects by ID and then
|
|
// notify the delegate that the operation has completed.
|
|
NSMutableArray* objects = [NSMutableArray arrayWithCapacity:[models count]];
|
|
for (id object in models) {
|
|
if ([object isKindOfClass:[NSManagedObjectID class]]) {
|
|
[objects addObject:[self.managedObjectStore objectWithID:(NSManagedObjectID*)object]];
|
|
} else {
|
|
[objects addObject:object];
|
|
}
|
|
}
|
|
|
|
[_delegate objectLoader:self didLoadObjects:[NSArray arrayWithArray:objects]];
|
|
[self release];
|
|
}
|
|
|
|
- (void)informDelegateOfObjectLoadErrorWithInfoDictionary:(NSDictionary*)dictionary {
|
|
NSError* error = [dictionary objectForKey:@"error"];
|
|
[dictionary release];
|
|
|
|
NSLog(@"[RestKit] RKObjectLoader: Error saving managed object context: error=%@ userInfo=%@", error, error.userInfo);
|
|
|
|
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
|
|
[error localizedDescription], NSLocalizedDescriptionKey,
|
|
nil];
|
|
NSError *rkError = [NSError errorWithDomain:RKRestKitErrorDomain code:RKObjectLoaderRemoteSystemError userInfo:userInfo];
|
|
|
|
[_delegate objectLoader:self didFailWithError:rkError];
|
|
[self release];
|
|
}
|
|
|
|
|
|
- (void)processLoadModelsInBackground:(RKResponse *)response {
|
|
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
|
|
RKManagedObjectStore* objectStore = self.managedObjectStore;
|
|
|
|
/**
|
|
* If this loader is bound to a particular object, then we map
|
|
* the results back into the instance. This is used for loading and updating
|
|
* individual object instances via getObject & friends.
|
|
*/
|
|
NSArray* results = nil;
|
|
if (self.source) {
|
|
if ([self.source isKindOfClass:[NSManagedObject class]]) {
|
|
NSManagedObjectID* modelID = [(NSManagedObject*)self.source objectID];
|
|
NSManagedObject* backgroundThreadModel = [self.managedObjectStore objectWithID:modelID];
|
|
[_mapper mapObject:backgroundThreadModel fromString:[response bodyAsString]];
|
|
results = [NSArray arrayWithObject:backgroundThreadModel];
|
|
} else {
|
|
[_mapper mapObject:self.source fromString:[response bodyAsString]];
|
|
results = [NSArray arrayWithObject:self.source];
|
|
}
|
|
} else {
|
|
id result = [_mapper mapFromString:[response bodyAsString] toClass:self.objectClass keyPath:_keyPath];
|
|
if ([result isKindOfClass:[NSArray class]]) {
|
|
results = (NSArray*)result;
|
|
} else {
|
|
// Using arrayWithObjects: instead of arrayWithObject:
|
|
// so that in the event result is nil, then we get empty array instead of exception for trying to insert nil.
|
|
results = [NSArray arrayWithObjects:result, nil];
|
|
}
|
|
|
|
if (objectStore && [objectStore managedObjectCache]) {
|
|
if ([self.URL isKindOfClass:[RKURL class]]) {
|
|
RKURL* rkURL = (RKURL*)self.URL;
|
|
NSArray* fetchRequests = [[objectStore managedObjectCache] fetchRequestsForResourcePath:rkURL.resourcePath];
|
|
NSArray* cachedObjects = [RKManagedObject objectsWithFetchRequests:fetchRequests];
|
|
for (id object in cachedObjects) {
|
|
if ([object isKindOfClass:[RKManagedObject class]]) {
|
|
if (NO == [results containsObject:object]) {
|
|
[[objectStore managedObjectContext] deleteObject:object];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Before looking up NSManagedObjectIDs, need to save to ensure we do not have
|
|
// temporary IDs for new objects prior to handing the objectIDs across threads
|
|
NSError* error = [objectStore save];
|
|
if (nil != error) {
|
|
NSDictionary* infoDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:response, @"response", error, @"error", nil] retain];
|
|
[self performSelectorOnMainThread:@selector(informDelegateOfObjectLoadErrorWithInfoDictionary:) withObject:infoDictionary waitUntilDone:NO];
|
|
} else {
|
|
// NOTE: Passing Core Data objects across threads is not safe.
|
|
// Iterate over each model and coerce Core Data objects into ID's to pass across the threads.
|
|
// The object ID's will be deserialized back into objects on the main thread before the delegate is called back
|
|
NSMutableArray* models = [NSMutableArray arrayWithCapacity:[results count]];
|
|
for (id object in results) {
|
|
if ([object isKindOfClass:[NSManagedObject class]]) {
|
|
[models addObject:[(NSManagedObject*)object objectID]];
|
|
} else {
|
|
[models addObject:object];
|
|
}
|
|
}
|
|
|
|
NSDictionary* infoDictionary = [[NSDictionary dictionaryWithObjectsAndKeys:response, @"response", models, @"models", nil] retain];
|
|
[self performSelectorOnMainThread:@selector(informDelegateOfObjectLoadWithInfoDictionary:) withObject:infoDictionary waitUntilDone:NO];
|
|
}
|
|
|
|
[pool release];
|
|
}
|
|
|
|
- (void)loadObjectsFromResponse:(RKResponse*)response {
|
|
_response = [response retain];
|
|
|
|
if (NO == [self encounteredErrorWhileProcessingRequest:response]) {
|
|
// TODO: When other mapping formats are supported, unwind this assumption...
|
|
if ([response isSuccessful] && [response isJSON]) {
|
|
[self performSelectorInBackground:@selector(processLoadModelsInBackground:) withObject:response];
|
|
} else {
|
|
NSLog(@"Encountered unexpected response code: %d (MIME Type: %@)", response.statusCode, response.MIMEType);
|
|
if ([_delegate respondsToSelector:@selector(objectLoaderDidLoadUnexpectedResponse:)]) {
|
|
[_delegate objectLoaderDidLoadUnexpectedResponse:self];
|
|
[self release];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// RKRequestDelegate
|
|
//
|
|
// If our delegate responds to the messages, forward them back...
|
|
|
|
- (void)requestDidStartLoad:(RKRequest*)request {
|
|
if ([_delegate respondsToSelector:@selector(requestDidStartLoad:)]) {
|
|
[_delegate requestDidStartLoad:request];
|
|
}
|
|
}
|
|
|
|
- (void)requestDidFinishLoad:(RKRequest*)request {
|
|
if ([_delegate respondsToSelector:@selector(requestDidFinishLoad:)]) {
|
|
[(NSObject<RKRequestDelegate>*)_delegate requestDidFinishLoad:request];
|
|
}
|
|
}
|
|
|
|
- (void)request:(RKRequest*)request didFailLoadWithError:(NSError*)error {
|
|
if ([_delegate respondsToSelector:@selector(request:didFailLoadWithError:)]) {
|
|
[(NSObject<RKRequestDelegate>*)_delegate request:request didFailLoadWithError:error];
|
|
}
|
|
}
|
|
|
|
- (void)requestDidCancelLoad:(RKRequest*)request {
|
|
[self release];
|
|
if ([_delegate respondsToSelector:@selector(requestDidCancelLoad:)]) {
|
|
[(NSObject<RKRequestDelegate>*)_delegate requestDidCancelLoad:request];
|
|
}
|
|
}
|
|
|
|
- (void)request:(RKRequest*)request didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
|
|
if ([_delegate respondsToSelector:@selector(request:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:)]) {
|
|
[(NSObject<RKRequestDelegate>*)_delegate request:request didSendBodyData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
|
|
}
|
|
}
|
|
|
|
@end
|