add networking helpers to BITAuthenticator

* NSOperation-based networking
* helpers for URLRequest, operation, getPath:error:
This commit is contained in:
Stephan Diederich
2013-08-11 21:37:47 +02:00
parent ffc54256dd
commit 039902da02
6 changed files with 412 additions and 1 deletions

View File

@@ -10,6 +10,8 @@
#import "HockeySDK.h"
#import "HockeySDKPrivate.h"
#import "BITAuthenticator_Private.h"
#import "BITHTTPOperation.h"
static NSString* const kBITAuthenticatorAuthTokenKey = @"BITAuthenticatorAuthTokenKey";
static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthenticatorLastAuthenticatedVersionKey";
@@ -21,6 +23,7 @@ static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthe
- (void)dealloc {
[self unregisterObservers];
[self cancelOperationsWithPath:nil method:nil];
}
- (instancetype) initWithAppIdentifier:(NSString *)appIdentifier isAppStoreEnvironemt:(BOOL)isAppStoreEnvironment {
@@ -251,4 +254,73 @@ static NSString* const kBITAuthenticatorLastAuthenticatedVersionKey = @"BITAuthe
[self validateInstallationWithCompletion:nil];
}
}
#pragma mark - Networking
- (NSMutableURLRequest *) requestWithMethod:(NSString*) method
path:(NSString *) path {
NSParameterAssert(self.serverURL);
NSParameterAssert(method);
path = path ? : @"";
NSURL *endpoint = [[NSURL URLWithString:self.serverURL] URLByAppendingPathComponent:path];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:endpoint];
request.HTTPMethod = method;
return request;
}
- (BITHTTPOperation*) operationWithURLRequest:(NSURLRequest*) request
completion:(BITNetworkCompletionBlock) completion {
BITHTTPOperation *operation = [BITHTTPOperation operationWithRequest:request
];
[operation setCompletion:completion];
return operation;
}
- (void)getPath:(NSString *)path completion:(BITNetworkCompletionBlock)completion {
NSURLRequest *request = [self requestWithMethod:@"GET" path:path];
BITHTTPOperation *op = [self operationWithURLRequest:request
completion:completion];
[self enqeueHTTPOperation:op];
}
- (void) enqeueHTTPOperation:(BITHTTPOperation *) operation {
[self.operationQueue addOperation:operation];
}
- (NSUInteger) cancelOperationsWithPath:(NSString*) path
method:(NSString*) method {
NSUInteger cancelledOperations = 0;
for(BITHTTPOperation *operation in self.operationQueue.operations) {
NSURLRequest *request = operation.URLRequest;
BOOL matchedMethod = YES;
if(method && ![request.HTTPMethod isEqualToString:method]) {
matchedMethod = NO;
}
BOOL matchedPath = YES;
if(path) {
//method is not interesting here, we' just creating it to get the URL
NSURL *url = [self requestWithMethod:@"GET" path:path].URL;
matchedPath = [request.URL isEqual:url];
}
if(matchedPath && matchedMethod) {
++cancelledOperations;
[operation cancel];
}
}
return cancelledOperations;
}
- (NSOperationQueue *)operationQueue {
if(nil == _operationQueue) {
_operationQueue = [[NSOperationQueue alloc] init];
_operationQueue.maxConcurrentOperationCount = 1;
}
return _operationQueue;
}
@end

View File

