Browse files

Significant performance improvments and API redesign

Removing Q*Operation dependency
  • Loading branch information...
2 parents ba98a2a + 3a4392e commit 3f901c81b36de941b4f314c6d2c6c8d287ba52d0 @mattt mattt committed Aug 3, 2011
Showing with 10,314 additions and 7,695 deletions.
  1. +0 −62 AFNetworking/AFHTTPOperation.h
  2. +0 −106 AFNetworking/AFHTTPOperation.m
  3. +56 −0 AFNetworking/AFHTTPRequestOperation.h
  4. +278 −0 AFNetworking/AFHTTPRequestOperation.m
  5. +13 −13 Example/Classes/AFImageRequest.h → AFNetworking/AFImageCache.h
  6. +60 −0 AFNetworking/AFImageCache.m
  7. +10 −41 AFNetworking/AFImageRequestOperation.h
  8. +43 −106 AFNetworking/AFImageRequestOperation.m
  9. +16 −33 AFNetworking/{AFCallback.m → AFJSONRequestOperation.h}
  10. +93 −0 AFNetworking/AFJSONRequestOperation.m
  11. +9 −10 AFNetworking/{AFCallback.h → AFNetworkActivityIndicatorManager.h}
  12. +57 −0 AFNetworking/AFNetworkActivityIndicatorManager.m
  13. +20 −10 AFNetworking/AFRestClient.h
  14. +64 −53 AFNetworking/AFRestClient.m
  15. +0 −245 AFNetworking/QHTTPOperation/QHTTPOperation.h
  16. +0 −653 AFNetworking/QHTTPOperation/QHTTPOperation.m
  17. +0 −119 AFNetworking/QHTTPOperation/QRunLoopOperation.h
  18. +0 −359 AFNetworking/QHTTPOperation/QRunLoopOperation.m
  19. +0 −1 AFNetworking/UIImage+AFNetworking.m
  20. +32 −0 AFNetworking/UIImageView+AFNetworking.h
  21. +95 −0 AFNetworking/UIImageView+AFNetworking.m
  22. +55 −63 Example/AFNetworking Example.xcodeproj/project.pbxproj
  23. +9,361 −5,649 ...Example.xcodeproj/project.xcworkspace/xcuserdata/mattt.xcuserdatad/UserInterfaceState.xcuserstate
  24. +0 −91 Example/Classes/AFImageRequest.m
  25. +2 −3 Example/Classes/Controllers/NearbySpotsViewController.h
  26. +33 −18 Example/Classes/Controllers/NearbySpotsViewController.m
  27. +1 −1 Example/Classes/Models/Spot.h
  28. +15 −10 Example/Classes/Models/Spot.m
  29. +1 −4 Example/Classes/Views/SpotTableViewCell.h
  30. +0 −45 Example/Classes/Views/SpotTableViewCell.m
