Merge branch 'reachability-queue-three20'

Conflicts:
	Code/Network/RKResponse.m
This commit is contained in:
Blake Watters
2010-12-23 14:10:17 -05:00
29 changed files with 1177 additions and 830 deletions

View File

@@ -10,3 +10,5 @@
#import "RKRequest.h"
#import "RKResponse.h"
#import "RKRequestSerializable.h"
#import "RKReachabilityObserver.h"
#import "RKRequestQueue.h"

View File

@@ -10,6 +10,7 @@
#import "RKParams.h"
#import "RKResponse.h"
#import "NSDictionary+RKRequestSerialization.h"
#import "RKReachabilityObserver.h"
/**
* RKClient exposes the low level client interface for working
@@ -21,6 +22,7 @@
NSString* _username;
NSString* _password;
NSMutableDictionary* _HTTPHeaders;
RKReachabilityObserver* _baseURLReachabilityObserver;
}
/**
@@ -43,6 +45,12 @@
*/
@property(nonatomic, readonly) NSDictionary* HTTPHeaders;
/**
* The RKReachabilityObserver used to monitor whether or not the client has a connection
* path to the baseURL
*/
@property(nonatomic, readonly) RKReachabilityObserver* baseURLReachabilityObserver;
/**
* Return the configured singleton instance of the Rest client
*/
@@ -95,11 +103,13 @@
*/
- (NSURL*)URLForResourcePath:(NSString *)resourcePath queryParams:(NSDictionary*)queryParams;
- (void)setupRequest:(RKRequest*)request;
/**
* Return a request object targetted at a resource path relative to the base URL. By default the method is set to GET
* All headers set on the client will automatically be applied to the request as well.
*/
- (RKRequest*)requestWithResourcePath:(NSString*)resourcePath delegate:(id)delegate callback:(SEL)callback;
- (RKRequest*)requestWithResourcePath:(NSString*)resourcePath delegate:(id)delegate;
///////////////////////////////////////////////////////////////////////////////////////////////////////////
// Asynchronous Helper Methods
@@ -112,31 +122,31 @@
*/
/**
* Fetch a resource via an HTTP GET and invoke a callback with the result
* Fetch a resource via an HTTP GET
*/
- (RKRequest*)get:(NSString*)resourcePath delegate:(id)delegate callback:(SEL)callback;
- (RKRequest*)get:(NSString*)resourcePath delegate:(id)delegate;
/**
* Fetch a resource via an HTTP GET with a dictionary of params and invoke a callback with the resulting payload
* Fetch a resource via an HTTP GET with a dictionary of params
*
* Note that this request _only_ allows NSDictionary objects as the params. The dictionary will be coerced into a URL encoded
* string and then appended to the resourcePath as the query string of the request.
*/
- (RKRequest*)get:(NSString*)resourcePath queryParams:(NSDictionary*)queryParams delegate:(id)delegate callback:(SEL)callback;
- (RKRequest*)get:(NSString*)resourcePath queryParams:(NSDictionary*)queryParams delegate:(id)delegate;
/**
* Create a resource via an HTTP POST with a set of form parameters and invoke a callback with the resulting payload
* Create a resource via an HTTP POST with a set of form parameters
*/
- (RKRequest*)post:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate callback:(SEL)callback;
- (RKRequest*)post:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate;
/**
* Update a resource via an HTTP PUT and invoke a callback with the resulting payload
* Update a resource via an HTTP PUT
*/
- (RKRequest*)put:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate callback:(SEL)callback;
- (RKRequest*)put:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate;
/**
* Destroy a resource via an HTTP DELETE and invoke a callback with the resulting payload
* Destroy a resource via an HTTP DELETE
*/
- (RKRequest*)delete:(NSString*)resourcePath delegate:(id)delegate callback:(SEL)callback;
- (RKRequest*)delete:(NSString*)resourcePath delegate:(id)delegate;
@end

View File