@@ -9,6 +9,7 @@
#import "BITAuthenticator.h"
#import "BITHockeyBaseManagerPrivate.h"
#import "BITAuthenticationViewController.h"
#import "BITHTTPOperation.h" //needed for typedef
@interface BITAuthenticator ()<BITAuthenticationViewControllerDelegate>
@@ -46,4 +47,57 @@
- (void) validationSucceeded;
- (void) validationFailedWithError:(NSError *) validationError;
#pragma mark - Networking helpers (TODO: move to base-class / networking component)
@property (nonatomic, strong) NSOperationQueue *operationQueue;
/**
* creates an NRURLRequest for the given method and path by using
* the internally stored baseURL.
*
* @param method the HTTPMethod to check, must not be nil
* @param path path to append to baseURL. can be nil in which case "/" is appended
*
* @return an NSMutableURLRequest for further configuration
*/
- (NSMutableURLRequest *) requestWithMethod:(NSString*) method
path:(NSString *) path;
/**
* Creates an operation for the given NSURLRequest
*
* @param request the request that should be handled
* @param completion completionBlock that is called once the operation finished
*
* @return operation, which can be queued via enqueueHTTPOperation:
*/
- (BITHTTPOperation*) operationWithURLRequest:(NSURLRequest*) request
completion:(BITNetworkCompletionBlock) completion;
/**
* Creates an operation for the given path, and enqueues it
*
* @param path the request path to check
* @param completion completionBlock that is called once the operation finished
*
*/
- (void) getPath:(NSString*) path
completion:(BITNetworkCompletionBlock) completion;
/**
* adds the given operation to the internal queue
*
* @param operation operation to add
*/
- (void) enqeueHTTPOperation:(BITHTTPOperation *) operation;
/**
* cancels the specified operations
*
* @param path the path which operation should be cancelled. Can be nil to match all
* @param method the method which operations to cancel. Can be nil to match all
* @return number of operations cancelled
*/
- (NSUInteger) cancelOperationsWithPath:(NSString*) path
method:(NSString*) method;
@end

View File

@@ -0,0 +1,25 @@
//
// BITHTTPOperation.h
// HockeySDK
//
// Created by Stephan Diederich on 10.08.13.
//
//
#import <Foundation/Foundation.h>
@class BITHTTPOperation;
typedef void (^BITNetworkCompletionBlock)(BITHTTPOperation* operation, id response, NSError* error);
@interface BITHTTPOperation : NSOperation
+ (instancetype) operationWithRequest:(NSURLRequest *) urlRequest;
@property (nonatomic, readonly) NSURLRequest *URLRequest;
- (void) setCompletion:(BITNetworkCompletionBlock) completionBlock;
@property (nonatomic, readonly) NSData *data;
@property (nonatomic, readonly) NSError *error;
@end

118
Classes/BITHTTPOperation.m Normal file
View File

@@ -0,0 +1,118 @@
//
// BITHTTPOperation.m
// HockeySDK
//
// Created by Stephan Diederich on 10.08.13.
//
//
#import "BITHTTPOperation.h"
@interface BITHTTPOperation()<NSURLConnectionDelegate>
@end
@implementation BITHTTPOperation {
NSURLRequest *_URLRequest;
NSURLConnection *_connection;
NSMutableData *_data;
BOOL _isExecuting;
BOOL _isFinished;
}
+ (instancetype)operationWithRequest:(NSURLRequest *)urlRequest {
BITHTTPOperation *op = [[self class] new];
op->_URLRequest = urlRequest;
return op;
}
#pragma mark - NSOperation overrides
- (BOOL)isConcurrent {
return YES;
}
- (void)cancel {
[_connection cancel];
[super cancel];
}
- (void) start {
if (![[NSThread currentThread] isMainThread]) {
[self performSelector:@selector(start) onThread:NSThread.mainThread withObject:nil waitUntilDone:NO];
}
[self willChangeValueForKey:@"isExecuting"];
_isExecuting = YES;
[self didChangeValueForKey:@"isExecuting"];
_connection = [[NSURLConnection alloc] initWithRequest:_URLRequest
delegate:self
startImmediately:NO];
[_connection scheduleInRunLoop:[NSRunLoop currentRunLoop]
forMode:NSDefaultRunLoopMode];
[_connection start];
}
- (void) finish {
[self willChangeValueForKey:@"isExecuting"];
[self willChangeValueForKey:@"isFinished"];
_isExecuting = NO;
_isFinished = YES;
[self didChangeValueForKey:@"isExecuting"];
[self didChangeValueForKey:@"isFinished"];
}
#pragma mark - NSURLConnectionDelegate
-(void)connection:(NSURLConnection*)connection didReceiveResponse:(NSURLResponse*)response {
_data = [[NSMutableData alloc] init];
}
-(void)connection:(NSURLConnection*)connection didReceiveData:(NSData*)data {
[_data appendData:data];
}
-(void)connection:(NSURLConnection*)connection didFailWithError:(NSError*)error {
//FINISHED and failed
_error = error;
_data = nil;
[self finish];
}
-(void)connectionDidFinishLoading:(NSURLConnection*)connection {
[self finish];
}
#pragma mark - Public interface
- (NSData *)data {
return _data;
}
- (void)setCompletion:(BITNetworkCompletionBlock)completion {
__weak typeof(self) weakSelf = self;
if(nil == completion) {
[super setCompletionBlock:nil];
} else {
[super setCompletionBlock:^{
typeof(self) strongSelf = weakSelf;
if(strongSelf) {
dispatch_async(dispatch_get_main_queue(), ^{
completion(strongSelf, strongSelf->_data, strongSelf->_error);
});
}
}];
}
}
- (BOOL)isFinished {
return _isFinished;
}
- (BOOL)isExecuting {
return _isExecuting;
}
@end

