AFNetworking/Example/AFHTTPRequestOperation.m
2011-07-27 15:14:15 -05:00

269 lines
8.6 KiB
Objective-C

// AFHTTPOperation.m
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#import "AFHTTPRequestOperation.h"
#import "AFNetworkActivityIndicatorManager.h"
typedef enum {
AFHTTPOperationReadyState = 1,
AFHTTPOperationExecutingState = 2,
AFHTTPOperationFinishedState = 3,
AFHTTPOperationCancelledState = 4,
} AFHTTPOperationState;
NSString * const AFHTTPOperationDidStartNotification = @"com.alamofire.http-operation.start";
NSString * const AFHTTPOperationDidFinishNotification = @"com.alamofire.http-operation.finish";
typedef void (^AFHTTPRequestOperationCompletionBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error);
static inline NSString * AFKeyPathFromOperationState(AFHTTPOperationState state) {
switch (state) {
case AFHTTPOperationReadyState:
return @"isReady";
case AFHTTPOperationExecutingState:
return @"isExecuting";
case AFHTTPOperationFinishedState:
case AFHTTPOperationCancelledState:
return @"isFinished";
default:
return @"state";
}
}
static inline BOOL AFHTTPOperationStateTransitionIsValid(AFHTTPOperationState from, AFHTTPOperationState to) {
if (from == to) {
return NO;
}
switch (from) {
case AFHTTPOperationReadyState:
switch (to) {
case AFHTTPOperationExecutingState:
case AFHTTPOperationCancelledState:
return YES;
default:
return NO;
}
case AFHTTPOperationExecutingState:
switch (to) {
case AFHTTPOperationReadyState:
return NO;
default:
return YES;
}
case AFHTTPOperationFinishedState:
case AFHTTPOperationCancelledState:
return NO;
default:
return YES;
}
}
@interface AFHTTPRequestOperation ()
@property (nonatomic, assign) AFHTTPOperationState state;
@property (readwrite, nonatomic, retain) NSPort *port;
@property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator;
@property (readwrite, nonatomic, copy) AFHTTPRequestOperationCompletionBlock completion;
@end
@implementation AFHTTPRequestOperation
@synthesize state = _state;
@synthesize connection = _connection;
@synthesize runLoopModes = _runLoopModes;
@synthesize port = _port;
@synthesize request = _request;
@synthesize response = _response;
@synthesize error = _error;
@synthesize responseBody = _responseBody;
@synthesize dataAccumulator = _dataAccumulator;
@synthesize completion = _completion;
+ (id)operationWithRequest:(NSURLRequest *)urlRequest
completion:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error))completion
{
AFHTTPRequestOperation *operation = [[[self alloc] initWithRequest:urlRequest] autorelease];
operation.completion = completion;
return operation;
}
- (id)initWithRequest:(NSURLRequest *)urlRequest {
self = [super init];
if (!self) {
return nil;
}
self.request = urlRequest;
self.runLoopModes = [NSSet setWithObjects:NSDefaultRunLoopMode, NSRunLoopCommonModes, nil];
self.state = AFHTTPOperationReadyState;
return self;
}
- (void)dealloc {
[_runLoopModes release];
[_port release];
[_request release];
[_response release];
[_responseBody release];
[_dataAccumulator release];
[_connection release];
[_completion release];
[super dealloc];
}
- (void)setState:(AFHTTPOperationState)state {
if (!AFHTTPOperationStateTransitionIsValid(self.state, state)) {
return;
}
NSString *oldStateKey = AFKeyPathFromOperationState(self.state);
NSString *newStateKey = AFKeyPathFromOperationState(state);
[self willChangeValueForKey:newStateKey];
[self willChangeValueForKey:oldStateKey];
_state = state;
[self didChangeValueForKey:oldStateKey];
[self didChangeValueForKey:newStateKey];
switch (state) {
case AFHTTPOperationExecutingState:
[[AFNetworkActivityIndicatorManager sharedManager] startAnimating];
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidStartNotification object:self];
break;
case AFHTTPOperationFinishedState:
case AFHTTPOperationCancelledState:
[[AFNetworkActivityIndicatorManager sharedManager] stopAnimating];
[[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidFinishNotification object:self];
for (NSString *runLoopMode in self.runLoopModes) {
[[NSRunLoop currentRunLoop] removePort:self.port forMode:runLoopMode];
[self.connection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:runLoopMode];
}
CFRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]);
break;
default:
break;
}
}
- (NSString *)responseString {
return [[[NSString alloc] initWithData:self.responseBody encoding:NSUTF8StringEncoding] autorelease];
}
#pragma mark - NSOperation
- (BOOL)isReady {
return self.state == AFHTTPOperationReadyState;
}
- (BOOL)isExecuting {
return self.state == AFHTTPOperationExecutingState;
}
- (BOOL)isFinished {
return self.state == AFHTTPOperationFinishedState || self.isCancelled;
}
- (BOOL)isCancelled {
return self.state == AFHTTPOperationCancelledState;
}
- (BOOL)isConcurrent {
return YES;
}
- (void)start {
if (self.isFinished) {
return;
}
self.state = AFHTTPOperationExecutingState;
self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease];
self.port = [NSPort port];
NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
for (NSString *runLoopMode in self.runLoopModes) {
[self.connection scheduleInRunLoop:runLoop forMode:runLoopMode];
[runLoop addPort:self.port forMode:runLoopMode];
}
[self.connection start];
[runLoop run];
}
- (void)cancel {
self.state = AFHTTPOperationCancelledState;
[self.connection cancel];
}
#pragma mark - AFHTTPRequestOperation
- (void)finish {
if (self.isCancelled) {
return;
}
if (self.completion) {
self.completion(self.request, self.response, self.responseBody, self.error);
}
}
#pragma mark - NSURLConnection
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
self.response = (NSHTTPURLResponse *)response;
NSUInteger contentLength = MIN(MAX(abs(response.expectedContentLength), 1024), 1024 * 1024 * 8);
self.dataAccumulator = [NSMutableData dataWithCapacity:contentLength];
}
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[self.dataAccumulator appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
self.state = AFHTTPOperationFinishedState;
self.responseBody = [NSData dataWithData:self.dataAccumulator];
self.dataAccumulator = nil;
[self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:YES modes:[self.runLoopModes allObjects]];
}
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error {
self.state = AFHTTPOperationFinishedState;
self.error = error;
[self performSelectorOnMainThread:@selector(finish) withObject:nil waitUntilDone:YES modes:[self.runLoopModes allObjects]];
}
@end