First cut at queue support.

This commit is contained in:
Blake Watters
2010-12-01 14:56:16 -05:00
parent fbe8457dd7
commit a53f28e339
8 changed files with 236 additions and 9 deletions

View File

@@ -18,3 +18,4 @@
*/
extern NSString* const kRKRequestSentNotification;
extern NSString* const kRKResponseReceivedNotification;
extern NSString* const kRKRequestFailedWithErrorNotification;

View File

@@ -10,3 +10,4 @@
NSString* const kRKRequestSentNotification = @"kRKRequestSentNotification";
NSString* const kRKResponseReceivedNotification = @"kRKRespongReceivedNotification";
NSString* const kRKRequestFailedWithErrorNotification = @"kRKRequestFailedWithErrorNotification";

View File

@@ -61,11 +61,13 @@ typedef enum RKRequestMethod {
* If the object implements the RKRequestDelegate protocol,
* it will receive request lifecycle event messages.
*/
// TODO: Should be RKRequestDelegate instead of id
@property(nonatomic, assign) id delegate;
/**
* The selector to invoke when the request is completed
*/
// TODO: Eliminate callback in favor of a delegate method (requestDidLoadResponse:) for simplicity
@property(nonatomic, assign) SEL callback;
/**
@@ -113,7 +115,8 @@ typedef enum RKRequestMethod {
- (id)initWithURL:(NSURL*)URL delegate:(id)delegate callback:(SEL)callback;
/**
* Send the request asynchronously
* Send the request asynchronously. It will be added to the queue and
* dispatched as soon as possible.
*/
- (void)send;
@@ -154,6 +157,7 @@ typedef enum RKRequestMethod {
*
* Modeled off of TTURLRequest
*/
// TODO: Add a didLoadResponse: delegate method in place off callback
@protocol RKRequestDelegate
@optional

View File

@@ -7,6 +7,7 @@
//
#import "RKRequest.h"
#import "RKRequestQueue.h"
#import "RKResponse.h"
#import "NSDictionary+RKRequestSerialization.h"
#import "RKNotifications.h"
@@ -115,6 +116,10 @@
}
- (void)send {
[[RKRequestQueue sharedQueue] sendRequest:self];
}
- (void)fireAsynchronousRequest {
[self addHeadersToRequest];
NSString* body = [[NSString alloc] initWithData:[_URLRequest HTTPBody] encoding:NSUTF8StringEncoding];
NSLog(@"Sending %@ request to URL %@. HTTP Body: %@", [self HTTPMethod], [[self URL] absoluteString], body);

View File

@@ -0,0 +1,63 @@
//
// RKRequestQueue.h
// RestKit
//
// Created by Blake Watters on 12/1/10.
// Copyright 2010 Two Toasters. All rights reserved.
//
#import <Foundation/Foundation.h>
#import "RKRequest.h"
/**
* A lightweight queue implementation responsible
* for dispatching and managing RKRequest objects
*/
@interface RKRequestQueue : NSObject {
NSMutableArray* _requests;
NSInteger _totalLoading;
NSTimer* _queueTimer;
BOOL _suspended;
}
/**
* Gets the flag that determines if new load requests are allowed to reach the network.
*
* Because network requests tend to slow down performance, this property can be used to
* temporarily delay them. All requests made while suspended are queued, and when
* suspended becomes false again they are executed.
*/
@property (nonatomic) BOOL suspended;
/**
* Return the global queue
*/
+ (RKRequestQueue*)sharedQueue;
/**
* Set the global queue
*/
+ (void)setSharedQueue:(RKRequestQueue*)requestQueue;
/**
* Add an asynchronous request to the queue and send it as
* as soon as possible
*/
- (void)sendRequest:(RKRequest*)request;
/**
* Cancel a request that is in progress
*/
- (void)cancelRequest:(RKRequest*)request;
/**
* Cancel all requests with a given delegate
*/
- (void)cancelRequestsWithDelegate:(NSObject<RKRequestDelegate>*)delegate;
/**
* Cancel all active or pending requests.
*/
- (void)cancelAllRequests;
@end

View File

@@ -0,0 +1,141 @@
//
// RKRequestQueue.m
// RestKit
//
// Created by Blake Watters on 12/1/10.
// Copyright 2010 Two Toasters. All rights reserved.
//
#import "RKRequestQueue.h"
#import "RKResponse.h"
#import "RKNotifications.h"
static RKRequestQueue* gSharedQueue = nil;
static const NSTimeInterval kFlushDelay = 0.3;
static const NSTimeInterval kTimeout = 300.0;
static const NSInteger kMaxConcurrentLoads = 5;
@implementation RKRequestQueue
@synthesize suspended = _suspended;
+ (RKRequestQueue*)sharedQueue {
if (!gSharedQueue) {
gSharedQueue = [[RKRequestQueue alloc] init];
}
return gSharedQueue;
}
+ (void)setSharedQueue:(RKRequestQueue*)requestQueue {
if (gSharedQueue != requestQueue) {
[gSharedQueue release];
gSharedQueue = [requestQueue retain];
}
}
- (id)init {
if (self = [super init]) {
_requests = [[NSMutableArray alloc] init];
_suspended = NO;
_totalLoading = 0;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(responseDidLoad:)
name:kRKResponseReceivedNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(responseDidLoad:)
name:kRKRequestFailedWithErrorNotification
object:nil];
}
return self;
}
- (void)dealloc {
[_queueTimer invalidate];
[_requests release];
_requests = nil;
[super dealloc];
}
- (void)loadNextInQueueDelayed {
if (!_queueTimer) {
_queueTimer = [NSTimer scheduledTimerWithTimeInterval:kFlushDelay
target:self
selector:@selector(loadNextInQueue)
userInfo:nil
repeats:NO];
}
}
- (void)dispatchRequest:(RKRequest*)request {
[request performSelector:@selector(fireAsynchronousRequest)];
}
- (void)loadNextInQueue {
_queueTimer = nil;
for (int i = 0;
i < kMaxConcurrentLoads && _totalLoading < kMaxConcurrentLoads
&& _requests.count;
++i) {
RKRequest* request = [[_requests objectAtIndex:0] retain];
[_requests removeObjectAtIndex:0];
[self dispatchRequest:request];
[request release];
}
if (_requests.count && !_suspended) {
[self loadNextInQueueDelayed];
}
}
- (void)setSuspended:(BOOL)isSuspended {
_suspended = isSuspended;
if (!_suspended) {
[self loadNextInQueue];
} else if (_queueTimer) {
[_queueTimer invalidate];
_queueTimer = nil;
}
}
- (void)sendRequest:(RKRequest*)request {
if (_suspended || _totalLoading == kMaxConcurrentLoads) {
[_requests addObject:request];
} else {
++_totalLoading;
[self dispatchRequest:request];
}
}
- (void)cancelRequest:(RKRequest*)request {
[request cancel];
}
- (void)cancelRequestsWithDelegate:(NSObject<RKRequestDelegate>*)delegate {
for (RKRequest* request in _requests) {
if (request.delegate && request.delegate == delegate) {
[request cancel];
}
}
}
- (void)cancelAllRequests {
for (RKRequest* request in [[[_requests copy] autorelease] objectEnumerator]) {
[request cancel];
}
}
/**
* Invoked via observation when a request has loaded a response. Remove
* the completed request from the queue and continue processing
*/
- (void)responseDidLoad:(NSNotification*)notification {
_totalLoading--;
[self loadNextInQueue];
}
@end

View File

@@ -53,7 +53,7 @@
}
// Handle basic auth
-(void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:[NSString stringWithFormat:@"%@", _request.username]
@@ -81,16 +81,16 @@
_httpURLResponse = [response retain];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
NSDate* receivedAt = [NSDate date]; // TODO - Carry around this timestamp on the response or request?
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[_request HTTPMethod], @"HTTPMethod", [_request URL], @"URL", receivedAt, @"receivedAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKResponseReceivedNotification object:self userInfo:userInfo];
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[[_request delegate] performSelector:[_request callback] withObject:self];
if ([[_request delegate] respondsToSelector:@selector(requestDidFinishLoad:)]) {
[[_request delegate] requestDidFinishLoad:_request];
}
NSDate* receivedAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[_request HTTPMethod], @"HTTPMethod", [_request URL], @"URL", receivedAt, @"receivedAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKResponseReceivedNotification object:self userInfo:userInfo];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
@@ -100,6 +100,11 @@
if ([[_request delegate] respondsToSelector:@selector(request:didFailLoadWithError:)]) {
[[_request delegate] request:_request didFailLoadWithError:error];
}
NSDate* receivedAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[_request HTTPMethod], @"HTTPMethod",
[_request URL], @"URL", receivedAt, @"receivedAt", error, @"error", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKRequestFailedWithErrorNotification object:_request userInfo:userInfo];
}
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
@@ -108,7 +113,6 @@
}
}
- (NSString*)localizedStatusCodeString {
return [NSHTTPURLResponse localizedStringForStatusCode:[self statusCode]];
}