View File

@@ -133,6 +133,8 @@
E48A3DEC17B3ED1C00924C3D /* BITAuthenticator.h in Headers */ = {isa = PBXBuildFile; fileRef = E48A3DEA17B3ED1C00924C3D /* BITAuthenticator.h */; settings = {ATTRIBUTES = (Public, ); }; };
E48A3DED17B3ED1C00924C3D /* BITAuthenticator.m in Sources */ = {isa = PBXBuildFile; fileRef = E48A3DEB17B3ED1C00924C3D /* BITAuthenticator.m */; };
E48A3DEF17B3EFF100924C3D /* BITAuthenticatorTests.m in Sources */ = {isa = PBXBuildFile; fileRef = E48A3DEE17B3EFF100924C3D /* BITAuthenticatorTests.m */; };
E4933E8017B66CDA00B11ACC /* BITHTTPOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = E4933E7E17B66CDA00B11ACC /* BITHTTPOperation.h */; };
E4933E8117B66CDA00B11ACC /* BITHTTPOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = E4933E7F17B66CDA00B11ACC /* BITHTTPOperation.m */; };
E4B4DB7D17B435550099C67F /* BITAuthenticationViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = E4B4DB7B17B435550099C67F /* BITAuthenticationViewController.h */; };
E4B4DB7E17B435550099C67F /* BITAuthenticationViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = E4B4DB7C17B435550099C67F /* BITAuthenticationViewController.m */; };
/* End PBXBuildFile section */
@@ -287,6 +289,8 @@
E48A3DEB17B3ED1C00924C3D /* BITAuthenticator.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITAuthenticator.m; sourceTree = "<group>"; };
E48A3DEE17B3EFF100924C3D /* BITAuthenticatorTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITAuthenticatorTests.m; sourceTree = "<group>"; };
E48A3DF117B408F400924C3D /* BITAuthenticator_Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = BITAuthenticator_Private.h; sourceTree = "<group>"; };
E4933E7E17B66CDA00B11ACC /* BITHTTPOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITHTTPOperation.h; sourceTree = "<group>"; };
E4933E7F17B66CDA00B11ACC /* BITHTTPOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITHTTPOperation.m; sourceTree = "<group>"; };
E4B4DB7B17B435550099C67F /* BITAuthenticationViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BITAuthenticationViewController.h; sourceTree = "<group>"; };
E4B4DB7C17B435550099C67F /* BITAuthenticationViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BITAuthenticationViewController.m; sourceTree = "<group>"; };
/* End PBXFileReference section */
@@ -573,6 +577,8 @@
E48A3DF117B408F400924C3D /* BITAuthenticator_Private.h */,
E4B4DB7B17B435550099C67F /* BITAuthenticationViewController.h */,
E4B4DB7C17B435550099C67F /* BITAuthenticationViewController.m */,
E4933E7E17B66CDA00B11ACC /* BITHTTPOperation.h */,
E4933E7F17B66CDA00B11ACC /* BITHTTPOperation.m */,
);
name = private;
sourceTree = "<group>";
@@ -608,6 +614,7 @@
1E49A47C1612226D00463151 /* BITUpdateManagerPrivate.h in Headers */,
1E49A4851612226D00463151 /* BITUpdateViewControllerPrivate.h in Headers */,
1E49A4B5161222B900463151 /* BITHockeyBaseManagerPrivate.h in Headers */,
E4933E8017B66CDA00B11ACC /* BITHTTPOperation.h in Headers */,
1E49A4BE161222B900463151 /* BITHockeyHelper.h in Headers */,
1E49A4C4161222B900463151 /* BITAppStoreHeader.h in Headers */,
1E49A4CA161222B900463151 /* BITStoreButton.h in Headers */,
@@ -825,6 +832,7 @@
1E49A4451612223B00463151 /* BITFeedbackListViewCell.m in Sources */,
1E49A44B1612223B00463151 /* BITFeedbackListViewController.m in Sources */,
1E49A4511612223B00463151 /* BITFeedbackManager.m in Sources */,
E4933E8117B66CDA00B11ACC /* BITHTTPOperation.m in Sources */,
1E49A45A1612223B00463151 /* BITFeedbackMessage.m in Sources */,
1E49A4601612223B00463151 /* BITFeedbackUserDataViewController.m in Sources */,
1E49A4701612226D00463151 /* BITAppVersionMetaInfo.m in Sources */,