View
62 AFNetworking/AFHTTPOperation.h
@@ -1,62 +0,0 @@
-// AFHTTPOperation.h
-//
-// 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 <Foundation/Foundation.h>
-#import "QHTTPOperation.h"
-#import "AFCallback.h"
-
-extern NSString * const AFHTTPOperationDidStartNotification;
-extern NSString * const AFHTTPOperationDidSucceedNotification;
-extern NSString * const AFHTTPOperationDidFailNotification;
-
-extern NSString * const AFHTTPOperationParsedDataErrorKey;
-
-@class AFHTTPOperationCallback;
-
-@interface AFHTTPOperation : QHTTPOperation {
-@private
- AFHTTPOperationCallback *_callback;
-}
-
-@property (nonatomic, retain) AFHTTPOperationCallback *callback;
-@property (readonly) NSString *responseString;
-
-+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback;
-- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback;
-
-@end
-
-#pragma mark - AFHTTPOperationCallback
-
-typedef void (^AFHTTPOperationSuccessBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSDictionary *data);
-typedef void (^AFHTTPOperationErrorBlock)(NSURLRequest *request, NSHTTPURLResponse *response, NSError *error);
-
-@protocol AFHTTPOperationCallback <NSObject>
-@optional
-+ (id)callbackWithSuccess:(AFHTTPOperationSuccessBlock)success;
-+ (id)callbackWithSuccess:(AFHTTPOperationSuccessBlock)success error:(AFHTTPOperationErrorBlock)error;
-@end
-
-@interface AFHTTPOperationCallback : AFCallback <AFHTTPOperationCallback>
-@property (readwrite, nonatomic, copy) AFHTTPOperationSuccessBlock successBlock;
-@property (readwrite, nonatomic, copy) AFHTTPOperationErrorBlock errorBlock;
-@end
View
106 AFNetworking/AFHTTPOperation.m
@@ -1,106 +0,0 @@
-// 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 "AFHTTPOperation.h"
-#import "JSONKit.h"
-
-NSString * const AFHTTPOperationDidStartNotification = @"com.alamofire.http-operation.start";
-NSString * const AFHTTPOperationDidSucceedNotification = @"com.alamofire.http-operation.success";
-NSString * const AFHTTPOperationDidFailNotification = @"com.alamofire.http-operation.failure";
-
-NSString * const AFHTTPOperationParsedDataErrorKey = @"com.alamofire.http-operation.error.parsed-data";
-
-@implementation AFHTTPOperation
-@synthesize callback = _callback;
-
-+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback {
- return [[[self alloc] initWithRequest:urlRequest callback:callback] autorelease];
-}
-
-- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFHTTPOperationCallback *)callback {
- self = [super initWithRequest:urlRequest];
- if (!self) {
- return nil;
- }
-
- self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/plain", nil];
- self.callback = callback;
-
- return self;
-}
-
-- (void)dealloc {
- [_callback release];
- [super dealloc];
-}
-
-- (NSString *)responseString {
- return [[[NSString alloc] initWithData:self.responseBody encoding:NSUTF8StringEncoding] autorelease];
-}
-
-#pragma mark - QRunLoopOperation
-
-- (void)operationDidStart {
- [super operationDidStart];
- [[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidStartNotification object:self];
-}
-
-- (void)finishWithError:(NSError *)error {
- [super finishWithError:error];
-
- NSDictionary *data = nil;
- if (self.contentTypeAcceptable) {
- if ([[self.lastResponse MIMEType] isEqualToString:@"application/json"]) {
- NSError *jsonError = nil;
- data = [[JSONDecoder decoder] parseJSONData:self.responseBody error:&jsonError];
- }
- }
-
- if (self.statusCodeAcceptable) {
- [[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidSucceedNotification object:self];
-
- if(self.callback.successBlock) {
- self.callback.successBlock(self.lastRequest, self.lastResponse, data);
- }
- } else {
- [[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidFailNotification object:self];
-
- NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[error userInfo]];
- [userInfo setValue:[NSHTTPURLResponse localizedStringForStatusCode:[self.lastResponse statusCode]] forKey:NSLocalizedDescriptionKey];
- [userInfo setValue:[[self.lastRequest URL] absoluteString] forKey:NSURLErrorFailingURLStringErrorKey];
- [userInfo setValue:data forKey:AFHTTPOperationParsedDataErrorKey];
-
- error = [[[NSError alloc] initWithDomain:NSURLErrorDomain code:[self.lastResponse statusCode] userInfo:userInfo] autorelease];
-
- if (self.callback.errorBlock) {
- self.callback.errorBlock(self.lastRequest, self.lastResponse, error);
- }
- }
-}
-
-@end
-
-#pragma mark - AFHTTPOperationCallback
-
-@implementation AFHTTPOperationCallback
-@dynamic successBlock, errorBlock;
-@end
View
56 AFNetworking/AFHTTPRequestOperation.h
@@ -0,0 +1,56 @@
+// AFHTTPOperation.h
+//
+// 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 <Foundation/Foundation.h>
+
+extern NSString * const AFHTTPOperationDidStartNotification;
+extern NSString * const AFHTTPOperationDidFinishNotification;
+
+@interface AFHTTPRequestOperation : NSOperation <NSURLConnectionDelegate, NSURLConnectionDataDelegate> {
+@private
+ NSURLConnection *_connection;
+ NSPort *_port;
+ NSSet *_runLoopModes;
+
+ NSURLRequest *_request;
+ NSHTTPURLResponse *_response;
+
+ NSData *_responseBody;
+ NSMutableData *_dataAccumulator;
+}
+
+@property (nonatomic, retain) NSURLConnection *connection;
+@property (nonatomic, retain) NSSet *runLoopModes;
+
+@property (nonatomic, retain) NSURLRequest *request;
+@property (nonatomic, retain) NSHTTPURLResponse *response;
+@property (nonatomic, retain) NSError *error;
+
+@property (nonatomic, retain) NSData *responseBody;
+@property (readonly) NSString *responseString;
+
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ completion:(void (^)(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error))completion;
+
+- (id)initWithRequest:(NSURLRequest *)urlRequest;
+
+@end
View
278 AFNetworking/AFHTTPRequestOperation.m
@@ -0,0 +1,278 @@
+// 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:
+ 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:
+ return YES;
+ default:
+ return NO;
+ }
+ case AFHTTPOperationExecutingState:
+ switch (to) {
+ case AFHTTPOperationReadyState:
+ return NO;
+ default:
+ return YES;
+ }
+ case AFHTTPOperationFinishedState:
+ return NO;
+ default:
+ return YES;
+ }
+}
+
+@interface AFHTTPRequestOperation ()
+@property (nonatomic, assign) AFHTTPOperationState state;
+@property (nonatomic, assign) BOOL isCancelled;
+@property (readwrite, nonatomic, retain) NSPort *port;
+@property (readwrite, nonatomic, retain) NSMutableData *dataAccumulator;
+@property (readwrite, nonatomic, copy) AFHTTPRequestOperationCompletionBlock completion;
+
+- (void)cleanup;
+@end
+
+@implementation AFHTTPRequestOperation
+@synthesize state = _state;
+@synthesize isCancelled = _isCancelled;
+@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)cleanup {
+ for (NSString *runLoopMode in self.runLoopModes) {
+ [[NSRunLoop currentRunLoop] removePort:self.port forMode:runLoopMode];
+ [self.connection unscheduleFromRunLoop:[NSRunLoop currentRunLoop] forMode:runLoopMode];
+ }
+ CFRunLoopStop([[NSRunLoop currentRunLoop] getCFRunLoop]);
+}
+
+- (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:
+ [[AFNetworkActivityIndicatorManager sharedManager] stopAnimating];
+ [[NSNotificationCenter defaultCenter] postNotificationName:AFHTTPOperationDidFinishNotification object:self];
+ [self cleanup];
+ 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)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.isCancelled = YES;
+
+ [self.connection cancel];
+
+ [self cleanup];
+}
+
+#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]];
+}
+
+- (NSCachedURLResponse *)connection:(NSURLConnection *)connection willCacheResponse:(NSCachedURLResponse *)cachedResponse {
+ if ([self isCancelled]) {
+ return nil;
+ }
+
+ return cachedResponse;
+}
+
+@end
View
26 Example/Classes/AFImageRequest.h → AFNetworking/AFImageCache.h
@@ -1,4 +1,4 @@
-// AFImageRequest.h
+// AFImageCache.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
@@ -20,20 +20,20 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
+#import <Foundation/Foundation.h>
#import "AFImageRequestOperation.h"
-@protocol AFImageRequester
-@required
-- (void)setImageURLString:(NSString *)urlString;
-- (void)setImageURLString:(NSString *)urlString options:(AFImageRequestOptions)options;
-@optional
-@property (nonatomic, copy) NSString *imageURLString;
-@end
+@interface AFImageCache : NSCache
+
++ (id)sharedImageCache;
+
+- (UIImage *)cachedImageForRequest:(NSURLRequest *)urlRequest
+ imageSize:(CGSize)imageSize
+ options:(AFImageRequestOptions)options;
-@interface AFImageRequest : NSObject
+- (void)cacheImage:(UIImage *)image
+ forRequest:(NSURLRequest *)urlRequest
+ imageSize:(CGSize)imageSize
+ options:(AFImageRequestOptions)options;
-+ (void)requestImageWithURLString:(NSString *)urlString options:(AFImageRequestOptions)options block:(void (^)(UIImage *image))block;
-+ (void)requestImageWithURLString:(NSString *)urlString size:(CGSize)imageSize options:(AFImageRequestOptions)options block:(void (^)(UIImage *image))block;
-+ (void)cancelImageRequestOperationsForURLString:(NSString *)urlString;
-+ (void)cancelAllImageRequestOperations;
@end
View
60 AFNetworking/AFImageCache.m
@@ -0,0 +1,60 @@
+// AFImageCache.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 "AFImageCache.h"
+
+static inline NSString * AFImageCacheKey(NSURLRequest *urlRequest, CGSize imageSize, AFImageRequestOptions options) {
+ return [[[urlRequest URL] absoluteString] stringByAppendingFormat:@"#%fx%f:%d", imageSize.width, imageSize.height, options];
+}
+
+@implementation AFImageCache
+
++ (id)sharedImageCache {
+ static NSCache *_sharedImageCache = nil;
+
+ if (!_sharedImageCache) {
+ _sharedImageCache = [[self alloc] init];
+ }
+
+ return _sharedImageCache;
+}
+
+- (UIImage *)cachedImageForRequest:(NSURLRequest *)urlRequest
+ imageSize:(CGSize)imageSize
+ options:(AFImageRequestOptions)options
+{
+ return [self objectForKey:AFImageCacheKey(urlRequest, imageSize, options)];
+}
+
+- (void)cacheImage:(UIImage *)image
+ forRequest:(NSURLRequest *)urlRequest
+ imageSize:(CGSize)imageSize
+ options:(AFImageRequestOptions)options
+{
+ if (!image) {
+ return;
+ }
+
+ [self setObject:image forKey:AFImageCacheKey(urlRequest, imageSize, options)];
+}
+
+@end
View
51 AFNetworking/AFImageRequestOperation.h
@@ -22,52 +22,21 @@
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
-#import "QHTTPOperation.h"
-#import "AFCallback.h"
+#import "AFHTTPRequestOperation.h"
typedef enum {
- AFImageRequestResize = 1 << 1,
- AFImageRequestRoundCorners = 1 << 2,
- AFImageCacheProcessedImage = 1 << 0xA,
- AFImageRequestDefaultOptions = AFImageRequestResize,
+ AFImageRequestDefaultOptions = 0,
+ AFImageRequestRoundCorners = 1 << 1,
} AFImageRequestOptions;
-@class AFImageRequestOperationCallback;
+@interface AFImageRequestOperation : AFHTTPRequestOperation
-@interface AFImageRequestOperation : QHTTPOperation {
-@private
- AFImageRequestOperationCallback *_callback;
-}
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ success:(void (^)(UIImage *image))success;
-@property (nonatomic, retain) AFImageRequestOperationCallback *callback;
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ imageSize:(CGSize)imageSize
+ options:(AFImageRequestOptions)options
+ success:(void (^)(UIImage *image))success;
-+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback;
-- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback;
-
-@end
-
-#pragma mark - AFHTTPOperationCallback
-
-typedef void (^AFImageRequestOperationSuccessBlock)(UIImage *image);
-typedef void (^AFImageRequestOperationErrorBlock)(NSError *error);
-
-@protocol AFImageRequestOperationCallback <NSObject>
-@optional
-+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success;
-+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success error:(AFImageRequestOperationErrorBlock)error;
-@end
-
-@interface AFImageRequestOperationCallback : AFCallback <AFImageRequestOperationCallback> {
-@private
- CGSize _imageSize;
- AFImageRequestOptions _options;
-}
-
-@property (readwrite, nonatomic, assign) CGSize imageSize;
-@property (readwrite, nonatomic, assign) AFImageRequestOptions options;
-
-@property (readwrite, nonatomic, copy) AFImageRequestOperationSuccessBlock successBlock;
-@property (readwrite, nonatomic, copy) AFImageRequestOperationErrorBlock errorBlock;
-
-+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success imageSize:(CGSize)imageSize options:(AFImageRequestOptions)options;
@end
View
149 AFNetworking/AFImageRequestOperation.m
@@ -21,129 +21,66 @@
// THE SOFTWARE.
#import "AFImageRequestOperation.h"
+#import "AFImageCache.h"
#import "UIImage+AFNetworking.h"
-const CGFloat kAFImageRequestJPEGQuality = 0.8;
-const NSUInteger kAFImageRequestMaximumResponseSize = 8 * 1024 * 1024;
+static CGFloat const kAFImageRequestJPEGQuality = 0.8;
+static NSUInteger const kAFImageRequestMaximumResponseSize = 8 * 1024 * 1024;
+
static inline CGSize kAFImageRequestRoundedCornerRadii(CGSize imageSize) {
CGFloat dimension = fmaxf(imageSize.width, imageSize.height) * 0.1;
return CGSizeMake(dimension, dimension);
}
@implementation AFImageRequestOperation
-@synthesize callback = _callback;
-
-+ (id)operationWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback {
- return [[[self alloc] initWithRequest:urlRequest callback:callback] autorelease];
-}
-
-- (id)initWithRequest:(NSURLRequest *)urlRequest callback:(AFImageRequestOperationCallback *)callback {
- self = [super initWithRequest:urlRequest];
- if (!self) {
- return nil;
- }
-
- self.maximumResponseSize = kAFImageRequestMaximumResponseSize;
-
- NSMutableIndexSet *statusCodes = [NSMutableIndexSet indexSetWithIndex:0];
- [statusCodes addIndexesInRange:NSMakeRange(200, 100)];
- self.acceptableStatusCodes = statusCodes;
- self.acceptableContentTypes = [NSSet setWithObjects:@"image/png", @"image/jpeg", @"image/pjpeg", @"image/gif", @"application/x-0", nil];
- self.callback = callback;
-
- if (self.callback) {
- self.runLoopModes = [NSSet setWithObjects:NSRunLoopCommonModes, NSDefaultRunLoopMode, nil];
- }
-
- return self;
-}
-
-#pragma mark - QHTTPRequestOperation
-// QHTTPRequestOperation requires this to return an NSHTTPURLResponse, but in certain circumstances,
-// this method would otherwise return an instance of its superclass, NSURLResponse
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response {
- if([response isKindOfClass:[NSHTTPURLResponse class]]) {
- [super connection:connection didReceiveResponse:response];
- } else {
- [super connection:connection didReceiveResponse:[[[NSHTTPURLResponse alloc] initWithURL:[response URL] MIMEType:[response MIMEType] expectedContentLength:[response expectedContentLength] textEncodingName:[response textEncodingName]] autorelease]];
- }
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ success:(void (^)(UIImage *image))success
+{
+ return [self operationWithRequest:urlRequest imageSize:CGSizeZero options:AFImageRequestDefaultOptions success:success];
}
-#pragma mark - QRunLoopOperation
-
-- (void)finishWithError:(NSError *)error {
- [super finishWithError:error];
-
- if (error) {
- if (self.callback.errorBlock) {
- self.callback.errorBlock(error);
- }
-
- return;
- }
-
- UIImage *image = nil;
- if ([[UIScreen mainScreen] scale] == 2.0) {
- CGImageRef imageRef = [UIImage imageWithData:self.responseBody].CGImage;
- image = [UIImage imageWithCGImage:imageRef scale:2.0 orientation:UIImageOrientationUp];
- } else {
- image = [UIImage imageWithData:self.responseBody];
- }
-
- BOOL didProcessingOnImage = NO;
-
- if ((self.callback.options & AFImageRequestResize) && !(CGSizeEqualToSize(image.size, self.callback.imageSize) || CGSizeEqualToSize(self.callback.imageSize, CGSizeZero))) {
- image = [UIImage imageByScalingAndCroppingImage:image size:self.callback.imageSize];
- didProcessingOnImage = YES;
- }
- if ((self.callback.options & AFImageRequestRoundCorners)) {
- image = [UIImage imageByRoundingCornersOfImage:image corners:UIRectCornerAllCorners cornerRadii:kAFImageRequestRoundedCornerRadii(image.size)];
- didProcessingOnImage = YES;
- }
-
-
- if (self.callback.successBlock) {
- self.callback.successBlock(image);
- }
-
- if ((self.callback.options & AFImageCacheProcessedImage) && didProcessingOnImage) {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- NSData *processedImageData = nil;
- if ((self.callback.options & AFImageRequestRoundCorners) || [[[[self.lastRequest URL] path] pathExtension] isEqualToString:@"png"]) {
- processedImageData = UIImagePNGRepresentation(image);
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ imageSize:(CGSize)imageSize
+ options:(AFImageRequestOptions)options
+ success:(void (^)(UIImage *image))success
+{
+ return [self operationWithRequest:urlRequest completion:^(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error) {
+ UIImage *image = nil;
+ if ([[UIScreen mainScreen] scale] == 2.0) {
+ CGImageRef imageRef = [[UIImage imageWithData:data] CGImage];
+ image = [UIImage imageWithCGImage:imageRef scale:2.0 orientation:UIImageOrientationUp];
} else {
- processedImageData = UIImageJPEGRepresentation(image, kAFImageRequestJPEGQuality);
+ image = [UIImage imageWithData:data];
}
- NSURLResponse *response = [[[NSURLResponse alloc] initWithURL:[self.lastRequest URL] MIMEType:[self.lastResponse MIMEType] expectedContentLength:[processedImageData length] textEncodingName:[self.lastResponse textEncodingName]] autorelease];
- NSCachedURLResponse *cachedResponse = [[[NSCachedURLResponse alloc] initWithResponse:response data:processedImageData] autorelease];
- [[NSURLCache sharedURLCache] storeCachedResponse:cachedResponse forRequest:self.lastRequest];
- [pool drain];
- }
-}
-
-- (void) dealloc {
- [_callback release];
-
- [super dealloc];
+
+ if (!(CGSizeEqualToSize(image.size, imageSize) || CGSizeEqualToSize(imageSize, CGSizeZero))) {
+ image = [UIImage imageByScalingAndCroppingImage:image size:imageSize];
+ }
+ if ((options & AFImageRequestRoundCorners)) {
+ image = [UIImage imageByRoundingCornersOfImage:image corners:UIRectCornerAllCorners cornerRadii:kAFImageRequestRoundedCornerRadii(image.size)];
+ }
+
+ if (success) {
+ success(image);
+ }
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
+ [[AFImageCache sharedImageCache] cacheImage:image forRequest:request imageSize:imageSize options:options];
+ });
+ }];
}
-@end
-
-#pragma mark - AFHTTPOperationCallback
-
-@implementation AFImageRequestOperationCallback : AFCallback
-@synthesize options = _options;
-@synthesize imageSize = _imageSize;
-@dynamic successBlock, errorBlock;
-
-+ (id)callbackWithSuccess:(AFImageRequestOperationSuccessBlock)success imageSize:(CGSize)imageSize options:(AFImageRequestOptions)options {
- id callback = [self callbackWithSuccess:success];
- [callback setImageSize:imageSize];
- [callback setOptions:options];
+- (id)initWithRequest:(NSURLRequest *)urlRequest {
+ self = [super initWithRequest:urlRequest];
+ if (!self) {
+ return nil;
+ }
+
+ self.runLoopModes = [NSSet setWithObject:NSRunLoopCommonModes];
- return callback;
+ return self;
}
@end
View
49 AFNetworking/AFCallback.m → AFNetworking/AFJSONRequestOperation.h
@@ -1,4 +1,4 @@
-// AFCallback.m
+// AFJSONRequestOperation.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
@@ -20,41 +20,24 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
-#import "AFCallback.h"
+#import "AFHTTPRequestOperation.h"
-@interface AFCallback ()
-@property (readwrite, nonatomic, copy) id successBlock;
-@property (readwrite, nonatomic, copy) id errorBlock;
-@end
-
-@implementation AFCallback
-@synthesize successBlock = _successBlock;
-@synthesize errorBlock = _errorBlock;
+@interface AFJSONRequestOperation : AFHTTPRequestOperation
-+ (id)callbackWithSuccess:(id)success {
- return [self callbackWithSuccess:success error:nil];
-}
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ success:(void (^)(id JSON))success;
-+ (id)callbackWithSuccess:(id)success error:(id)error {
- id callback = [[[self alloc] init] autorelease];
- [callback setSuccessBlock:success];
- [callback setErrorBlock:error];
-
- return callback;
-}
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ success:(void (^)(id JSON))success
+ failure:(void (^)(NSError *error))failure;
-- (id)init {
- if ([self class] == [AFCallback class]) {
- [NSException raise:NSInternalInconsistencyException format:@"You must override %@ in a subclass", NSStringFromSelector(_cmd)];
- }
-
- return [super init];
-}
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ acceptableStatusCodes:(NSIndexSet *)acceptableStatusCodes
+ acceptableContentTypes:(NSSet *)acceptableContentTypes
+ success:(void (^)(id JSON))success
+ failure:(void (^)(NSError *error))failure;
-- (void)dealloc {
- [_successBlock release];
- [_errorBlock release];
- [super dealloc];
-}
++ (NSIndexSet *)defaultAcceptableStatusCodes;
++ (NSSet *)defaultAcceptableContentTypes;
-@end
+@end
View
93 AFNetworking/AFJSONRequestOperation.m
@@ -0,0 +1,93 @@
+// AFJSONRequestOperation.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 "AFJSONRequestOperation.h"
+#import "JSONKit.h"
+
+@implementation AFJSONRequestOperation
+
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ success:(void (^)(id JSON))success
+{
+ return [self operationWithRequest:urlRequest success:success failure:nil];
+}
+
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ success:(void (^)(id JSON))success
+ failure:(void (^)(NSError *error))failure
+{
+ return [self operationWithRequest:urlRequest acceptableStatusCodes:[self defaultAcceptableStatusCodes] acceptableContentTypes:[self defaultAcceptableContentTypes] success:success failure:failure];
+}
+
++ (id)operationWithRequest:(NSURLRequest *)urlRequest
+ acceptableStatusCodes:(NSIndexSet *)acceptableStatusCodes
+ acceptableContentTypes:(NSSet *)acceptableContentTypes
+ success:(void (^)(id JSON))success
+ failure:(void (^)(NSError *error))failure
+{
+ return [self operationWithRequest:urlRequest completion:^(NSURLRequest *request, NSHTTPURLResponse *response, NSData *data, NSError *error) {
+ BOOL statusCodeAcceptable = [acceptableStatusCodes containsIndex:[response statusCode]];
+ BOOL contentTypeAcceptable = [acceptableContentTypes containsObject:[response MIMEType]];
+ if (!statusCodeAcceptable || !contentTypeAcceptable) {
+ NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
+ [userInfo setValue:[NSHTTPURLResponse localizedStringForStatusCode:[response statusCode]] forKey:NSLocalizedDescriptionKey];
+ [userInfo setValue:[request URL] forKey:NSURLErrorFailingURLErrorKey];
+
+ error = [[[NSError alloc] initWithDomain:NSURLErrorDomain code:[response statusCode] userInfo:userInfo] autorelease];
+ }
+
+ if (error) {
+ if (failure) {
+ failure(error);
+ }
+ } else {
+ id JSON = nil;
+
+ Class NSJSONSerialization = NSClassFromString(@"NSJSONSerialization");
+ if (NSJSONSerialization) {
+ JSON = [NSJSONSerialization JSONObjectWithData:data options:0 error:&error];
+ } else {
+ JSON = [[JSONDecoder decoder] objectWithData:data error:&error];
+ }
+
+ if (error) {
+ if (failure) {
+ failure(error);
+ }
+ } else {
+ if (success) {
+ success(JSON);
+ }
+ }
+ }
+ }];
+}
+
++ (NSIndexSet *)defaultAcceptableStatusCodes {
+ return [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
+}
+
++ (NSSet *)defaultAcceptableContentTypes {
+ return [NSSet setWithObjects:@"application/json", @"application/x-javascript", @"text/javascript", @"text/x-javascript", @"text/x-json", @"text/json", @"text/plain", nil];
+}
+
+@end
View
19 AFNetworking/AFCallback.h → ...rking/AFNetworkActivityIndicatorManager.h
@@ -1,4 +1,4 @@
-// AFCallback.h
+// AFNetworkActivityIndicatorManager.h
//
// Copyright (c) 2011 Gowalla (http://gowalla.com/)
//
@@ -22,15 +22,14 @@
#import <Foundation/Foundation.h>
-@protocol AFCallback <NSObject>
-+ (id)callbackWithSuccess:(id)success;
-+ (id)callbackWithSuccess:(id)success error:(id)error;
-@end
-
-@interface AFCallback : NSObject <AFCallback> {
+@interface AFNetworkActivityIndicatorManager : NSObject {
@private
- id _successBlock;
- id _errorBlock;
+ NSUInteger _activityCount;
}
-@end
++ (AFNetworkActivityIndicatorManager *)sharedManager;
+
+- (void)startAnimating;
+- (void)stopAnimating;
+
+@end
View
57 AFNetworking/AFNetworkActivityIndicatorManager.m
@@ -0,0 +1,57 @@
+// AFNetworkActivityIndicatorManager.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 "AFNetworkActivityIndicatorManager.h"
+
+@interface AFNetworkActivityIndicatorManager ()
+@property (readwrite, nonatomic, assign) NSUInteger activityCount;
+@end
+
+@implementation AFNetworkActivityIndicatorManager
+@synthesize activityCount = _activityCount;
+
++ (AFNetworkActivityIndicatorManager *)sharedManager {
+ static AFNetworkActivityIndicatorManager *_sharedManager = nil;
+ if (!_sharedManager) {
+ _sharedManager = [[AFNetworkActivityIndicatorManager alloc] init];
+ }
+
+ return _sharedManager;
+}
+
+- (void)setActivityCount:(NSUInteger)activityCount {
+ [self willChangeValueForKey:@"activityCount"];
+ _activityCount = MAX(activityCount, 0);
+ [self didChangeValueForKey:@"activityCount"];
+
+ [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:self.activityCount > 0];
+}
+
+- (void)startAnimating {
+ self.activityCount += 1;
+}
+
+- (void)stopAnimating {
+ self.activityCount -= 1;
+}
+
+@end
View
30 AFNetworking/AFRestClient.h
@@ -21,27 +21,37 @@
// THE SOFTWARE.
#import <Foundation/Foundation.h>
-#import "AFHTTPOperation.h"
+#import "AFHTTPRequestOperation.h"
@protocol AFRestClient <NSObject>
-@required
+ (NSURL *)baseURL;
-- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;
@end
-@interface AFRestClient : NSObject <AFRestClient>
+@interface AFRestClient : NSObject <AFRestClient> {
+@protected
+ NSMutableDictionary *_defaultHeaders;
+ NSOperationQueue *_operationQueue;
+}
+
- (NSString *)defaultValueForHeader:(NSString *)header;
- (void)setDefaultHeader:(NSString *)header value:(NSString *)value;
- (void)setAuthorizationHeaderWithToken:(NSString *)token;
- (void)clearAuthorizationHeader;
-- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
-- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
-- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
-- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback;
+- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters;
+- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(id response))success failure:(void (^)(NSError *error))failure;
+
+- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success;
+- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure;
+
+- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success;
+- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure;
+
+- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success;
+- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure;
-- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request callback:(AFHTTPOperationCallback *)callback;
-- (void)enqueueHTTPOperation:(AFHTTPOperation *)operation;
+- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success;
+- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure;
@end
#pragma mark - NSString + AFRestClient
View
117 AFNetworking/AFRestClient.m
@@ -21,13 +21,15 @@
// THE SOFTWARE.
#import "AFRestClient.h"
-#import "AFHTTPOperation.h"
+#import "AFJSONRequestOperation.h"
static NSStringEncoding const kAFRestClientStringEncoding = NSUTF8StringEncoding;
@interface AFRestClient ()
@property (readwrite, nonatomic, retain) NSMutableDictionary *defaultHeaders;
@property (readwrite, nonatomic, retain) NSOperationQueue *operationQueue;
+
+- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(id response))success failure:(void (^)(NSError *error))failure;
@end
@implementation AFRestClient
@@ -78,36 +80,6 @@ + (NSURL *)baseURL {
return nil;
}
-- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
- NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
- NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:_defaultHeaders];
- NSURL *url = nil;
-
- NSMutableArray *mutableParameterComponents = [NSMutableArray array];
- for (id key in [parameters allKeys]) {
- NSString *component = [NSString stringWithFormat:@"%@=%@", [key urlEncodedStringWithEncoding:kAFRestClientStringEncoding], [[parameters valueForKey:key] urlEncodedStringWithEncoding:kAFRestClientStringEncoding]];
- [mutableParameterComponents addObject:component];
- }
- NSString *queryString = [mutableParameterComponents componentsJoinedByString:@"&"];
-
- if ([method isEqualToString:@"GET"]) {
- path = [path stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", queryString];
- url = [NSURL URLWithString:path relativeToURL:[[self class] baseURL]];
- } else {
- url = [NSURL URLWithString:path relativeToURL:[[self class] baseURL]];
- NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(kAFRestClientStringEncoding));
- [headers setObject:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forKey:@"Content-Type"];
- [request setHTTPBody:[queryString dataUsingEncoding:NSUTF8StringEncoding]];
- }
-
- [request setURL:url];
- [request setHTTPMethod:method];
- [request setHTTPShouldHandleCookies:NO];
- [request setAllHTTPHeaderFields:headers];
-
- return request;
-}
-
- (NSString *)defaultValueForHeader:(NSString *)header {
return [self.defaultHeaders valueForKey:header];
}
@@ -124,42 +96,81 @@ - (void)clearAuthorizationHeader {
[self.defaultHeaders removeObjectForKey:@"Authorization"];
}
-
#pragma mark -
-- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
+- (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters {
+ NSMutableURLRequest *request = [[[NSMutableURLRequest alloc] init] autorelease];
+ NSMutableDictionary *headers = [NSMutableDictionary dictionaryWithDictionary:self.defaultHeaders];
+ NSURL *url = [NSURL URLWithString:path relativeToURL:[[self class] baseURL]];
+
+ if (parameters) {
+ NSMutableArray *mutableParameterComponents = [NSMutableArray array];
+ for (id key in [parameters allKeys]) {
+ NSString *component = [NSString stringWithFormat:@"%@=%@", [key urlEncodedStringWithEncoding:kAFRestClientStringEncoding], [[parameters valueForKey:key] urlEncodedStringWithEncoding:kAFRestClientStringEncoding]];
+ [mutableParameterComponents addObject:component];
+ }
+ NSString *queryString = [mutableParameterComponents componentsJoinedByString:@"&"];
+
+ if ([method isEqualToString:@"GET"]) {
+ url = [NSURL URLWithString:[[url absoluteString] stringByAppendingFormat:[path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@", queryString]];
+ } else {
+ NSString *charset = (NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(kAFRestClientStringEncoding));
+ [headers setObject:[NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset] forKey:@"Content-Type"];
+ [request setHTTPBody:[queryString dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+ }
+
+ [request setURL:url];
+ [request setHTTPMethod:method];
+ [request setHTTPShouldHandleCookies:NO];
+ [request setAllHTTPHeaderFields:headers];
+
+ return request;
+}
+
+- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request success:(void (^)(id response))success failure:(void (^)(NSError *error))failure {
+ if ([request URL] == nil || [[request URL] isEqual:[NSNull null]]) {
+ return;
+ }
+
+ AFHTTPRequestOperation *operation = [AFJSONRequestOperation operationWithRequest:request success:success failure:failure];
+ [self.operationQueue addOperation:operation];
+}
+
+- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success {
+ [self getPath:path parameters:parameters success:success failure:nil];
+}
+
+- (void)getPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure {
NSURLRequest *request = [self requestWithMethod:@"GET" path:path parameters:parameters];
- [self enqueueHTTPOperationWithRequest:request callback:callback];
+ [self enqueueHTTPOperationWithRequest:request success:success failure:failure];
}
-- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
- NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters];
- [self enqueueHTTPOperationWithRequest:request callback:callback];
+- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success {
+ [self postPath:path parameters:parameters success:success failure:nil];
}
-- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
- NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters];
- [self enqueueHTTPOperationWithRequest:request callback:callback];
+- (void)postPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure {
+ NSURLRequest *request = [self requestWithMethod:@"POST" path:path parameters:parameters];
+ [self enqueueHTTPOperationWithRequest:request success:success failure:failure];
}
-- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters callback:(AFHTTPOperationCallback *)callback {
- NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters];
- [self enqueueHTTPOperationWithRequest:request callback:callback];
+- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success {
+ [self putPath:path parameters:parameters success:success failure:nil];
}
-#pragma mark -
+- (void)putPath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure {
+ NSURLRequest *request = [self requestWithMethod:@"PUT" path:path parameters:parameters];
+ [self enqueueHTTPOperationWithRequest:request success:success failure:failure];
+}
-- (void)enqueueHTTPOperationWithRequest:(NSURLRequest *)request callback:(AFHTTPOperationCallback *)callback {
- if ([request URL] == nil || [[request URL] isEqual:[NSNull null]]) {
- return;
- }
-
- AFHTTPOperation *operation = [[[AFHTTPOperation alloc] initWithRequest:request callback:callback] autorelease];
- [self enqueueHTTPOperation:operation];
+- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success {
+ [self deletePath:path parameters:parameters success:success failure:nil];
}
-- (void)enqueueHTTPOperation:(AFHTTPOperation *)operation {
- [self.operationQueue addOperation:operation];
+- (void)deletePath:(NSString *)path parameters:(NSDictionary *)parameters success:(void (^)(id response))success failure:(void (^)(NSError *error))failure {
+ NSURLRequest *request = [self requestWithMethod:@"DELETE" path:path parameters:parameters];
+ [self enqueueHTTPOperationWithRequest:request success:success failure:failure];
}
@end
View
245 AFNetworking/QHTTPOperation/QHTTPOperation.h
@@ -1,245 +0,0 @@
-/*
- File: QHTTPOperation.h
-
- Contains: An NSOperation that runs an HTTP request.
-
- Written by: DTS
-
- Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
- ("Apple") in consideration of your agreement to the following
- terms, and your use, installation, modification or
- redistribution of this Apple software constitutes acceptance of
- these terms. If you do not agree with these terms, please do
- not use, install, modify or redistribute this Apple software.
-
- In consideration of your agreement to abide by the following
- terms, and subject to these terms, Apple grants you a personal,
- non-exclusive license, under Apple's copyrights in this
- original Apple software (the "Apple Software"), to use,
- reproduce, modify and redistribute the Apple Software, with or
- without modifications, in source and/or binary forms; provided
- that if you redistribute the Apple Software in its entirety and
- without modifications, you must retain this notice and the
- following text and disclaimers in all such redistributions of
- the Apple Software. Neither the name, trademarks, service marks
- or logos of Apple Inc. may be used to endorse or promote
- products derived from the Apple Software without specific prior
- written permission from Apple. Except as expressly stated in
- this notice, no other rights or licenses, express or implied,
- are granted by Apple herein, including but not limited to any
- patent rights that may be infringed by your derivative works or
- by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.
- APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
- THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
- COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
- INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
- OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
- OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
- OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-*/
-
-#import "QRunLoopOperation.h"
-
-/*
- QHTTPOperation is a general purpose NSOperation that runs an HTTP request.
- You initialise it with an HTTP request and then, when you run the operation,
- it sends the request and gathers the response. It is quite a complex
- object because it handles a wide variety of edge cases, but it's very
- easy to use in simple cases:
-
- 1. create the operation with the URL you want to get
-
- op = [[[QHTTPOperation alloc] initWithURL:url] autorelease];
-
- 2. set up any non-default parameters, for example, set which HTTP
- content types are acceptable
-
- op.acceptableContentTypes = [NSSet setWithObject:@"text/html"];
-
- 3. enqueue the operation
-
- [queue addOperation:op];
-
- 4. finally, when the operation is done, use the lastResponse and
- error properties to find out how things went
-
- As mentioned above, QHTTPOperation is very general purpose. There are a
- large number of configuration and result options available to you.
-
- o You can specify a NSURLRequest rather than just a URL.
-
- o You can configure the run loop and modes on which the NSURLConnection is
- scheduled.
-
- o You can specify what HTTP status codes and content types are OK.
-
- o You can set an authentication delegate to handle authentication challenges.
-
- o You can accumulate responses in memory or in an NSOutputStream.
-
- o For in-memory responses, you can specify a default response size
- (used to size the response buffer) and a maximum response size
- (to prevent unbounded memory use).
-
- o You can get at the last request and the last response, to track
- redirects.
-
- o There are a variety of funky debugging options to simulator errors
- and delays.
-
- Finally, it's perfectly reasonable to subclass QHTTPOperation to meet you
- own specific needs. Specifically, it's common for the subclass to
- override -connection:didReceiveResponse: in order to setup the output
- stream based on the specific details of the response.
-*/
-
-@protocol QHTTPOperationAuthenticationDelegate;
-
-@interface QHTTPOperation : QRunLoopOperation /* <NSURLConnectionDelegate> */
-{
- NSURLRequest * _request;
- NSIndexSet * _acceptableStatusCodes;
- NSSet * _acceptableContentTypes;
- id<QHTTPOperationAuthenticationDelegate> _authenticationDelegate;
- NSOutputStream * _responseOutputStream;
- NSUInteger _defaultResponseSize;
- NSUInteger _maximumResponseSize;
- NSURLConnection * _connection;
- BOOL _firstData;
- NSMutableData * _dataAccumulator;
- NSURLRequest * _lastRequest;
- NSHTTPURLResponse * _lastResponse;
- NSData * _responseBody;
-#if ! defined(NDEBUG)
- NSError * _debugError;
- NSTimeInterval _debugDelay;
- NSTimer * _debugDelayTimer;
-#endif
-}
-
-- (id)initWithRequest:(NSURLRequest *)request; // designated
-- (id)initWithURL:(NSURL *)url; // convenience, calls +[NSURLRequest requestWithURL:]
-
-// Things that are configured by the init method and can't be changed.
-
-@property (copy, readonly) NSURLRequest * request;
-@property (copy, readonly) NSURL * URL;
-
-// Things you can configure before queuing the operation.
-
-// runLoopThread and runLoopModes inherited from QRunLoopOperation
-@property (copy, readwrite) NSIndexSet * acceptableStatusCodes; // default is nil, implying 200..299
-@property (copy, readwrite) NSSet * acceptableContentTypes; // default is nil, implying anything is acceptable
-@property (assign, readwrite) id<QHTTPOperationAuthenticationDelegate> authenticationDelegate;
-
-#if ! defined(NDEBUG)
-@property (copy, readwrite) NSError * debugError; // default is nil
-@property (assign, readwrite) NSTimeInterval debugDelay; // default is none
-#endif
-
-// Things you can configure up to the point where you start receiving data.
-// Typically you would change these in -connection:didReceiveResponse:, but
-// it is possible to change them up to the point where -connection:didReceiveData:
-// is called for the first time (that is, you could override -connection:didReceiveData:
-// and change these before calling super).
-
-// IMPORTANT: If you set a response stream, QHTTPOperation calls the response
-// stream synchronously. This is fine for file and memory streams, but it would
-// not work well for other types of streams (like a bound pair).
-
-@property (retain, readwrite) NSOutputStream * responseOutputStream; // defaults to nil, which puts response into responseBody
-@property (assign, readwrite) NSUInteger defaultResponseSize; // default is 1 MB, ignored if responseOutputStream is set
-@property (assign, readwrite) NSUInteger maximumResponseSize; // default is 4 MB, ignored if responseOutputStream is set
- // defaults are 1/4 of the above on embedded
-
-// Things that are only meaningful after a response has been received;
-
-@property (assign, readonly, getter=isStatusCodeAcceptable) BOOL statusCodeAcceptable;
-@property (assign, readonly, getter=isContentTypeAcceptable) BOOL contentTypeAcceptable;
-
-// Things that are only meaningful after the operation is finished.
-
-// error property inherited from QRunLoopOperation
-@property (copy, readonly) NSURLRequest * lastRequest;
-@property (copy, readonly) NSHTTPURLResponse * lastResponse;
-
-@property (copy, readonly) NSData * responseBody;
-
-@end
-
-@interface QHTTPOperation (NSURLConnectionDelegate)
-
-// QHTTPOperation implements all of these methods, so if you override them
-// you must consider whether or not to call super.
-//
-// These will be called on the operation's run loop thread.
-
-- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
- // Routes the request to the authentication delegate if it exists, otherwise
- // just returns NO.
-
-- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
- // Routes the request to the authentication delegate if it exists, otherwise
- // just cancels the challenge.
-
-- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response;
- // Latches the request and response in lastRequest and lastResponse.
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response;
- // Latches the response in lastResponse.
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data;
- // If this is the first chunk of data, it decides whether the data is going to be
- // routed to memory (responseBody) or a stream (responseOutputStream) and makes the
- // appropriate preparations. For this and subsequent data it then actually shuffles
- // the data to its destination.
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection;
- // Completes the operation with either no error (if the response status code is acceptable)
- // or an error (otherwise).
-
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error;
- // Completes the operation with the error.
-
-@end
-
-@protocol QHTTPOperationAuthenticationDelegate <NSObject>
-@required
-
-// These are called on the operation's run loop thread and have the same semantics as their
-// NSURLConnection equivalents. It's important to realise that there is no
-// didCancelAuthenticationChallenge callback (because NSURLConnection doesn't issue one to us).
-// Rather, an authentication delegate is expected to observe the operation and cancel itself
-// if the operation completes while the challenge is running.
-
-- (BOOL)httpOperation:(QHTTPOperation *)operation canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace;
-- (void)httpOperation:(QHTTPOperation *)operation didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge;
-
-@end
-
-extern NSString * kQHTTPOperationErrorDomain;
-
-// positive error codes are HTML status codes (when they are not allowed via acceptableStatusCodes)
-//
-// 0 is, of course, not a valid error code
-//
-// negative error codes are errors from the module
-
-enum {
- kQHTTPOperationErrorResponseTooLarge = -1,
- kQHTTPOperationErrorOnOutputStream = -2,
- kQHTTPOperationErrorBadContentType = -3
-};
View
653 AFNetworking/QHTTPOperation/QHTTPOperation.m
@@ -1,653 +0,0 @@
-/*
- File: QHTTPOperation.m
-
- Contains: An NSOperation that runs an HTTP request.
-
- Written by: DTS
-
- Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
- ("Apple") in consideration of your agreement to the following
- terms, and your use, installation, modification or
- redistribution of this Apple software constitutes acceptance of
- these terms. If you do not agree with these terms, please do
- not use, install, modify or redistribute this Apple software.
-
- In consideration of your agreement to abide by the following
- terms, and subject to these terms, Apple grants you a personal,
- non-exclusive license, under Apple's copyrights in this
- original Apple software (the "Apple Software"), to use,
- reproduce, modify and redistribute the Apple Software, with or
- without modifications, in source and/or binary forms; provided
- that if you redistribute the Apple Software in its entirety and
- without modifications, you must retain this notice and the
- following text and disclaimers in all such redistributions of
- the Apple Software. Neither the name, trademarks, service marks
- or logos of Apple Inc. may be used to endorse or promote
- products derived from the Apple Software without specific prior
- written permission from Apple. Except as expressly stated in
- this notice, no other rights or licenses, express or implied,
- are granted by Apple herein, including but not limited to any
- patent rights that may be infringed by your derivative works or
- by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.
- APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
- THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
- COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
- INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
- OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
- OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
- OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-*/
-
-#import "QHTTPOperation.h"
-
-@interface QHTTPOperation ()
-
-// Read/write versions of public properties
-
-@property (copy, readwrite) NSURLRequest * lastRequest;
-@property (copy, readwrite) NSHTTPURLResponse * lastResponse;
-
-// Internal properties
-
-@property (retain, readwrite) NSURLConnection * connection;
-@property (assign, readwrite) BOOL firstData;
-@property (retain, readwrite) NSMutableData * dataAccumulator;
-
-#if ! defined(NDEBUG)
-@property (retain, readwrite) NSTimer * debugDelayTimer;
-#endif
-
-@end
-
-@implementation QHTTPOperation
-
-#pragma mark * Initialise and finalise
-
-- (id)initWithRequest:(NSURLRequest *)request
- // See comment in header.
-{
- // any thread
- assert(request != nil);
- assert([request URL] != nil);
- // Because we require an NSHTTPURLResponse, we only support HTTP and HTTPS URLs.
- assert([[[[request URL] scheme] lowercaseString] isEqual:@"http"] || [[[[request URL] scheme] lowercaseString] isEqual:@"https"]);
- self = [super init];
- if (self != nil) {
- #if TARGET_OS_EMBEDDED || TARGET_IPHONE_SIMULATOR
- static const NSUInteger kPlatformReductionFactor = 4;
- #else
- static const NSUInteger kPlatformReductionFactor = 1;
- #endif
- self->_request = [request copy];
- self->_defaultResponseSize = 1 * 1024 * 1024 / kPlatformReductionFactor;
- self->_maximumResponseSize = 4 * 1024 * 1024 / kPlatformReductionFactor;
- self->_firstData = YES;
- }
- return self;
-}
-
-- (id)initWithURL:(NSURL *)url
- // See comment in header.
-{
- assert(url != nil);
- return [self initWithRequest:[NSURLRequest requestWithURL:url]];
-}
-
-- (void)dealloc
-{
- #if ! defined(NDEBUG)
- [self->_debugError release];
- [self->_debugDelayTimer invalidate];
- [self->_debugDelayTimer release];
- #endif
- // any thread
- [self->_request release];
- [self->_acceptableStatusCodes release];
- [self->_acceptableContentTypes release];
- [self->_responseOutputStream release];
- assert(self->_connection == nil); // should have been shut down by now
- [self->_dataAccumulator release];
- [self->_lastRequest release];
- [self->_lastResponse release];
- [self->_responseBody release];
- [super dealloc];
-}
-
-#pragma mark * Properties
-
-// We write our own settings for many properties because we want to bounce
-// sets that occur in the wrong state. And, given that we've written the
-// setter anyway, we also avoid KVO notifications when the value doesn't change.
-
-@synthesize request = _request;
-
-@synthesize authenticationDelegate = _authenticationDelegate;
-
-+ (BOOL)automaticallyNotifiesObserversOfAuthenticationDelegate
-{
- return NO;
-}
-
-- (id<QHTTPOperationAuthenticationDelegate>)authenticationDelegate
-{
- return self->_authenticationDelegate;
-}
-
-- (void)setAuthenticationDelegate:(id<QHTTPOperationAuthenticationDelegate>)newValue
-{
- if (self.state != kQRunLoopOperationStateInited) {
- assert(NO);
- } else {
- if (newValue != self->_authenticationDelegate) {
- [self willChangeValueForKey:@"authenticationDelegate"];
- self->_authenticationDelegate = newValue;
- [self didChangeValueForKey:@"authenticationDelegate"];
- }
- }
-}
-
-@synthesize acceptableStatusCodes = _acceptableStatusCodes;
-
-+ (BOOL)automaticallyNotifiesObserversOfAcceptableStatusCodes
-{
- return NO;
-}
-
-- (NSIndexSet *)acceptableStatusCodes
-{
- return [[self->_acceptableStatusCodes retain] autorelease];
-}
-
-- (void)setAcceptableStatusCodes:(NSIndexSet *)newValue
-{
- if (self.state != kQRunLoopOperationStateInited) {
- assert(NO);
- } else {
- if (newValue != self->_acceptableStatusCodes) {
- [self willChangeValueForKey:@"acceptableStatusCodes"];
- [self->_acceptableStatusCodes autorelease];
- self->_acceptableStatusCodes = [newValue copy];
- [self didChangeValueForKey:@"acceptableStatusCodes"];
- }
- }
-}
-
-@synthesize acceptableContentTypes = _acceptableContentTypes;
-
-+ (BOOL)automaticallyNotifiesObserversOfAcceptableContentTypes
-{
- return NO;
-}
-
-- (NSSet *)acceptableContentTypes
-{
- return [[self->_acceptableContentTypes retain] autorelease];
-}
-
-- (void)setAcceptableContentTypes:(NSSet *)newValue
-{
- if (self.state != kQRunLoopOperationStateInited) {
- assert(NO);
- } else {
- if (newValue != self->_acceptableContentTypes) {
- [self willChangeValueForKey:@"acceptableContentTypes"];
- [self->_acceptableContentTypes autorelease];
- self->_acceptableContentTypes = [newValue copy];
- [self didChangeValueForKey:@"acceptableContentTypes"];
- }
- }
-}
-
-@synthesize responseOutputStream = _responseOutputStream;
-
-+ (BOOL)automaticallyNotifiesObserversOfResponseOutputStream
-{
- return NO;
-}
-
-- (NSOutputStream *)responseOutputStream
-{
- return [[self->_responseOutputStream retain] autorelease];
-}
-
-- (void)setResponseOutputStream:(NSOutputStream *)newValue
-{
- if (self.dataAccumulator != nil) {
- assert(NO);
- } else {
- if (newValue != self->_responseOutputStream) {
- [self willChangeValueForKey:@"responseOutputStream"];
- [self->_responseOutputStream autorelease];
- self->_responseOutputStream = [newValue retain];
- [self didChangeValueForKey:@"responseOutputStream"];
- }
- }
-}
-
-@synthesize defaultResponseSize = _defaultResponseSize;
-
-+ (BOOL)automaticallyNotifiesObserversOfDefaultResponseSize
-{
- return NO;
-}
-
-- (NSUInteger)defaultResponseSize
-{
- return self->_defaultResponseSize;
-}
-
-- (void)setDefaultResponseSize:(NSUInteger)newValue
-{
- if (self.dataAccumulator != nil) {
- assert(NO);
- } else {
- if (newValue != self->_defaultResponseSize) {
- [self willChangeValueForKey:@"defaultResponseSize"];
- self->_defaultResponseSize = newValue;
- [self didChangeValueForKey:@"defaultResponseSize"];
- }
- }
-}
-
-@synthesize maximumResponseSize = _maximumResponseSize;
-
-+ (BOOL)automaticallyNotifiesObserversOfMaximumResponseSize
-{
- return NO;
-}
-
-- (NSUInteger)maximumResponseSize
-{
- return self->_maximumResponseSize;
-}
-
-- (void)setMaximumResponseSize:(NSUInteger)newValue
-{
- if (self.dataAccumulator != nil) {
- assert(NO);
- } else {
- if (newValue != self->_maximumResponseSize) {
- [self willChangeValueForKey:@"maximumResponseSize"];
- self->_maximumResponseSize = newValue;
- [self didChangeValueForKey:@"maximumResponseSize"];
- }
- }
-}
-
-@synthesize lastRequest = _lastRequest;
-@synthesize lastResponse = _lastResponse;
-@synthesize responseBody = _responseBody;
-
-@synthesize connection = _connection;
-@synthesize firstData = _firstData;
-@synthesize dataAccumulator = _dataAccumulator;
-
-- (NSURL *)URL
-{
- return [self.request URL];
-}
-
-- (BOOL)isStatusCodeAcceptable
-{
- NSIndexSet * acceptableStatusCodes;
- NSInteger statusCode;
-
- assert(self.lastResponse != nil);
-
- acceptableStatusCodes = self.acceptableStatusCodes;
- if (acceptableStatusCodes == nil) {
- acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
- }
- assert(acceptableStatusCodes != nil);
-
- statusCode = [self.lastResponse statusCode];
- return (statusCode >= 0) && [acceptableStatusCodes containsIndex: (NSUInteger) statusCode];
-}
-
-- (BOOL)isContentTypeAcceptable
-{
- NSString * contentType;
-
- assert(self.lastResponse != nil);
- contentType = [self.lastResponse MIMEType];
- return (self.acceptableContentTypes == nil) || ((contentType != nil) && [self.acceptableContentTypes containsObject:contentType]);
-}
-
-#pragma mark * Start and finish overrides
-
-- (void)operationDidStart
- // Called by QRunLoopOperation when the operation starts. This kicks of an
- // asynchronous NSURLConnection.
-{
- assert(self.isActualRunLoopThread);
- assert(self.state == kQRunLoopOperationStateExecuting);
-
- assert(self.defaultResponseSize > 0);
- assert(self.maximumResponseSize > 0);
- assert(self.defaultResponseSize <= self.maximumResponseSize);
-
- assert(self.request != nil);
-
- // If a debug error is set, apply that error rather than running the connection.
-
- #if ! defined(NDEBUG)
- if (self.debugError != nil) {
- [self finishWithError:self.debugError];
- return;
- }
- #endif
-
- // Create a connection that's scheduled in the required run loop modes.
-
- assert(self.connection == nil);
- self.connection = [[[NSURLConnection alloc] initWithRequest:self.request delegate:self startImmediately:NO] autorelease];
- assert(self.connection != nil);
-
- for (NSString * mode in self.actualRunLoopModes) {
- [self.connection scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:mode];
- }
-
- [self.connection start];
-}
-
-- (void)operationWillFinish
- // Called by QRunLoopOperation when the operation has finished. We
- // do various bits of tidying up.
-{
- assert(self.isActualRunLoopThread);
- assert(self.state == kQRunLoopOperationStateExecuting);
-
- // It is possible to hit this state of the operation is cancelled while
- // the debugDelayTimer is running. In that case, hey, we'll just accept
- // the inevitable and finish rather than trying anything else clever.
-
- #if ! defined(NDEBUG)
- if (self.debugDelayTimer != nil) {
- [self.debugDelayTimer invalidate];
- self.debugDelayTimer = nil;
- }
- #endif
-
- [self.connection cancel];
- self.connection = nil;
-
- // If we have an output stream, close it at this point. We might never
- // have actually opened this stream but, AFAICT, closing an unopened stream
- // doesn't hurt.
-
- if (self.responseOutputStream != nil) {
- [self.responseOutputStream close];
- }
-}
-
-- (void)finishWithError:(NSError *)error
- // We override -finishWithError: just so we can handle our debug delay.
-{
- // If a debug delay was set, don't finish now but rather start the debug delay timer
- // and have it do the actual finish. We clear self.debugDelay so that the next
- // time this code runs its doesn't do this again.
- //
- // We only do this in the non-cancellation case. In the cancellation case, we
- // just stop immediately.
-
- #if ! defined(NDEBUG)
- if (self.debugDelay > 0.0) {
- if ( (error != nil) && [[error domain] isEqual:NSCocoaErrorDomain] && ([error code] == NSUserCancelledError) ) {
- self.debugDelay = 0.0;
- } else {
- assert(self.debugDelayTimer == nil);
- self.debugDelayTimer = [NSTimer timerWithTimeInterval:self.debugDelay target:self selector:@selector(debugDelayTimerDone:) userInfo:error repeats:NO];
- assert(self.debugDelayTimer != nil);
- for (NSString * mode in self.actualRunLoopModes) {
- [[NSRunLoop currentRunLoop] addTimer:self.debugDelayTimer forMode:mode];
- }
- self.debugDelay = 0.0;
- return;
- }
- }
- #endif
-
- [super finishWithError:error];
-}
-
-#if ! defined(NDEBUG)
-
-@synthesize debugError = _debugError;
-@synthesize debugDelay = _debugDelay;
-@synthesize debugDelayTimer = _debugDelayTimer;
-
-- (void)debugDelayTimerDone:(NSTimer *)timer
-{
- NSError * error;
-
- assert(timer == self.debugDelayTimer);
-
- error = [[[timer userInfo] retain] autorelease];
- assert( (error == nil) || [error isKindOfClass:[NSError class]] );
-
- [self.debugDelayTimer invalidate];
- self.debugDelayTimer = nil;
-
- [self finishWithError:error];
-}
-
-#endif
-
-#pragma mark * NSURLConnection delegate callbacks
-
-- (BOOL)connection:(NSURLConnection *)connection canAuthenticateAgainstProtectionSpace:(NSURLProtectionSpace *)protectionSpace
- // See comment in header.
-{
- BOOL result;
-
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
- assert(protectionSpace != nil);
- #pragma unused(protectionSpace)
-
- result = NO;
- if (self.authenticationDelegate != nil) {
- result = [self.authenticationDelegate httpOperation:self canAuthenticateAgainstProtectionSpace:protectionSpace];
- }
- return result;
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveAuthenticationChallenge:(NSURLAuthenticationChallenge *)challenge
- // See comment in header.
-{
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
- assert(challenge != nil);
- #pragma unused(challenge)
-
- if (self.authenticationDelegate != nil) {
- [self.authenticationDelegate httpOperation:self didReceiveAuthenticationChallenge:challenge];
- } else {
- if ( [challenge previousFailureCount] == 0 ) {
- [[challenge sender] continueWithoutCredentialForAuthenticationChallenge:challenge];
- } else {
- [[challenge sender] cancelAuthenticationChallenge:challenge];
- }
- }
-}
-
-- (NSURLRequest *)connection:(NSURLConnection *)connection willSendRequest:(NSURLRequest *)request redirectResponse:(NSURLResponse *)response
- // See comment in header.
-{
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
- assert( (response == nil) || [response isKindOfClass:[NSHTTPURLResponse class]] );
-
- self.lastRequest = request;
- self.lastResponse = (NSHTTPURLResponse *) response;
- return request;
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response
- // See comment in header.
-{
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
- assert([response isKindOfClass:[NSHTTPURLResponse class]]);
-
- self.lastResponse = (NSHTTPURLResponse *) response;
-
- // We don't check the status code here because we want to give the client an opportunity
- // to get the data of the error message. Perhaps we /should/ check the content type
- // here, but I'm not sure whether that's the right thing to do.
-}
-
-- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data
- // See comment in header.
-{
- BOOL success;
-
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
- assert(data != nil);
-
- // If we don't yet have a destination for the data, calculate one. Note that, even
- // if there is an output stream, we don't use it for error responses.
-
- success = YES;
- if (self.firstData) {
- assert(self.dataAccumulator == nil);
-
- if ( (self.responseOutputStream == nil) || ! self.isStatusCodeAcceptable ) {
- long long length;
-
- assert(self.dataAccumulator == nil);
-
- length = [self.lastResponse expectedContentLength];
- if (length == NSURLResponseUnknownLength) {
- length = self.defaultResponseSize;
- }
- if (length <= (long long) self.maximumResponseSize) {
- self.dataAccumulator = [NSMutableData dataWithCapacity:(NSUInteger)length];
- } else {
- [self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorResponseTooLarge userInfo:nil]];
- success = NO;
- }
- }
-
- // If the data is going to an output stream, open it.
-
- if (success) {
- if (self.dataAccumulator == nil) {
- assert(self.responseOutputStream != nil);
- [self.responseOutputStream open];
- }
- }
-
- self.firstData = NO;
- }
-
- // Write the data to its destination.
-
- if (success) {
- if (self.dataAccumulator != nil) {
- if ( ([self.dataAccumulator length] + [data length]) <= self.maximumResponseSize ) {
- [self.dataAccumulator appendData:data];
- } else {
- [self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorResponseTooLarge userInfo:nil]];
- }
- } else {
- NSUInteger dataOffset;
- NSUInteger dataLength;
- const uint8_t * dataPtr;
- NSError * error;
- NSInteger bytesWritten;
-
- assert(self.responseOutputStream != nil);
-
- dataOffset = 0;
- dataLength = [data length];
- dataPtr = [data bytes];
- error = nil;
- do {
- if (dataOffset == dataLength) {
- break;
- }
- bytesWritten = [self.responseOutputStream write:&dataPtr[dataOffset] maxLength:dataLength - dataOffset];
- if (bytesWritten <= 0) {
- error = [self.responseOutputStream streamError];
- if (error == nil) {
- error = [NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorOnOutputStream userInfo:nil];
- }
- break;
- } else {
- dataOffset += bytesWritten;
- }
- } while (YES);
-
- if (error != nil) {
- [self finishWithError:error];
- }
- }
- }
-}
-
-- (void)connectionDidFinishLoading:(NSURLConnection *)connection
- // See comment in header.
-{
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
-
- assert(self.lastResponse != nil);
-
- // Swap the data accumulator over to the response data so that we don't trigger a copy.
-
- assert(self->_responseBody == nil);
- self->_responseBody = self->_dataAccumulator;
- self->_dataAccumulator = nil;
-
- // Because we fill out _dataAccumulator lazily, an empty body will leave _dataAccumulator
- // set to nil. That's not what our clients expect, so we fix it here.
-
- if (self->_responseBody == nil) {
- self->_responseBody = [[NSData alloc] init];
- assert(self->_responseBody != nil);
- }
-
- if ( ! self.isStatusCodeAcceptable ) {
- [self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:self.lastResponse.statusCode userInfo:nil]];
- } else if ( ! self.isContentTypeAcceptable ) {
- [self finishWithError:[NSError errorWithDomain:kQHTTPOperationErrorDomain code:kQHTTPOperationErrorBadContentType userInfo:nil]];
- } else {
- [self finishWithError:nil];
- }
-}
-
-- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
- // See comment in header.
-{
- assert(self.isActualRunLoopThread);
- assert(connection == self.connection);
- #pragma unused(connection)
- assert(error != nil);
-
- [self finishWithError:error];
-}
-
-@end
-
-NSString * kQHTTPOperationErrorDomain = @"kQHTTPOperationErrorDomain";
View
119 AFNetworking/QHTTPOperation/QRunLoopOperation.h
@@ -1,119 +0,0 @@
-/*
- File: QRunLoopOperation.h
-
- Contains: An abstract subclass of NSOperation for async run loop based operations.
-
- Written by: DTS
-
- Copyright: Copyright (c) 2010 Apple Inc. All Rights Reserved.
-
- Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
- ("Apple") in consideration of your agreement to the following
- terms, and your use, installation, modification or
- redistribution of this Apple software constitutes acceptance of
- these terms. If you do not agree with these terms, please do
- not use, install, modify or redistribute this Apple software.
-
- In consideration of your agreement to abide by the following
- terms, and subject to these terms, Apple grants you a personal,
- non-exclusive license, under Apple's copyrights in this
- original Apple software (the "Apple Software"), to use,
- reproduce, modify and redistribute the Apple Software, with or
- without modifications, in source and/or binary forms; provided
- that if you redistribute the Apple Software in its entirety and
- without modifications, you must retain this notice and the
- following text and disclaimers in all such redistributions of
- the Apple Software. Neither the name, trademarks, service marks
- or logos of Apple Inc. may be used to endorse or promote
- products derived from the Apple Software without specific prior
- written permission from Apple. Except as expressly stated in
- this notice, no other rights or licenses, express or implied,
- are granted by Apple herein, including but not limited to any
- patent rights that may be infringed by your derivative works or
- by other works in which the Apple Software may be incorporated.
-
- The Apple Software is provided by Apple on an "AS IS" basis.
- APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
- WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
- THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
- COMBINATION WITH YOUR PRODUCTS.
-
- IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
- INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
- TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
- OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
- OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
- OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
- OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
-*/
-
-#import <Foundation/Foundation.h>
-
-enum QRunLoopOperationState {
- kQRunLoopOperationStateInited,
- kQRunLoopOperationStateExecuting,
- kQRunLoopOperationStateFinished
-};
-typedef enum QRunLoopOperationState QRunLoopOperationState;
-
-@interface QRunLoopOperation : NSOperation
-{
- QRunLoopOperationState _state;
- NSThread * _runLoopThread;
- NSSet * _runLoopModes;
- NSError * _error;
-}
-
-// Things you can configure before queuing the operation.
-
-// IMPORTANT: Do not change these after queuing the operation; it's very likely that
-// bad things will happen if you do.
-
-@property (retain, readwrite) NSThread * runLoopThread; // default is nil, implying main thread
-@property (copy, readwrite) NSSet * runLoopModes; // default is nil, implying set containing NSDefaultRunLoopMode
-
-// Things that are only meaningful after the operation is finished.
-
-@property (copy, readonly ) NSError * error;