@@ -24,6 +24,7 @@ static RKClient* sharedClient = nil;
@synthesize username = _username;
@synthesize password = _password;
@synthesize HTTPHeaders = _HTTPHeaders;
@synthesize baseURLReachabilityObserver = _baseURLReachabilityObserver;
+ (RKClient*)sharedClient {
return sharedClient;
@@ -80,20 +81,13 @@ static RKClient* sharedClient = nil;
}
- (BOOL)isNetworkAvailable {
Boolean success;
const char *host_name = "google.com"; // your data source host name
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(NULL, host_name);
#ifdef TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
SCNetworkReachabilityFlags flags;
#else
SCNetworkConnectionFlags flags;
#endif
success = SCNetworkReachabilityGetFlags(reachability, &flags);
BOOL isNetworkAvailable = success && (flags & kSCNetworkFlagsReachable) && !(flags & kSCNetworkFlagsConnectionRequired);
CFRelease(reachability);
BOOL isNetworkAvailable = NO;
if (self.baseURLReachabilityObserver) {
isNetworkAvailable = [self.baseURLReachabilityObserver isNetworkReachable];
} else {
RKReachabilityObserver* googleObserver = [RKReachabilityObserver reachabilityObserverWithHostName:@"google.com"];
isNetworkAvailable = [googleObserver isNetworkReachable];
}
return isNetworkAvailable;
}
@@ -119,8 +113,18 @@ static RKClient* sharedClient = nil;
[_HTTPHeaders setValue:value forKey:header];
}
- (RKRequest*)requestWithResourcePath:(NSString*)resourcePath delegate:(id)delegate callback:(SEL)callback {
RKRequest* request = [[RKRequest alloc] initWithURL:[self URLForResourcePath:resourcePath] delegate:delegate callback:callback];
- (void)setBaseURL:(NSString*)baseURL {
[_baseURL release];
_baseURL = nil;
_baseURL = [baseURL retain];
[_baseURLReachabilityObserver release];
_baseURLReachabilityObserver = nil;
_baseURLReachabilityObserver = [[RKReachabilityObserver reachabilityObserverWithHostName:baseURL] retain];
}
- (RKRequest*)requestWithResourcePath:(NSString*)resourcePath delegate:(id)delegate {
RKRequest* request = [[RKRequest alloc] initWithURL:[self URLForResourcePath:resourcePath] delegate:delegate];
[self setupRequest:request];
[request autorelease];
@@ -131,8 +135,8 @@ static RKClient* sharedClient = nil;
// Asynchronous Requests
///////////////////////////////////////////////////////////////////////////////////////////////////////////
- (RKRequest*)load:(NSString*)resourcePath method:(RKRequestMethod)method params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate callback:(SEL)callback {
RKRequest* request = [[RKRequest alloc] initWithURL:[self URLForResourcePath:resourcePath] delegate:delegate callback:callback];
- (RKRequest*)load:(NSString*)resourcePath method:(RKRequestMethod)method params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate {
RKRequest* request = [[RKRequest alloc] initWithURL:[self URLForResourcePath:resourcePath] delegate:delegate];
[self setupRequest:request];
[request autorelease];
request.params = params;
@@ -142,25 +146,25 @@ static RKClient* sharedClient = nil;
return request;
}
- (RKRequest*)get:(NSString*)resourcePath delegate:(id)delegate callback:(SEL)callback {
return [self load:resourcePath method:RKRequestMethodGET params:nil delegate:delegate callback:callback];
- (RKRequest*)get:(NSString*)resourcePath delegate:(id)delegate {
return [self load:resourcePath method:RKRequestMethodGET params:nil delegate:delegate];
}
- (RKRequest*)get:(NSString*)resourcePath queryParams:(NSDictionary*)queryParams delegate:(id)delegate callback:(SEL)callback {
- (RKRequest*)get:(NSString*)resourcePath queryParams:(NSDictionary*)queryParams delegate:(id)delegate {
NSString* resourcePathWithQueryString = [NSString stringWithFormat:@"%@?%@", resourcePath, [queryParams URLEncodedString]];
return [self load:resourcePathWithQueryString method:RKRequestMethodGET params:nil delegate:delegate callback:callback];
return [self load:resourcePathWithQueryString method:RKRequestMethodGET params:nil delegate:delegate];
}
- (RKRequest*)post:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate callback:(SEL)callback {
return [self load:resourcePath method:RKRequestMethodPOST params:params delegate:delegate callback:callback];
- (RKRequest*)post:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate {
return [self load:resourcePath method:RKRequestMethodPOST params:params delegate:delegate];
}
- (RKRequest*)put:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate callback:(SEL)callback {
return [self load:resourcePath method:RKRequestMethodPUT params:params delegate:delegate callback:callback];
- (RKRequest*)put:(NSString*)resourcePath params:(NSObject<RKRequestSerializable>*)params delegate:(id)delegate {
return [self load:resourcePath method:RKRequestMethodPUT params:params delegate:delegate];
}
- (RKRequest*)delete:(NSString*)resourcePath delegate:(id)delegate callback:(SEL)callback {
return [self load:resourcePath method:RKRequestMethodDELETE params:nil delegate:delegate callback:callback];
- (RKRequest*)delete:(NSString*)resourcePath delegate:(id)delegate {
return [self load:resourcePath method:RKRequestMethodDELETE params:nil delegate:delegate];
}
@end

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

@@ -0,0 +1,55 @@
//
// RKReachabilityObserver.h
// RestKit
//
// Created by Blake Watters on 9/14/10.
// Copyright 2010 RestKit. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <SystemConfiguration/SystemConfiguration.h>
/**
* Posted when the network state has changed
*/
extern NSString* const RKReachabilityStateChangedNotification;
typedef enum {
RKReachabilityNotReachable,
RKReachabilityReachableViaWiFi,
RKReachabilityReachableViaWWAN
} RKReachabilityNetworkStatus;
/**
* Provides a notification based interface for monitoring changes
* to network status
*/
@interface RKReachabilityObserver : NSObject {
SCNetworkReachabilityRef _reachabilityRef;
}
/**
* Create a new reachability observer against a given hostname. The observer
* will monitor the ability to reach the specified hostname and emit notifications
* when its reachability status changes.
*
* Note that the observer will be scheduled in the current run loop.
*/
+ (RKReachabilityObserver*)reachabilityObserverWithHostName:(NSString*)hostName;
/**
* Returns the current network status
*/
- (RKReachabilityNetworkStatus)networkStatus;
/**
* Returns YES when the Internet is reachable (via WiFi or WWAN)
*/
- (BOOL)isNetworkReachable;
/**
* Returns YES when WWAN may be available, but not active until a connection has been established.
*/
- (BOOL)isConnectionRequired;
@end

View File

@@ -0,0 +1,145 @@
//
// RKReachabilityObserver.m
// RestKit
//
// Created by Blake Watters on 9/14/10.
// Copyright 2010 RestKit. All rights reserved.
//
#import "RKReachabilityObserver.h"
#import <UIKit/UIKit.h>
// Constants
NSString* const RKReachabilityStateChangedNotification = @"RKReachabilityStateChangedNotification";
static void ReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info) {
#pragma unused (target, flags)
// We're on the main RunLoop, so an NSAutoreleasePool is not necessary, but is added defensively
// in case someone uses the Reachablity object in a different thread.
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
RKReachabilityObserver* observer = (RKReachabilityObserver*) info;
// Post a notification to notify the client that the network reachability changed.
[[NSNotificationCenter defaultCenter] postNotificationName:RKReachabilityStateChangedNotification object:observer];
[pool release];
}
#pragma mark -
@interface RKReachabilityObserver (Private)
// Internal initializer
- (id)initWithReachabilityRef:(SCNetworkReachabilityRef)reachabilityRef;
- (void)scheduleObserver;
- (void)unscheduleObserver;
@end
@implementation RKReachabilityObserver
+ (RKReachabilityObserver*)reachabilityObserverWithHostName:(NSString*)hostName {
RKReachabilityObserver* observer = nil;
SCNetworkReachabilityRef reachabilityRef = SCNetworkReachabilityCreateWithName(NULL, [hostName UTF8String]);
if (nil != reachabilityRef) {
observer = [[[self alloc] initWithReachabilityRef:reachabilityRef] autorelease];
}
return observer;
}
- (id)initWithReachabilityRef:(SCNetworkReachabilityRef)reachabilityRef {
if (self = [self init]) {
_reachabilityRef = reachabilityRef;
[self scheduleObserver];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(scheduleObserver)
name:UIApplicationDidBecomeActiveNotification
object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(unscheduleObserver)
name:UIApplicationWillResignActiveNotification
object:nil];
}
return self;
}
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
[self unscheduleObserver];
if (_reachabilityRef) {
CFRelease(_reachabilityRef);
}
[super dealloc];
}
- (RKReachabilityNetworkStatus)networkStatus {
NSAssert(_reachabilityRef != NULL, @"currentNetworkStatus called with NULL reachabilityRef");
RKReachabilityNetworkStatus status = RKReachabilityNotReachable;
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
if ((flags & kSCNetworkReachabilityFlagsReachable) == 0) {
// if target host is not reachable
return RKReachabilityNotReachable;
}
if ((flags & kSCNetworkReachabilityFlagsConnectionRequired) == 0) {
// if target host is reachable and no connection is required
// then we'll assume (for now) that your on Wi-Fi
status = RKReachabilityReachableViaWiFi;
}
if ((((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) ||
(flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)) {
// ... and the connection is on-demand (or on-traffic) if the
// calling application is using the CFSocketStream or higher APIs
if ((flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0) {
// ... and no [user] intervention is needed
status = RKReachabilityReachableViaWiFi;
}
}
if ((flags & kSCNetworkReachabilityFlagsIsWWAN) == kSCNetworkReachabilityFlagsIsWWAN) {
// ... but WWAN connections are OK if the calling application
// is using the CFNetwork (CFSocketStream?) APIs.
status = RKReachabilityReachableViaWWAN;
}
}
return status;
}
- (BOOL)isNetworkReachable {
return (RKReachabilityNotReachable != [self networkStatus]);
}
- (BOOL)isConnectionRequired {
NSAssert(_reachabilityRef != NULL, @"connectionRequired called with NULL reachabilityRef");
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(_reachabilityRef, &flags)) {
return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
}
return NO;
}
#pragma mark Observer scheduling
- (void)scheduleObserver {
SCNetworkReachabilityContext context = {0, self, NULL, NULL, NULL};
if (SCNetworkReachabilitySetCallback(_reachabilityRef, ReachabilityCallback, &context)) {
if (NO == SCNetworkReachabilityScheduleWithRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode)) {
NSLog(@"Warning -- Unable to schedule reachability observer in current run loop.");
}
}
}
- (void)unscheduleObserver {
if (nil != _reachabilityRef) {
SCNetworkReachabilityUnscheduleFromRunLoop(_reachabilityRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
}
}
@end

View File

@@ -22,6 +22,7 @@ typedef enum RKRequestMethod {
} RKRequestMethod;
@class RKResponse;
@protocol RKRequestDelegate;
@interface RKRequest : NSObject {
NSURL* _URL;
@@ -29,12 +30,13 @@ typedef enum RKRequestMethod {
NSURLConnection* _connection;
NSDictionary* _additionalHTTPHeaders;
NSObject<RKRequestSerializable>* _params;
id _delegate;
SEL _callback;
NSObject<RKRequestDelegate>* _delegate;
id _userData;
NSString* _username;
NSString* _password;
RKRequestMethod _method;
BOOL _isLoading;
BOOL _isLoaded;
}
/**
@@ -42,6 +44,11 @@ typedef enum RKRequestMethod {
*/
@property(nonatomic, readonly) NSURL* URL;
/**
* The resourcePath portion of this loader's URL
*/
@property (nonatomic, readonly) NSString* resourcePath;
/**
* The HTTP verb the request is sent via
*
@@ -61,12 +68,7 @@ typedef enum RKRequestMethod {
* If the object implements the RKRequestDelegate protocol,
* it will receive request lifecycle event messages.
*/
@property(nonatomic, assign) id delegate;
/**
* The selector to invoke when the request is completed
*/
@property(nonatomic, assign) SEL callback;
@property(nonatomic, assign) NSObject<RKRequestDelegate>* delegate;
/**
* A Dictionary of additional HTTP Headers to send with the request
@@ -100,7 +102,7 @@ typedef enum RKRequestMethod {
/**
* Return a REST request that is ready for dispatching
*/
+ (RKRequest*)requestWithURL:(NSURL*)URL delegate:(id)delegate callback:(SEL)callback;
+ (RKRequest*)requestWithURL:(NSURL*)URL delegate:(id)delegate;
/**
* Initialize a synchronous request
@@ -110,10 +112,11 @@ typedef enum RKRequestMethod {
/**
* Initialize a REST request and prepare it for dispatching
*/
- (id)initWithURL:(NSURL*)URL delegate:(id)delegate callback:(SEL)callback;
- (id)initWithURL:(NSURL*)URL delegate:(id)delegate;
/**
* Send the request asynchronously
* Send the request asynchronously. It will be added to the queue and
* dispatched as soon as possible.
*/
- (void)send;
@@ -122,6 +125,18 @@ typedef enum RKRequestMethod {
*/
- (RKResponse*)sendSynchronously;
/**
* Callback performed to notify the request that the underlying NSURLConnection
* has failed with an error.
*/
- (void)didFailLoadWithError:(NSError*)error;
/**
* Callback performed to notify the request that the underlying NSURLConnection
* has completed with a response.
*/
- (void)didFinishLoad:(RKResponse*)response;
/**
* Cancels the underlying URL connection
*/
@@ -147,6 +162,16 @@ typedef enum RKRequestMethod {
*/
- (BOOL)isDELETE;
/**
* Returns YES when this request is in-progress
*/
- (BOOL)isLoading;
/**
* Returns YES when this request has been completed
*/
- (BOOL)isLoaded;
@end
/**
@@ -154,18 +179,13 @@ typedef enum RKRequestMethod {
*
* Modeled off of TTURLRequest
*/
@protocol RKRequestDelegate
@protocol RKRequestDelegate
@optional
/**
* Sent when a request has started loading
*/
- (void)requestDidStartLoad:(RKRequest*)request;
/**
* Sent when a request has finished loading
*/
- (void)requestDidFinishLoad:(RKRequest*)request;
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response;
/**
* Sent when a request has failed due to an error
@@ -173,9 +193,9 @@ typedef enum RKRequestMethod {
- (void)request:(RKRequest*)request didFailLoadWithError:(NSError*)error;
/**
* Sent when a request has been canceled
* Sent when a request has started loading
*/
- (void)requestDidCancelLoad:(RKRequest*)request;
- (void)requestDidStartLoad:(RKRequest*)request;
/**
* Sent when a request has uploaded data to the remote site

View File

@@ -7,20 +7,21 @@
//
#import "RKRequest.h"
#import "RKRequestQueue.h"
#import "RKResponse.h"
#import "NSDictionary+RKRequestSerialization.h"
#import "RKNotifications.h"
#import "RKClient.h"
#import "../Support/Support.h"
#import "RKURL.h"
@implementation RKRequest
@synthesize URL = _URL, URLRequest = _URLRequest, delegate = _delegate, callback = _callback, additionalHTTPHeaders = _additionalHTTPHeaders,
@synthesize URL = _URL, URLRequest = _URLRequest, delegate = _delegate, additionalHTTPHeaders = _additionalHTTPHeaders,
params = _params, userData = _userData, username = _username, password = _password, method = _method;
+ (RKRequest*)requestWithURL:(NSURL*)URL delegate:(id)delegate callback:(SEL)callback {
RKRequest* request = [[RKRequest alloc] initWithURL:URL delegate:delegate callback:callback];
[request autorelease];
return request;
+ (RKRequest*)requestWithURL:(NSURL*)URL delegate:(id)delegate {
return [[[RKRequest alloc] initWithURL:URL delegate:delegate] autorelease];
}
- (id)initWithURL:(NSURL*)URL {
@@ -28,30 +29,38 @@
_URL = [URL retain];
_URLRequest = [[NSMutableURLRequest alloc] initWithURL:_URL];
_connection = nil;
_isLoading = NO;
_isLoaded = NO;
}
return self;
}
- (id)initWithURL:(NSURL*)URL delegate:(id)delegate callback:(SEL)callback {
- (id)initWithURL:(NSURL*)URL delegate:(id)delegate {
if (self = [self initWithURL:URL]) {
_delegate = delegate;
_callback = callback;
}
return self;
}
- (void)dealloc {
self.delegate = nil;
[_connection cancel];
[_connection release];
_connection = nil;
[_userData release];
_userData = nil;
[_URL release];
_URL = nil;
[_URLRequest release];
_URLRequest = nil;
[_params release];
_params = nil;
[_additionalHTTPHeaders release];
_additionalHTTPHeaders = nil;
[_username release];
_username = nil;
[_password release];
_password = nil;
[super dealloc];
}
@@ -126,38 +135,96 @@
}
- (void)send {
[self addHeadersToRequest];
NSString* body = [[NSString alloc] initWithData:[_URLRequest HTTPBody] encoding:NSUTF8StringEncoding];
NSLog(@"Sending %@ request to URL %@. HTTP Body: %@", [self HTTPMethod], [[self URL] absoluteString], body);
[body release];
NSDate* sentAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[self HTTPMethod], @"HTTPMethod", [self URL], @"URL", sentAt, @"sentAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKRequestSentNotification object:self userInfo:userInfo];
RKResponse* response = [[[RKResponse alloc] initWithRequest:self] autorelease];
_connection = [[NSURLConnection connectionWithRequest:_URLRequest delegate:response] retain];
[[RKRequestQueue sharedQueue] sendRequest:self];
}
- (void)fireAsynchronousRequest {
if ([[RKClient sharedClient] isNetworkAvailable]) {
[self addHeadersToRequest];
NSString* body = [[NSString alloc] initWithData:[_URLRequest HTTPBody] encoding:NSUTF8StringEncoding];
NSLog(@"Sending %@ request to URL %@. HTTP Body: %@", [self HTTPMethod], [[self URL] absoluteString], body);
[body release];
NSDate* sentAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[self HTTPMethod], @"HTTPMethod", [self URL], @"URL", sentAt, @"sentAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKRequestSentNotification object:self userInfo:userInfo];
_isLoading = YES;
RKResponse* response = [[[RKResponse alloc] initWithRequest:self] autorelease];
_connection = [[NSURLConnection connectionWithRequest:_URLRequest delegate:response] retain];
} else {
NSString* errorMessage = [NSString stringWithFormat:@"The client is unable to contact the resource at %@", [[self URL] absoluteString]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
errorMessage, NSLocalizedDescriptionKey,
nil];
NSError* error = [NSError errorWithDomain:RKRestKitErrorDomain code:RKRequestBaseURLOfflineError userInfo:userInfo];
[self didFailLoadWithError:error];
}
}
- (RKResponse*)sendSynchronously {
[self addHeadersToRequest];
NSString* body = [[NSString alloc] initWithData:[_URLRequest HTTPBody] encoding:NSUTF8StringEncoding];
NSLog(@"Sending synchronous %@ request to URL %@. HTTP Body: %@", [self HTTPMethod], [[self URL] absoluteString], body);
[body release];
NSDate* sentAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[self HTTPMethod], @"HTTPMethod", [self URL], @"URL", sentAt, @"sentAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKRequestSentNotification object:self userInfo:userInfo];
NSURLResponse* URLResponse = nil;
NSError* error = nil;
NSData* payload = [NSURLConnection sendSynchronousRequest:_URLRequest returningResponse:&URLResponse error:&error];
return [[[RKResponse alloc] initWithSynchronousRequest:self URLResponse:URLResponse body:payload error:error] autorelease];
NSData* payload = nil;
RKResponse* response = nil;
if ([[RKClient sharedClient] isNetworkAvailable]) {
[self addHeadersToRequest];
NSString* body = [[NSString alloc] initWithData:[_URLRequest HTTPBody] encoding:NSUTF8StringEncoding];
NSLog(@"Sending synchronous %@ request to URL %@. HTTP Body: %@", [self HTTPMethod], [[self URL] absoluteString], body);
[body release];
NSDate* sentAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[self HTTPMethod], @"HTTPMethod", [self URL], @"URL", sentAt, @"sentAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKRequestSentNotification object:self userInfo:userInfo];
_isLoading = YES;
payload = [NSURLConnection sendSynchronousRequest:_URLRequest returningResponse:&URLResponse error:&error];
response = [[[RKResponse alloc] initWithSynchronousRequest:self URLResponse:URLResponse body:payload error:error] autorelease];
} else {
NSString* errorMessage = [NSString stringWithFormat:@"The client is unable to contact the resource at %@", [[self URL] absoluteString]];
NSDictionary *userInfo = [NSDictionary dictionaryWithObjectsAndKeys:
errorMessage, NSLocalizedDescriptionKey,
nil];
error = [NSError errorWithDomain:RKRestKitErrorDomain code:RKRequestBaseURLOfflineError userInfo:userInfo];
[self didFailLoadWithError:error];
// TODO: Is this needed here? Or can we just return a nil response and everyone will be happy??
response = [[[RKResponse alloc] initWithSynchronousRequest:self URLResponse:URLResponse body:payload error:error] autorelease];
}
return response;
}
- (void)cancel {
[_connection cancel];
[_connection release];
_connection = nil;
if ([_delegate respondsToSelector:@selector(requestDidCancelLoad:)]) {
[_delegate requestDidCancelLoad:self];
_isLoading = NO;
}
- (void)didFailLoadWithError:(NSError*)error {
_isLoading = NO;
if ([_delegate respondsToSelector:@selector(request:didFailLoadWithError:)]) {
[_delegate request:self didFailLoadWithError:error];
}
NSDate* receivedAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[self HTTPMethod], @"HTTPMethod",
[self URL], @"URL", receivedAt, @"receivedAt", error, @"error", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKRequestFailedWithErrorNotification object:self userInfo:userInfo];
}
- (void)didFinishLoad:(RKResponse*)response {
_isLoading = NO;
_isLoaded = YES;
if ([_delegate respondsToSelector:@selector(request:didLoadResponse:)]) {
[_delegate request:self didLoadResponse:response];
}
NSDate* receivedAt = [NSDate date];
NSDictionary* userInfo = [NSDictionary dictionaryWithObjectsAndKeys:[self HTTPMethod], @"HTTPMethod", [self URL], @"URL", receivedAt, @"receivedAt", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kRKResponseReceivedNotification object:response userInfo:userInfo];
}
- (BOOL)isGET {
@@ -176,4 +243,21 @@
return _method == RKRequestMethodDELETE;
}
- (BOOL)isLoading {
return _isLoading;
}
- (BOOL)isLoaded {
return _isLoaded;
}
- (NSString*)resourcePath {
NSString* resourcePath = nil;
if ([self.URL isKindOfClass:[RKURL class]]) {
RKURL* url = (RKURL*)self.URL;
resourcePath = url.resourcePath;
}
return resourcePath;
}
@end

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,163 @@
//
// 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 (RKRequest* request in _requests) {
if (![request isLoading] && ![request isLoaded] && _totalLoading < kMaxConcurrentLoads) {
++_totalLoading;
[self dispatchRequest:request];
}
}
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 {
[_requests addObject:request];
[self loadNextInQueue];
}
- (void)cancelRequest:(RKRequest*)request loadNext:(BOOL)loadNext {
if ([_requests containsObject:request] && ![request isLoaded]) {
[request cancel];
request.delegate = nil;
[_requests removeObject:request];
_totalLoading--;
if (loadNext) {
[self loadNextInQueue];
}
}
}
- (void)cancelRequest:(RKRequest*)request {
[self cancelRequest:request loadNext:YES];
}
- (void)cancelRequestsWithDelegate:(NSObject<RKRequestDelegate>*)delegate {
NSArray* requestsCopy = [NSArray arrayWithArray:_requests];
for (RKRequest* request in requestsCopy) {
if (request.delegate && request.delegate == delegate) {
[self cancelRequest:request];
}
}
}
- (void)cancelAllRequests {
NSArray* requestsCopy = [NSArray arrayWithArray:_requests];
for (RKRequest* request in requestsCopy) {
[self cancelRequest:request loadNext:NO];
}
}
/**
* Invoked via observation when a request has loaded a response. Remove
* the completed request from the queue and continue processing
*/
- (void)responseDidLoad:(NSNotification*)notification {
if (notification.object) {
// Our RKRequest completed and we're notified with an RKResponse object
if ([notification.object isKindOfClass:[RKResponse class]]) {
RKRequest* request = [(RKResponse*)notification.object request];
[_requests removeObject:request];
_totalLoading--;
// Our RKRequest failed and we're notified with the original RKRequest object
} else if ([notification.object isKindOfClass:[RKRequest class]]) {
RKRequest* request = (RKRequest*)notification.object;
[_requests removeObject:request];
_totalLoading--;
}
[self loadNextInQueue];
}
}
@end

View File

@@ -26,7 +26,9 @@
- (id)initWithRequest:(RKRequest*)request {
if (self = [self init]) {
_request = [request retain];
// We don't retain here as we're letting RKRequestQueue manage
// request ownership
_request = request;
}
return self;
@@ -34,7 +36,9 @@
- (id)initWithSynchronousRequest:(RKRequest*)request URLResponse:(NSURLResponse*)URLResponse body:(NSData*)body error:(NSError*)error {
if (self = [super init]) {
_request = [request retain];
// TODO: Does the lack of retain here cause problems with synchronous requests, since they
// are not being retained by the RKRequestQueue??
_request = request;
_httpURLResponse = [URLResponse retain];
_failureError = [error retain];
_body = [body retain];
@@ -47,11 +51,24 @@
- (void)dealloc {
[_httpURLResponse release];
[_body release];
[_request release];
[_failureError release];
[super dealloc];
}
// Handle basic auth
- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge {
if ([challenge previousFailureCount] == 0) {
NSURLCredential *newCredential;
newCredential=[NSURLCredential credentialWithUser:[NSString stringWithFormat:@"%@", _request.username]
password:[NSString stringWithFormat:@"%@", _request.password]
persistence:NSURLCredentialPersistenceNone];
[[challenge sender] useCredential:newCredential
forAuthenticationChallenge:challenge];
} else {
[[challenge sender] cancelAuthenticationChallenge:challenge];
}
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (NO == _loading) {
_loading = YES;
@@ -67,25 +84,13 @@
_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];
[[_request delegate] performSelector:[_request callback] withObject:self];
if ([[_request delegate] respondsToSelector:@selector(requestDidFinishLoad:)]) {
[[_request delegate] requestDidFinishLoad:_request];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
[_request didFinishLoad:self];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
_failureError = [error retain];
[[_request delegate] performSelector:[_request callback] withObject:self];
if ([[_request delegate] respondsToSelector:@selector(request:didFailLoadWithError:)]) {
[[_request delegate] request:_request didFailLoadWithError:error];
}
[_request didFailLoadWithError:_failureError];
}
- (void)connection:(NSURLConnection *)connection didSendBodyData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite {
@@ -94,7 +99,6 @@
}
}
- (NSString*)localizedStatusCodeString {
return [NSHTTPURLResponse localizedStringForStatusCode:[self statusCode]];
}