View File

@@ -17,7 +17,7 @@
#import "HockeySDK.h"
#import "BITAuthenticator.h"
#import "BITAuthenticator_Private.h"
#import "BITHTTPOperation.h"
#import "BITTestHelper.h"
@interface MyDevice : NSObject
@@ -287,4 +287,138 @@
[verifyCount(delegateMock, never()) authenticator:_sut failedToValidateInstallationWithError:(id)anything()];
[verifyCount(delegateMock, times(1)) authenticatorDidValidateInstallation:_sut];
}
#pragma mark - Networking base tests
- (void) testThatURLRequestHasBaseURLSet {
_sut.serverURL = @"http://myserver.com";
NSMutableURLRequest *request = [_sut requestWithMethod:@"GET" path:nil];
assertThat(request.URL, equalTo([NSURL URLWithString:@"http://myserver.com/"]));
}
- (void) testThatURLRequestHasPathAppended {
_sut.serverURL = @"http://myserver.com";
NSMutableURLRequest *request = [_sut requestWithMethod:@"GET" path:@"projects"];
assertThat(request.URL, equalTo([NSURL URLWithString:@"http://myserver.com/projects"]));
}
- (void) testThatURLRequestHasMethodSet {
NSMutableURLRequest *request = [_sut requestWithMethod:@"POST" path:nil];
assertThat(request.HTTPMethod, equalTo(@"POST"));
}
- (void) testThatOperationHasURLRequestSet {
_sut.serverURL = @"http://myserver.com";
NSURLRequest *r = [_sut requestWithMethod:@"PUT" path:@"x"];
BITHTTPOperation *op = [_sut operationWithURLRequest:r
completion:nil];
assertThat(op.URLRequest, equalTo(r));
}
#pragma mark - Convenience methods
- (void) testThatGetPathCreatesAndEnquesAnOperation {
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(0));
[given([_sut operationWithURLRequest:(id)anything()
completion:(id)anything()]) willReturn:[NSOperation new]];
[_sut getPath:@"endpoint"
completion:nil];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(1));
}
#pragma mark - Completion Tests
- (void) testThatCompletionIsCalled {
//TODO
}
#pragma mark - HTTPOperation enqueuing / cancellation
- (void) testThatOperationIsQueued {
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(0));
[_sut.operationQueue setSuspended:YES];
BITHTTPOperation *op = [BITHTTPOperation new];
[_sut enqeueHTTPOperation:op];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(1));
}
- (void) testThatOperationCancellingMatchesAllOperationsWithNilMethod {
[_sut.operationQueue setSuspended:YES];
NSURLRequest *requestGet = [_sut requestWithMethod:@"GET" path:nil];
NSURLRequest *requestPut = [_sut requestWithMethod:@"PUT" path:nil];
NSURLRequest *requestPost = [_sut requestWithMethod:@"POST" path:nil];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestGet
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPut
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPost
completion:nil]];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(3));
NSUInteger numCancelled = [_sut cancelOperationsWithPath:nil method:nil];
assertThatUnsignedInt(numCancelled, equalToUnsignedInt(3));
}
- (void) testThatOperationCancellingMatchesAllOperationsWithNilPath {
[_sut.operationQueue setSuspended:YES];
NSURLRequest *requestGet = [_sut requestWithMethod:@"GET" path:@"test"];
NSURLRequest *requestPut = [_sut requestWithMethod:@"PUT" path:@"Another/acas"];
NSURLRequest *requestPost = [_sut requestWithMethod:@"POST" path:nil];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestGet
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPut
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPost
completion:nil]];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(3));
NSUInteger numCancelled = [_sut cancelOperationsWithPath:nil method:nil];
assertThatUnsignedInt(numCancelled, equalToUnsignedInt(3));
}
- (void) testThatOperationCancellingMatchesAllOperationsWithSetPath {
NSURLRequest *requestGet = [_sut requestWithMethod:@"GET" path:@"test"];
NSURLRequest *requestPut = [_sut requestWithMethod:@"PUT" path:@"Another/acas"];
NSURLRequest *requestPost = [_sut requestWithMethod:@"POST" path:nil];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestGet
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPut
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPost
completion:nil]];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(3));
[_sut cancelOperationsWithPath:@"Another/acas" method:nil];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(2));
}
- (void) testThatOperationCancellingMatchesAllOperationsWithSetMethod {
NSURLRequest *requestGet = [_sut requestWithMethod:@"GET" path:@"test"];
NSURLRequest *requestPut = [_sut requestWithMethod:@"PUT" path:@"Another/acas"];
NSURLRequest *requestPost = [_sut requestWithMethod:@"POST" path:nil];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestGet
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPut
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPost
completion:nil]];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(3));
NSUInteger numCancelled = [_sut cancelOperationsWithPath:nil method:@"POST"];
assertThatUnsignedInt(numCancelled, equalToUnsignedInt(1));
}
- (void) testThatOperationCancellingMatchesAllOperationsWithSetMethodAndPath {
NSURLRequest *requestGet = [_sut requestWithMethod:@"GET" path:@"test"];
NSURLRequest *requestPut = [_sut requestWithMethod:@"PUT" path:@"Another/acas"];
NSURLRequest *requestPost = [_sut requestWithMethod:@"POST" path:nil];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestGet
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPut
completion:nil]];
[_sut enqeueHTTPOperation:[_sut operationWithURLRequest:requestPost
completion:nil]];
assertThatUnsignedInt(_sut.operationQueue.operationCount, equalToUnsignedInt(3));
NSUInteger numCancelled = [_sut cancelOperationsWithPath:@"Another/acas" method:@"PUT"];
assertThatUnsignedInt(numCancelled, equalToUnsignedInt(1));
}
#pragma mark - Operation Testing
@end