// // RKRequestModel.m // RestKit // // Created by Jeff Arena on 4/26/10. // Copyright 2010 RestKit. All rights reserved. // #import "RKRequestModel.h" #import "RKManagedObjectStore.h" #import @implementation RKRequestModel @synthesize objects = _objects; @synthesize loaded = _loaded; @synthesize resourcePath = _resourcePath; @synthesize params = _params; @synthesize objectLoader = _objectLoader; @synthesize method = _method; @synthesize refreshRate = _refreshRate; - (id)initWithResourcePath:(NSString*)resourcePath delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; } [self loadFromObjectCache]; return self; } /** * TODO: These initializers have to set the ivars so the state is configured before loadFromObjectCache is triggered. * Do NOT DRY these up by adding dependencies or loadFromObjectCache will fire too early. * * WARNING */ - (id)initWithResourcePath:(NSString*)resourcePath params:(NSDictionary*)params delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; _params = [params retain]; } [self loadFromObjectCache]; return self; } - (id)initWithResourcePath:(NSString*)resourcePath params:(NSDictionary*)params objectClass:(Class)klass delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; _params = [params retain]; _objectClass = [klass retain]; } [self loadFromObjectCache]; return self; } - (id)initWithResourcePath:(NSString*)resourcePath params:(NSDictionary*)params objectClass:(Class)klass keyPath:(NSString*)keyPath delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; _params = [params retain]; _objectClass = [klass retain]; _keyPath = [keyPath retain]; } [self loadFromObjectCache]; return self; } - (id)initWithResourcePath:(NSString*)resourcePath params:(NSDictionary*)params method:(RKRequestMethod)method delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; _params = [params retain]; _method = method; } [self loadFromObjectCache]; return self; } - (id)initWithResourcePath:(NSString*)resourcePath params:(NSDictionary*)params method:(RKRequestMethod)method objectClass:(Class)klass delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; _params = [params retain]; _objectClass = [klass retain]; _method = method; } [self loadFromObjectCache]; return self; } - (id)initWithResourcePath:(NSString*)resourcePath params:(NSDictionary*)params method:(RKRequestMethod)method objectClass:(Class)klass keyPath:(NSString*)keyPath delegate:(id)delegate { if (self = [self init]) { _resourcePath = [resourcePath retain]; _delegate = [delegate retain]; _params = [params retain]; _objectClass = [klass retain]; _keyPath = [keyPath retain]; _method = method; } [self loadFromObjectCache]; return self; } /////////////////////////////////////////////////////////////////////////////////////////////////// // NSObject - (id)init { if (self = [super init]) { _method = RKRequestMethodGET; _refreshRate = NSTimeIntervalSince1970; // Essentially, default to never } return self; } - (void)dealloc { [_delegate release]; [_objectLoader.request cancel]; [_objectLoader release]; [_params release]; [_objects release]; [_objectClass release]; [_keyPath release]; [super dealloc]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // RKRequestDelegate - (void)requestDidFinishLoad:(RKRequest*)request { [self saveLoadedTime]; if ([_delegate respondsToSelector:@selector(rkModelDidFinishLoad)]) { [_delegate rkModelDidFinishLoad]; } } //// TODO: I get replaced... - (void)request:(RKRequest*)request didFailLoadWithError:(NSError*)error { if ([_delegate respondsToSelector:@selector(rkModelDidFailLoadWithError:)]) { [_delegate rkModelDidFailLoadWithError:error]; } } - (void)requestDidCancelLoad:(RKRequest*)request { if ([_delegate respondsToSelector:@selector(rkModelDidCancelLoad)]) { [_delegate rkModelDidCancelLoad]; } } - (BOOL)needsRefresh { NSDate* loadedTime = self.loadedTime; BOOL outdated = NO; if (loadedTime) { outdated = -[loadedTime timeIntervalSinceNow] > _refreshRate; } else { [self saveLoadedTime]; } return outdated; } - (void)clearLoadedTime { [[NSUserDefaults standardUserDefaults] removeObjectForKey:_resourcePath]; } - (void)saveLoadedTime { [[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:_resourcePath]; } - (NSDate*)loadedTime { return [[NSUserDefaults standardUserDefaults] objectForKey:_resourcePath]; } - (void)loadFromObjectCache { RKManagedObjectStore* store = [RKObjectManager sharedManager].objectStore; NSArray* cachedObjects = nil; if (store.managedObjectCache) { cachedObjects = [RKManagedObject objectsWithFetchRequests:[store.managedObjectCache fetchRequestsForResourcePath:self.resourcePath]]; if (cachedObjects && [cachedObjects count] > 0) { if ([_delegate respondsToSelector:@selector(rkModelDidStartLoad)]) { [_delegate rkModelDidStartLoad]; } _objects = [cachedObjects retain]; _loaded = YES; if ([_delegate respondsToSelector:@selector(rkModelDidLoad)]) { [_delegate rkModelDidLoad]; } } else { [self load]; } } if ([self needsRefresh]) { [self load]; } } - (BOOL)errorWarrantsOptionToGoOffline:(NSError*)error { switch ([error code]) { case NSURLErrorTimedOut: case NSURLErrorCannotFindHost: case NSURLErrorCannotConnectToHost: case NSURLErrorNetworkConnectionLost: case NSURLErrorDNSLookupFailed: case NSURLErrorNotConnectedToInternet: case NSURLErrorInternationalRoamingOff: return YES; break; default: return NO; break; } } - (void)showAlertWithOptionToGoOfflineForError:(NSError*)error { UIAlertView* alert = [[[UIAlertView alloc] initWithTitle:TTLocalizedString(@"Network Error", @"") message:[error localizedDescription] delegate:self cancelButtonTitle:TTLocalizedString(@"OK", @"") otherButtonTitles:TTLocalizedString(@"Go Offline", @""), nil] autorelease]; [alert show]; } - (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex { // Go Offline button if (1 == buttonIndex) { [[RKObjectManager sharedManager] goOffline]; } } - (void)modelsDidLoad:(NSArray*)models { [models retain]; [_objects release]; _objects = models; _loaded = YES; // NOTE: You must finish load after clearing the loadingRequest and setting the loaded flag if ([_delegate respondsToSelector:@selector(rkModelDidLoad)]) { [_delegate rkModelDidLoad]; } } /////////////////////////////////////////////////////////////////////////////////////////////////// // RKModelLoaderDelegate // This callback is invoked after the request has been fully serviced. Finish the load here. - (void)objectLoader:(RKObjectLoader *)objectLoader didLoadObjects:(NSArray *)objects { [objectLoader release]; _objectLoader = nil; [self modelsDidLoad:objects]; } - (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError *)error { [objectLoader release]; _objectLoader = nil; if ([self errorWarrantsOptionToGoOffline:error]) { [self showAlertWithOptionToGoOfflineForError:error]; } else { [_delegate didFailLoadWithError:error]; } } - (void)objectLoaderDidLoadUnexpectedResponse:(RKObjectLoader*)objectLoader { [objectLoader release]; _objectLoader = nil; [_delegate didFailLoadWithError:nil]; } /////////////////////////////////////////////////////////////////////////////////////////////////// // public - (void)reset { [self clearLoadedTime]; } - (void)load { if ([[RKObjectManager sharedManager] isOnline]) { if ([_delegate respondsToSelector:@selector(rkModelDidStartLoad)]) { [_delegate rkModelDidStartLoad]; } _objectLoader = [[[RKObjectManager sharedManager] objectLoaderWithResourcePath:_resourcePath delegate:self] retain]; _objectLoader.method = _method; _objectLoader.objectClass = _objectClass; _objectLoader.keyPath = _keyPath; _objectLoader.params = _params; [_objectLoader send]; } } @end