Permalink
Browse files

Initial commit.

  • Loading branch information...
0 parents commit 713768b2496861cd462a4014b21cde3f3403a178 @scelis scelis committed Mar 5, 2012
@@ -0,0 +1,36 @@
+//
+// MBBaseRequest.h
+// MBRequest
+//
+// Created by Sebastian Celis on 2/29/12.
+// Copyright (c) 2012 Mobiata, LLC. All rights reserved.
+//
+
+#import "MBURLConnectionOperation.h"
+
+typedef void (^MBRequestDownloadProgressCallback)(NSInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected);
+typedef void (^MBRequestUploadProgressCallback)(NSInteger bytes, NSInteger totalBytes, NSInteger totalBytesExpected);
+
+@interface MBBaseRequest : NSObject <MBURLConnectionOperationDelegate>
+
+// Returns true if the request is currently active. This will be true as soon as the URL connection
+// operation has been added to the queue and will remain true until the request is cancelled or
+// completes.
+@property (atomic, assign, readonly, getter=isRunning) BOOL running;
+
+// Cancels the request. Once cancelled, you should not receive any callbacks (success or
+// error) that relates to this request.
+- (void)cancel;
+- (BOOL)isCancelled;
+
+// The operation associated with the URL connection.
+@property (atomic, retain, readonly) MBURLConnectionOperation *connectionOperation;
+
+// An error associated with this request.
+@property (atomic, retain, readonly) NSError *error;
+
+// Callbacks for upload and download progress.
+@property (atomic, copy) MBRequestDownloadProgressCallback downloadProgressCallback;
+@property (atomic, copy) MBRequestUploadProgressCallback uploadProgressCallback;
+
+@end
@@ -0,0 +1,163 @@
+//
+// MBBaseRequest.m
+// MBRequest
+//
+// Created by Sebastian Celis on 2/29/12.
+// Copyright (c) 2012 Mobiata, LLC. All rights reserved.
+//
+
+#import "MBBaseRequest.h"
+#import "MBBaseRequestSubclass.h"
+
+@interface MBBaseRequest ()
+@property (atomic, retain, readwrite) NSError *error;
+@property (atomic, assign, readwrite, getter=isRunning) BOOL running;
+@property (atomic, retain, readwrite) MBURLConnectionOperation *connectionOperation;
+@end
+
+
+// This set of active requests serves the sole purpose of ensuring that the request
+// objects are not dealloced out from under the request. connectionOperationDidFinish
+// runs on a background thread, and without lots of @synchronized calls it would
+// be possible for the main thread to cancel and dealloc the request while the background
+// thread was executing this method. This NSMutableSet ensures that the request sticks
+// around long enough that we don't run into any memory issues.
+static NSMutableSet *_activeRequests;
+
+void _MBAddRequest(MBBaseRequest *request)
+{
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ _activeRequests = [[NSMutableSet alloc] init];
+ });
+
+ @synchronized (_activeRequests)
+ {
+ [_activeRequests addObject:request];
+ }
+}
+
+void _MBRemoveRequest(MBBaseRequest *request)
+{
+ @synchronized (_activeRequests)
+ {
+ [_activeRequests removeObject:request];
+ }
+}
+
+@implementation MBBaseRequest
+
+@synthesize connectionOperation = _connectionOperation;
+@synthesize downloadProgressCallback = _downloadProgressCallback;
+@synthesize error = _error;
+@synthesize running = _running;
+@synthesize uploadProgressCallback = _uploadProgressCallback;
+
+#pragma mark - Object Lifecycle
+
+- (void)dealloc
+{
+ [_connectionOperation cancel];
+ [_connectionOperation release];
+ [_downloadProgressCallback release];
+ [_error release];
+ [_uploadProgressCallback release];
+ [super dealloc];
+}
+
+#pragma mark - Public Methods
+
+- (void)cancel
+{
+ @synchronized (self)
+ {
+ [[self connectionOperation] cancel];
+ [self setRunning:NO];
+ }
+
+ _MBRemoveRequest(self);
+}
+
+- (BOOL)isCancelled
+{
+ return [[self connectionOperation] isCancelled];
+}
+
+#pragma mark - Private Methods
+
+- (NSOperationQueue *)sharedRequestQueue
+{
+ static NSOperationQueue *sharedQueue;
+ static dispatch_once_t onceToken;
+ dispatch_once(&onceToken, ^{
+ sharedQueue = [[NSOperationQueue alloc] init];
+ [sharedQueue setMaxConcurrentOperationCount:5];
+ [sharedQueue setName:@"Shared MBRequest Queue"];
+ });
+
+ return sharedQueue;
+}
+
+- (void)scheduleOperation
+{
+ [self scheduleOperationOnQueue:[self sharedRequestQueue]];
+}
+
+- (void)scheduleOperationOnQueue:(NSOperationQueue *)queue
+{
+ _MBAddRequest(self);
+ [queue addOperation:[self connectionOperation]];
+ [self setRunning:YES];
+}
+
+- (void)connectionOperationDidFinish
+{
+}
+
+#pragma mark - MBURLConnectionOperationDelegate
+
+- (void)connectionOperationDidFinish:(MBURLConnectionOperation *)operation
+{
+ @synchronized (self)
+ {
+ [self setError:[operation error]];
+ [self connectionOperationDidFinish];
+ [self setRunning:NO];
+ }
+
+ _MBRemoveRequest(self);
+}
+
+- (void)connectionOperation:(MBURLConnectionOperation *)operation
+ didReceiveBodyData:(NSInteger)bytesRead
+ totalBytesRead:(NSInteger)totalBytesReceived
+ totalBytesExpectedToRead:(NSInteger)totalBytesExpectedToRead
+{
+ if (![self isCancelled] && [self downloadProgressCallback])
+ {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (![self isCancelled])
+ {
+ [self downloadProgressCallback](bytesRead, totalBytesReceived, totalBytesExpectedToRead);
+ }
+ });
+ }
+}
+
+- (void)connectionOperation:(MBURLConnectionOperation *)operation
+ didSendBodyData:(NSInteger)bytesWritten
+ totalBytesWritten:(NSInteger)totalBytesWritten
+ totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
+{
+ if (![self isCancelled] && [self uploadProgressCallback])
+ {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (![self isCancelled])
+ {
+ [self uploadProgressCallback](bytesWritten, totalBytesWritten, totalBytesExpectedToWrite);
+ }
+ });
+ }
+}
+
+@end
@@ -0,0 +1,34 @@
+//
+// MBBaseRequestSubclass.h
+// MBRequest
+//
+// Created by Sebastian Celis on 3/1/12.
+// Copyright (c) 2012 Mobiata, LLC. All rights reserved.
+//
+
+// The extensions in this header are to be used only by subclasses of MBBaseRequest.
+
+#import <Foundation/Foundation.h>
+
+@interface MBBaseRequest (ForSubclassEyesOnly)
+
+// The shared request queue for all MBBaseRequest objects. You may override this method to return
+// a different queue if you would like to separate some requests from other requests. For example,
+// you might want a special queue to handle certain requests which are fast and return very little
+// data. This queue could have 20 max concurrent operations while another, slower queue might only
+// 2 or 3.
+- (NSOperationQueue *)sharedRequestQueue;
+
+// Schedules the operation on the sharedRequestQueue returned by the current class.
+- (void)scheduleOperation;
+
+// Schedules the operation on a particular NSOperationQueue.
+- (void)scheduleOperationOnQueue:(NSOperationQueue *)queue;
+
+// Override this method in your subclass to properly handle the response. It should parse
+// the response into understandable objects for the caller and then notify the caller in
+// some way, whether that be through delegate callbacks or blocks. To make things simpler for
+// the callers, consider executing any callback code on the main thread.
+- (void)connectionOperationDidFinish;
+
+@end
@@ -0,0 +1,16 @@
+//
+// MBHTTPConnectionOperation.h
+// MBRequest
+//
+// Created by Sebastian Celis on 2/28/12.
+// Copyright (c) 2012 Mobiata, LLC. All rights reserved.
+//
+
+#import "MBURLConnectionOperation.h"
+
+@interface MBHTTPConnectionOperation : MBURLConnectionOperation
+
+@property (atomic, retain) NSIndexSet *successfulStatusCodes;
+@property (atomic, retain, readonly) NSHTTPURLResponse *response;
+
+@end
@@ -0,0 +1,77 @@
+//
+// MBHTTPConnectionOperation.m
+// MBRequest
+//
+// Created by Sebastian Celis on 2/28/12.
+// Copyright (c) 2012 Mobiata, LLC. All rights reserved.
+//
+
+#import "MBHTTPConnectionOperation.h"
+
+#import "MBRequestError.h"
+#import "MBRequestLocalization.h"
+#import "MBURLConnectionOperationSubclass.h"
+
+@interface MBHTTPConnectionOperation ()
+@property (atomic, retain, readwrite) NSError *error;
+@end
+
+
+@implementation MBHTTPConnectionOperation
+
+@dynamic error;
+@synthesize delegate = _delegate;
+@synthesize successfulStatusCodes = _successfulStatusCodes;
+
+#pragma mark - Object Lifecycle
+
+- (id)init
+{
+ if ((self = [super init]))
+ {
+ _successfulStatusCodes = [[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)] retain];
+ }
+
+ return self;
+}
+
+- (void)dealloc
+{
+ [_successfulStatusCodes release];
+ [super dealloc];
+}
+
+#pragma mark - Accessors
+
+- (NSHTTPURLResponse *)response
+{
+ return (NSHTTPURLResponse *)[super response];
+}
+
+#pragma mark - Subclassing
+
+- (void)handleResponse
+{
+ @synchronized (self)
+ {
+ [super handleResponse];
+
+ if ([self error] == nil)
+ {
+ NSHTTPURLResponse *response = [self response];
+ NSInteger statusCode = [response statusCode];
+ if (![[self successfulStatusCodes] containsIndex:statusCode])
+ {
+ NSString *format = MBRequestLocalizedString(@"request_unsuccessful_bad_status_code", @"Request failed (status code %d)");
+ NSString *msg = [NSString stringWithFormat:format, statusCode];
+ NSDictionary *userInfo = [NSDictionary dictionaryWithObject:msg forKey:NSLocalizedDescriptionKey];
+ NSError *error = [NSError errorWithDomain:MBRequestErrorDomain
+ code:MBRequestErrorCodeUnsuccessfulServerResponse
+ userInfo:userInfo];
+ [self setError:error];
+ }
+ }
+ }
+}
+
+@end
@@ -0,0 +1,15 @@
+//
+// MBJSONRequest.h
+// MBRequest
+//
+// Created by Sebastian Celis on 3/4/12.
+// Copyright (c) 2012 Mobiata, LLC. All rights reserved.
+//
+
+#import "MBBaseRequest.h"
+
+@interface MBJSONRequest : MBBaseRequest
+
+@property (atomic, retain, readonly) id responseJSON;
+
+@end
Oops, something went wrong.

0 comments on commit 713768b

Please sign in to comment.