Project import
diff --git a/Courier/CRSessionController.h b/Courier/CRSessionController.h new file mode 100644 index 0000000..c82ac11 --- /dev/null +++ b/Courier/CRSessionController.h
@@ -0,0 +1,204 @@ +// +// CRSessionController.h +// Courier +// +// Created by Andrew Smith on 9/22/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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 <Reachability/Reachability.h> + +/** + The CRSessionController wraps around an NSURLSession. Use this class to create and managed tasks. For + instance, you can group tasks by name and bulk cancel said group. + */ +NS_CLASS_AVAILABLE(10_9, 7_0) +@interface CRSessionController : NSObject + +/** + The internal NSURLSession. + */ +@property (nonatomic, readonly, strong) NSURLSession *session; + +/** + The NSOperationQueue on which the NSURLSessions delegate callbacks are run. + */ +@property (nonatomic, readonly, strong) NSOperationQueue *sessionDelegateOperationQueue; + +/** + A copy of the internal NSURLSessionConfiguration from the internal NSURLSession. + */ +@property (nonatomic, readonly) NSURLSessionConfiguration *configuration; + +/** + Create a session controller with the given NSURLSessionConfiguration. Note that once a configuration + is passed in, it is immutable. + */ +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER; + +/** + Create a session controller with the given NSURLSessionConfiguration. Note that once a configuration + is passed in, it is immutable. + */ ++ (instancetype)sessionControllerWithConfiguration:(NSURLSessionConfiguration *)configuration; + +/** + Create an NSURLSessionDataTask for the given request. The completion handler will run regardless of + whether or not the task succeeds or not. +*/ +- (NSURLSessionDataTask *)dataTaskForRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSData *data, + NSURLResponse *response, + BOOL cachedResponse, + NSError *error))completionHandler; +/** + Create an NSURLSessionDataTask for the given request and add it to the specified + non-nil task group. If task group is nil, task will be added to generic group. + */ +- (NSURLSessionDataTask *)dataTaskForRequest:(NSURLRequest *)request + taskGroup:(NSString *)group + completionHandler:(void (^)(NSData *data, + NSURLResponse *response, + BOOL cachedResponse, + NSError *error))completionHandler; + +/** + Adds an NSURLSessionTaskDelegate to the given task. Right now, just the HTTPRedirect call and + authentication challenge is passed through to the delegate, but future work will pass through + all callbacks. + + @param delegate The delegate that will respond to NSURLSessionTaskDelegate callbacks + @param task The task to use the delegate with. + */ +- (void)addNSURLSessionTaskDelegate:(id <NSURLSessionTaskDelegate>)delegate + forTask:(NSURLSessionTask *)task; + +/** + Removes the delegate. At this point, the delegate will no longer respond to callbacks. + + @param delegate The delegate to remove. + */ +- (void)removeNSURLSessionTaskDelegate:(id <NSURLSessionTaskDelegate>)delegate; + +/** + @returns The delegate specified in addNSURLSessionTaskDelegate:forTask: by the task parameter. + + @param task The NSURLSessionTask to retrieve the delegate for. + */ +- (id <NSURLSessionTaskDelegate>)NSURLSessionTaskDelegateForTask:(NSURLSessionTask *)task; + +@end + +@interface CRSessionController (TaskManagement) + +/** + * Returns the task, if any, associated with the task ID + * + * @param taskId The task ID, called from task.taskIdentifier. + * + * @return The task associated with the taskIdentifier + */ +- (NSURLSessionTask *)taskWithIdentifier:(NSUInteger)taskIdentifier; + +/** + * A quick way to find out if you have a task with the given identifier. + * + * @param taskIdentifier The task identifier, from task.taskIdentifier + * + * @return Returns YES if the controller has a task with the given taskIdentifier. + */ +- (BOOL)hasTaskWithIdentifier:(NSUInteger)taskIdentifier; + +/** + @returns YES if the task group has any task with the state. + @param group The name of the task group. + @param state The state of the NSURLSessionTask to test against. + */ +- (BOOL)hasTasksInGroup:(NSString *)group + withState:(NSURLSessionTaskState)state; + +/** + Calls -suspend on all tasks in a given group. + */ +- (void)suspendTasksInGroup:(NSString *)group; + +/** + Calls -resume on all tasks in a given group. + */ +- (void)resumeTasksInGroup:(NSString *)group; + +/** + Calls -cancel on all tasks in a given group. + */ +- (void)cancelTasksInGroup:(NSString *)group; + +/** + Calls -suspend on all tasks tracked by this controller + */ +- (void)suspendAllTasks; + +/** + Calls -resume on all tasks tracked by this controller + */ +- (void)resumeAllTasks; + +/** + Calls -cancel on all tasks tracked by this controller + */ +- (void)cancelAllTasks; + +@end + +@interface CRSessionController (Reachability) + +/** + Returns YES if any internet connection is reachable. + */ +- (BOOL)isInternetReachable; + +/** + Starts monitoring for reachability changes. If there is a change, the reachability status block will + be called. Alternatively, you can listen for the kReachabilityChangedNotification NSNotification. + */ +- (void)startReachabilityMonitoring; + +/** + Stops reachability monitoring. Changes in reachability will no longer call the reachability status + change block, or fire off the kReachabilityChangedNotification NSNotification. + */ +- (void)stopReachabilityMonitoring; + +/** + Sets a block that will run every time the network reachability status changes. + @param block The block to call when the network status changes. + */ +- (void)setReachabilityStatusChangeBlock:(void (^)(NetworkStatus status))block; + +@end + +@interface CRSessionController (Debug) + +/** + * Pretty prints all the current requests both queued and executing + */ +- (void)logRequests; + +@end
diff --git a/Courier/CRSessionController.m b/Courier/CRSessionController.m new file mode 100644 index 0000000..298f681 --- /dev/null +++ b/Courier/CRSessionController.m
@@ -0,0 +1,528 @@ +// +// CRSessionController.m +// Courier +// +// Created by Andrew Smith on 9/22/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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 "CRSessionController.h" + +#import <UIKit/UIKit.h> +#import <Reachability/Reachability.h> + +#import "CourierLog.h" +#import "NSURLResponse+Courier.h" +#import "CRSessionDelegate.h" + +#define kCRSessionControllerGenericTaskGroup @"kCRSessionControllerGenericTaskGroup" + +@interface CRSessionController () + +/** + * The keys are the group names, and the values are arrays of associated tasks + */ +@property (nonatomic, strong) NSMutableDictionary *groups; + +/** + * The keys are a unique string, the values are a dictionary with the task object + * and associated group. + */ +@property (nonatomic, strong) NSMutableDictionary *tasksByToken; + +/** + * The keys are the task IDs, and the values are the associated task object + */ +@property (nonatomic, strong) NSMutableDictionary *tasksByIdentifier; + +/** + + */ +@property (nonatomic, strong) NSMutableDictionary *sessionDelegates; + +/** + Internal reachability object + */ +@property (nonatomic, strong) Reachability *reachabilityObject; + +/** + This is a serial queue used to manage NSURLSessionTasks. This queue is used to ensure that you + can interact with the CRSessionController in a thread safe way. + */ +@property (nonatomic, strong) NSOperationQueue *serialQueue; + +@end + +@implementation CRSessionController + +- (void)dealloc +{ + [self.session invalidateAndCancel]; +} + +- (instancetype)init +{ + return [self initWithSessionConfiguration:nil]; +} + +- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration +{ + self = [super init]; + if (self != nil) { + // + // Use default session configuration if none provided + // + if (configuration == nil) { + configuration = [NSURLSessionConfiguration defaultSessionConfiguration]; + } + + // + // NSURLSession delegate queue + // + NSOperationQueue *delegateQueue = [NSOperationQueue new]; + delegateQueue.maxConcurrentOperationCount = 1; + _sessionDelegateOperationQueue = delegateQueue; + + // + // Build NSURLSession + // + CRSessionDelegate *sessionDelegate = + [CRSessionDelegate sessionDelegateWithSessionController:self]; + _session = [NSURLSession sessionWithConfiguration:configuration + delegate:sessionDelegate + delegateQueue:_sessionDelegateOperationQueue]; + + // + // TODO + // + NSOperationQueue *serialQueue = [NSOperationQueue new]; + serialQueue.maxConcurrentOperationCount = 1; + _serialQueue = serialQueue; + + // + // Keep track of tasks + // + _groups = [NSMutableDictionary dictionary]; + _tasksByToken = [NSMutableDictionary dictionary]; + _tasksByIdentifier = [NSMutableDictionary dictionary]; + _sessionDelegates = [NSMutableDictionary dictionary]; + + // + // Reachable + // + _reachabilityObject = [Reachability reachabilityForInternetConnection]; + } + return self; +} + ++ (instancetype)sessionControllerWithConfiguration:(NSURLSessionConfiguration *)configuration +{ + return [[self alloc] initWithSessionConfiguration:configuration]; +} + +#pragma mark - Task creation + +- (NSURLSessionDataTask *)dataTaskForRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSData *data, + NSURLResponse *response, + BOOL cachedResponse, + NSError *error))completionHandler +{ + NSURLSessionDataTask *task = [self dataTaskForRequest:request + taskGroup:nil + completionHandler:completionHandler]; + return task; +} + +- (NSURLSessionDataTask *)dataTaskForRequest:(NSURLRequest *)request + taskGroup:(NSString *)group + completionHandler:(void (^)(NSData *data, + NSURLResponse *response, + BOOL cachedResponse, + NSError *error))completionHandler +{ + CourierLogInfo(@"Creating task for URL : %@", request.URL); + + // + // Unique task token + // + NSString *token = [self uniqueToken]; + + // + // Check for cached response. Pass this into the completion and allow the task + // to pass through the normal delegate queue flow. + // + NSURLCache *urlCache = _session.configuration.URLCache; + BOOL cachedResponse = urlCache && [urlCache cachedResponseForRequest:request]; + + // + // Create Task + // + __weak typeof(self) weakSelf = self; + NSURLSessionDataTask *task = [_session dataTaskWithRequest:request + completionHandler:^(NSData *data, + NSURLResponse *response, + NSError *error) { + __strong typeof(self) strongSelf = weakSelf; + [strongSelf logResponse:response data:data error:error]; + [strongSelf removeTaskWithToken:token]; + if (completionHandler) completionHandler(data, + response, + cachedResponse, + error); + }]; + + // + // Keep track of the task + // + [self addTask:task + withToken:token + toGroup:group]; + + return task; +} + +- (void)logResponse:(NSURLResponse *)response + data:(NSData *)data + error:(NSError *)error +{ + CourierLogInfo(@"Finishing task for URL : %@ status code: %li", + response.URL, + (long)response.cou_statusCode); + +#if DEBUG && COURIER_LOG + NSMutableString *logString = [NSMutableString string]; + + [logString appendString:@"\n########################################"]; + [logString appendFormat:@"\nResponse: %@", response]; + + NSString *encodingName = response.textEncodingName; + NSString *dataString = nil; + if (encodingName) { + NSStringEncoding encodingType = CFStringConvertEncodingToNSStringEncoding(CFStringConvertIANACharSetNameToEncoding((CFStringRef)encodingName)); + dataString = [[NSString alloc] initWithData:data encoding:encodingType]; + } + + [logString appendFormat:@"\nData: %@", dataString]; + [logString appendFormat:@"\nError: %@", error]; + [logString appendString:@"\n########################################"]; + + NSLog(@"%@", logString); +#endif +} + +- (NSURLSessionConfiguration *)configuration +{ + return _session.configuration; +} + +#pragma mark - Add/Remove Tasks + +- (NSString *)uniqueToken +{ + return [[NSProcessInfo processInfo] globallyUniqueString]; +} + +- (void)addTask:(NSURLSessionTask *)task + withToken:(NSString *)token + toGroup:(NSString *)group +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + + if (!task) return; + NSString *internalGroup = group; + if (!internalGroup || internalGroup.length == 0) { + internalGroup = kCRSessionControllerGenericTaskGroup; + } + + CourierLogInfo(@"Adding task to group : %@", group); + + // + // Add task to Groups + // + NSMutableArray *tasks = [strongSelf.groups objectForKey:internalGroup]; + if (!tasks) { + tasks = [NSMutableArray array]; + } + [tasks addObject:task]; + [strongSelf.groups setObject:tasks + forKey:internalGroup]; + + // + // Add task to Tasks by token + // + NSMutableDictionary *taskDict = [strongSelf.tasksByToken objectForKey:internalGroup]; + if (!taskDict) { + taskDict = [NSMutableDictionary dictionary]; + } + taskDict[@"task"] = task; + taskDict[@"group"] = internalGroup; + [strongSelf.tasksByToken setObject:taskDict + forKey:token]; + + // + // Add task to tasks by id + // + [strongSelf.tasksByIdentifier setObject:task + forKey:@(task.taskIdentifier)]; + }]; +} + +- (void)removeTaskWithToken:(NSString *)token +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + NSDictionary *taskDict = [strongSelf.tasksByToken objectForKey:token]; + NSString *group = taskDict[@"group"]; + NSURLSessionTask *task = taskDict[@"task"]; + + if (!task) return; + if (!group || group.length == 0) { + group = kCRSessionControllerGenericTaskGroup; + } + + // + // Remove task delegate + // + id delegate = [strongSelf NSURLSessionTaskDelegateForTask:task]; + [strongSelf removeNSURLSessionTaskDelegate:delegate]; + + CourierLogInfo(@"Removing task for URL: %@ in group: %@", + task.currentRequest.URL, + group); + + // + // Get the tasks + // + NSMutableArray *groupTasks = [strongSelf.groups objectForKey:group]; + + // + // Remove the task + // + [strongSelf.tasksByToken removeObjectForKey:token]; + [strongSelf.tasksByIdentifier removeObjectForKey:@(task.taskIdentifier)]; + [groupTasks removeObject:task]; + + // + // If empty, remove tasks array + // + if (groupTasks.count == 0) { + [strongSelf.groups removeObjectForKey:group]; + } + }]; +} + +- (void)addNSURLSessionTaskDelegate:(id <NSURLSessionTaskDelegate>)delegate + forTask:(NSURLSessionTask *)task +{ + NSParameterAssert(delegate); + NSParameterAssert(task); + if (delegate == nil || task == nil) return; + @synchronized(_sessionDelegates) { + [_sessionDelegates setObject:delegate + forKey:[NSValue valueWithNonretainedObject:task]]; + } +} + +- (void)removeNSURLSessionTaskDelegate:(id <NSURLSessionTaskDelegate>)delegate +{ + if (delegate == nil) return; + @synchronized(_sessionDelegates) { + __block id taskKey; + [_sessionDelegates enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + if ([obj isEqual:delegate]) { + taskKey = key; + *stop = YES; + } + }]; + [_sessionDelegates removeObjectForKey:taskKey]; + } +} + +- (id <NSURLSessionTaskDelegate>)NSURLSessionTaskDelegateForTask:(NSURLSessionTask *)task +{ + return [_sessionDelegates objectForKey:[NSValue valueWithNonretainedObject:task]]; +} + +@end + +@implementation CRSessionController (TaskManagement) + +- (NSURLSessionTask *)taskWithIdentifier:(NSUInteger)taskIdentifier +{ + return [_tasksByIdentifier objectForKey:@(taskIdentifier)]; +} + +- (BOOL)hasTaskWithIdentifier:(NSUInteger)taskIdentifier +{ + return [_tasksByIdentifier objectForKey:@(taskIdentifier)] != nil; +} + +- (BOOL)hasTasksInGroup:(NSString *)group + withState:(NSURLSessionTaskState)state +{ + __block BOOL result = NO; + typeof(self) __weak weakSelf = self; + NSOperation *op = [NSBlockOperation blockOperationWithBlock:^{ + NSArray *groupTasks = weakSelf.groups[group]; + for (NSURLSessionTask *task in groupTasks) { + if (task.state == state) { + result = YES; + return; + } + } + }]; + + [self.serialQueue addOperations:@[op] + waitUntilFinished:YES]; + + return result; +} + +- (void)suspendTasksInGroup:(NSString *)group +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + CourierLogInfo(@"Suspend tasks in group : %@", group); + NSArray *tasks = [strongSelf.groups valueForKey:group]; + for (NSURLSessionTask *task in tasks) { + [task suspend]; + } + }]; +} + +- (void)resumeTasksInGroup:(NSString *)group +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + CourierLogInfo(@"Resume tasks in group : %@", group); + NSArray *tasks = [strongSelf.groups valueForKey:group]; + for (NSURLSessionTask *task in tasks) { + [task resume]; + } + }]; +} + +- (void)cancelTasksInGroup:(NSString *)group +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + CourierLogInfo(@"Canceling tasks in group : %@", group); + NSArray *tasks = [strongSelf.groups valueForKey:group]; + for (NSURLSessionTask *task in tasks) { + [task cancel]; + } + }]; +} + +- (void)suspendAllTasks +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + CourierLogInfo(@"Suspend all tasks"); + for (NSString *groupName in strongSelf.groups.allKeys) { + [strongSelf suspendTasksInGroup:groupName]; + } + }]; +} + +- (void)resumeAllTasks +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + CourierLogInfo(@"Resume all tasks"); + for (NSString *groupName in strongSelf.groups.allKeys) { + [strongSelf resumeTasksInGroup:groupName]; + } + }]; +} + +- (void)cancelAllTasks +{ + typeof(self) __weak weakSelf = self; + [self.serialQueue addOperationWithBlock:^{ + typeof(self) __strong strongSelf = weakSelf; + CourierLogInfo(@"Cancel all tasks"); + for (NSString *groupName in strongSelf.groups.allKeys) { + [strongSelf cancelTasksInGroup:groupName]; + } + }]; +} + +@end + +@implementation CRSessionController (Reachability) + +- (BOOL)isInternetReachable +{ + return _reachabilityObject.isReachable; +} + +- (void)startReachabilityMonitoring +{ + [_reachabilityObject startNotifier]; +} + +- (void)stopReachabilityMonitoring +{ + [_reachabilityObject stopNotifier]; +} + +- (void)setReachabilityStatusChangeBlock:(void (^)(NetworkStatus status))block +{ + _reachabilityObject.reachableBlock = ^(Reachability *reachability) { + if (block) { + block(reachability.currentReachabilityStatus); + } + }; + + _reachabilityObject.unreachableBlock = ^(Reachability *reachability) { + if (block) { + block(reachability.currentReachabilityStatus); + } + }; +} + +@end + +@implementation CRSessionController (Debug) + +- (void)logRequests +{ + CourierLogInfo(@"Log current tasks:"); + NSArray *tasks = _tasksByIdentifier.allValues; + for (NSURLSessionTask *task in tasks) { + __unused NSString *description = [NSString stringWithFormat:@"task: %@\n URL: %@\nMethod: %@", + task, + task.currentRequest.URL, + task.currentRequest.HTTPMethod]; + CourierLogInfo(@"%@", description); + } +} + +@end
diff --git a/Courier/CRSessionDelegate.h b/Courier/CRSessionDelegate.h new file mode 100644 index 0000000..184e42b --- /dev/null +++ b/Courier/CRSessionDelegate.h
@@ -0,0 +1,12 @@ +#import <Foundation/Foundation.h> + +@class CRSessionController; + +@interface CRSessionDelegate : NSObject <NSURLSessionDelegate, + NSURLSessionTaskDelegate> + ++ (instancetype)sessionDelegateWithSessionController:(CRSessionController *)sessionController; + +- (instancetype)init NS_UNAVAILABLE; + +@end
diff --git a/Courier/CRSessionDelegate.m b/Courier/CRSessionDelegate.m new file mode 100644 index 0000000..072b572 --- /dev/null +++ b/Courier/CRSessionDelegate.m
@@ -0,0 +1,67 @@ +#import "CRSessionDelegate.h" + +#import "CRSessionController.h" + +@interface CRSessionDelegate () + +@property (nonatomic, weak) CRSessionController *sessionController; + +@end + +@implementation CRSessionDelegate + +- (instancetype)initWithSessionController:(CRSessionController *)sessionController +{ + self = [super init]; + if (self) { + _sessionController = sessionController; + } + return self; +} + ++ (instancetype)sessionDelegateWithSessionController:(CRSessionController *)sessionController +{ + CRSessionDelegate *delegate = [[self alloc] initWithSessionController:sessionController]; + return delegate; +} + +#pragma mark - NSURLSessionTaskDelegate + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +willPerformHTTPRedirection:(NSHTTPURLResponse *)response + newRequest:(NSURLRequest *)request + completionHandler:(void (^)(NSURLRequest *))completionHandler +{ + id delegate = [self.sessionController NSURLSessionTaskDelegateForTask:task]; + if ([delegate respondsToSelector:@selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)]) { + [delegate URLSession:session + task:task + willPerformHTTPRedirection:response + newRequest:request + completionHandler:completionHandler]; + } else { + if (completionHandler) completionHandler(request); + } +} + +- (void)URLSession:(NSURLSession *)session + task:(NSURLSessionTask *)task +didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge + completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, + NSURLCredential *))completionHandler +{ + id delegate = [self.sessionController NSURLSessionTaskDelegateForTask:task]; + if ([delegate respondsToSelector:@selector(URLSession:task:didReceiveChallenge:completionHandler:)]) { + [delegate URLSession:session + task:task + didReceiveChallenge:challenge + completionHandler:completionHandler]; + } else { + if (completionHandler) { + completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil); + } + } +} + +@end
diff --git a/Courier/Courier.h b/Courier/Courier.h new file mode 100644 index 0000000..e7e4cef --- /dev/null +++ b/Courier/Courier.h
@@ -0,0 +1,37 @@ +// +// CourierSession.h +// Courier +// +// Created by Andrew Smith on 9/22/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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. +// +// + +#ifndef Courier_CourierSession_h +#define Courier_CourierSession_h + +#import "NSString+Courier.h" +#import "NSMutableURLRequest+Courier.h" +#import "NSURLResponse+Courier.h" +#import "NSDictionary+Courier.h" +#import "CRSessionController.h" + +#endif +
diff --git a/Courier/CourierLog.h b/Courier/CourierLog.h new file mode 100644 index 0000000..8d5a63a --- /dev/null +++ b/Courier/CourierLog.h
@@ -0,0 +1,22 @@ +// +// CourierLog.h +// Courier +// +// Created by Andrew Smith on 2/3/14. +// Copyright (c) 2014 Andrew B. Smith. All rights reserved. +// + +#ifndef Courier_CourierLog_h +#define Courier_CourierLog_h + +#define COURIER_LOG 0 + +#if DEBUG && COURIER_LOG + #define CourierLogInfo(...) NSLog(@"%s %@", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__]) + #define CourierLogWarning(...) NSLog(@"\n!!!!\n%s %@\n!!!!\n", __PRETTY_FUNCTION__, [NSString stringWithFormat:__VA_ARGS__]) +#else + #define CourierLogInfo(...) do { } while (0) + #define CourierLogWarning(...) do { } while (0) +#endif + +#endif
diff --git a/Courier/NSDictionary+Courier.h b/Courier/NSDictionary+Courier.h new file mode 100644 index 0000000..e0a06de --- /dev/null +++ b/Courier/NSDictionary+Courier.h
@@ -0,0 +1,50 @@ +// +// NSDictionary+Courier.h +// Courier +// +// Created by Andrew Smith on 2/28/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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> + +@interface NSDictionary (Courier) + +/** + * NSUTF8StringEncoding encoded JSON string + */ +- (NSString *)cou_asJSONString; + +/** + * NSJSONSerialization representation + */ +- (NSData *)cou_asJSONData; + +/** + * & joined form URL encoded string as NSData + */ +- (NSData *)cou_asFormURLEncodedData; + +/** + * Returns URL formated query string, "key1=value1&key2=value2..." + */ +- (NSString *)cou_asFormURLEncodedString; + +@end
diff --git a/Courier/NSDictionary+Courier.m b/Courier/NSDictionary+Courier.m new file mode 100644 index 0000000..a4bc096 --- /dev/null +++ b/Courier/NSDictionary+Courier.m
@@ -0,0 +1,76 @@ +// +// NSDictionary+Courier.m +// Courier +// +// Created by Andrew Smith on 2/28/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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 "NSDictionary+Courier.h" +#import "NSString+Courier.h" + +@implementation NSDictionary (Courier) + +#pragma mark - application/json + +- (NSString *)cou_asJSONString +{ + NSData *jsonData = [self cou_asJSONData]; + if (!jsonData) return nil; + return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; +} + +- (NSData *)cou_asJSONData +{ + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:self + options:0 + error:&error]; + + if (!jsonData) NSLog(@"JSON serialization error: %@", error); + + return jsonData; +} + +#pragma mark - application/x-www-form-urlencoded + +- (NSString *)cou_asFormURLEncodedString +{ + __block NSMutableArray *mutableParameterComponents = [NSMutableArray array]; + + [self enumerateKeysAndObjectsUsingBlock:^(id key, id obj, BOOL *stop) { + NSString *encodedKey = [[key description] cou_urlEncodedStringWithEncoding:NSUTF8StringEncoding]; + NSString *encodedValue = [[obj description] cou_urlEncodedStringWithEncoding:NSUTF8StringEncoding]; + NSString *component = [NSString stringWithFormat:@"%@=%@", encodedKey, encodedValue]; + [mutableParameterComponents addObject:component]; + }]; + + NSString *andJoinedString = [mutableParameterComponents componentsJoinedByString:@"&"]; + + return andJoinedString; +} + +- (NSData *)cou_asFormURLEncodedData +{ + NSString *andJoinedString = [self cou_asFormURLEncodedString]; + return [andJoinedString dataUsingEncoding:NSUTF8StringEncoding]; +} + +@end
diff --git a/Courier/NSMutableURLRequest+Courier.h b/Courier/NSMutableURLRequest+Courier.h new file mode 100644 index 0000000..cd32168 --- /dev/null +++ b/Courier/NSMutableURLRequest+Courier.h
@@ -0,0 +1,53 @@ +// +// NSMutableURLRequest+Courier.h +// Courier +// +// Created by Andrew Smith on 7/25/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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> + +typedef enum { + CRURLRequestEncodingUnknown, + CRURLFormURLParameterEncoding, + CRURLJSONParameterEncoding, + CRURLImageEncoding, +} CRURLRequestEncoding; + +@interface NSMutableURLRequest (Courier) + ++ (NSMutableURLRequest *)cou_requestWithMethod:(NSString *)method + path:(NSString *)path; + +/** + + @note This will set the Content-Type header to the appropriate type, unless you + specify your own. + + */ ++ (NSMutableURLRequest *)cou_requestWithMethod:(NSString *)method + path:(NSString *)path + encoding:(CRURLRequestEncoding)encoding + URLParameters:(NSDictionary *)urlParameters + HTTPBodyParameters:(NSDictionary *)httpBodyParameters + header:(NSDictionary *)header; + +@end
diff --git a/Courier/NSMutableURLRequest+Courier.m b/Courier/NSMutableURLRequest+Courier.m new file mode 100644 index 0000000..9648716 --- /dev/null +++ b/Courier/NSMutableURLRequest+Courier.m
@@ -0,0 +1,132 @@ +// +// NSMutableURLRequest+Courier.m +// Courier +// +// Created by Andrew Smith on 7/25/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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 "NSMutableURLRequest+Courier.h" + +#import "NSDictionary+Courier.h" + +@implementation NSMutableURLRequest (Courier) + ++ (NSMutableURLRequest *)cou_requestWithMethod:(NSString *)method + path:(NSString *)path +{ + return [self cou_requestWithMethod:method + path:path + encoding:CRURLRequestEncodingUnknown + URLParameters:nil + HTTPBodyParameters:nil + header:nil]; +} + ++ (NSMutableURLRequest *)cou_requestWithMethod:(NSString *)method + path:(NSString *)path + encoding:(CRURLRequestEncoding)encoding + URLParameters:(NSDictionary *)urlParameters + HTTPBodyParameters:(NSDictionary *)httpBodyParameters + header:(NSDictionary *)header +{ + NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init]; + + [request setURLPath:path withParameters:urlParameters]; + [request setHTTPMethod:method]; + [request setHTTPBodyDataWithParameters:httpBodyParameters encoding:encoding]; + [request setAllHTTPHeaderFields:header]; + + // Only add content type if there are body params. Play! filters seems to + // have an issue with setting Content-Type header but not provide a body. + if (httpBodyParameters && httpBodyParameters.count > 0) { + [request addHeaderContentTypeForEncoding:encoding]; + } + + return request; +} + +#pragma mark - URL + +- (void)setURLPath:(NSString *)path + withParameters:(NSDictionary *)parameters +{ + // + // Append path with URL params, if present + // + if (parameters.count > 0) { + NSString *stringToAppend = [path rangeOfString:@"?"].location == NSNotFound ? @"?%@" : @"&%@"; + NSString *newPath = [path stringByAppendingFormat:stringToAppend, [parameters cou_asFormURLEncodedString]]; + path = newPath; + } + + self.URL = [NSURL URLWithString:path]; +} + +#pragma mark - Header + +- (void)addHeaderContentTypeForEncoding:(CRURLRequestEncoding)encoding +{ + // + // Bail if content type is already set + // + if (self.allHTTPHeaderFields[@"Content-Type"]) return; + + // + // Set the content type + // + if (encoding == CRURLFormURLParameterEncoding) { + // Form URL + NSString *charset = (__bridge NSString *)CFStringConvertEncodingToIANACharSetName(CFStringConvertNSStringEncodingToEncoding(NSUTF8StringEncoding)); + NSString *type = [NSString stringWithFormat:@"application/x-www-form-urlencoded; charset=%@", charset]; + [self addValue:type forHTTPHeaderField:@"Content-Type"]; + } else if (encoding == CRURLJSONParameterEncoding) { + // JSON + [self addValue:@"application/json,text/json,text/javascript" forHTTPHeaderField:@"Content-Type"]; + } else if (encoding == CRURLImageEncoding) { + [self addValue:@"image/tiff,image/jpeg,image/gif,image/png,image/ico,image/x-icon,image/bmp,image/x-bmp,image/x-xbitmap,image/x-win-bitmap" + forHTTPHeaderField:@"Content-Type"]; + } +} + +#pragma mark - Body + +- (void)setHTTPBodyDataWithParameters:(NSDictionary *)parameters + encoding:(CRURLRequestEncoding)encoding +{ + if (!parameters) return; + + NSData *bodyData = nil; + + if (encoding == CRURLFormURLParameterEncoding) { + // Form URL + bodyData = [parameters cou_asFormURLEncodedData]; + } else if (encoding == CRURLJSONParameterEncoding) { + // JSON + bodyData = [parameters cou_asJSONData]; + } else { + // Unknown encoding. Pass it through. + bodyData = [NSKeyedArchiver archivedDataWithRootObject:parameters]; + } + + [self setHTTPBody:bodyData]; +} + +@end
diff --git a/Courier/NSString+Courier.h b/Courier/NSString+Courier.h new file mode 100644 index 0000000..f6eca57 --- /dev/null +++ b/Courier/NSString+Courier.h
@@ -0,0 +1,35 @@ +// +// NSString+Courier.h +// Courier +// +// Created by Andrew Smith on 10/19/11. +// Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// +// 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> + +@interface NSString (Courier) + +- (NSString *)cou_urlEncodedString; + +- (NSString *)cou_urlEncodedStringWithEncoding:(NSStringEncoding)encoding; + +@end
diff --git a/Courier/NSString+Courier.m b/Courier/NSString+Courier.m new file mode 100644 index 0000000..cfb3a0e --- /dev/null +++ b/Courier/NSString+Courier.m
@@ -0,0 +1,47 @@ +// +// NSString+Courier.m +// Courier +// +// Created by Andrew Smith on 10/19/11. +// Copyright (c) 2011 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// +// 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 "NSString+Courier.h" + +@implementation NSString (Courier) + +- (NSString *)cou_urlEncodedString +{ + return [self cou_urlEncodedStringWithEncoding:NSUTF8StringEncoding]; +} + +// See http://github.com/pokeb/asi-http-request/raw/master/Classes/ASIFormDataRequest.m +- (NSString *)cou_urlEncodedStringWithEncoding:(NSStringEncoding)encoding +{ + NSString *urlEncodedString = (__bridge_transfer NSString *)CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault, + (__bridge CFStringRef)self, + NULL, (CFStringRef)@":/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`", + CFStringConvertNSStringEncodingToEncoding(encoding)); + return urlEncodedString ? urlEncodedString : @""; +} + +@end
diff --git a/Courier/NSURLResponse+Courier.h b/Courier/NSURLResponse+Courier.h new file mode 100644 index 0000000..a0dd9e4 --- /dev/null +++ b/Courier/NSURLResponse+Courier.h
@@ -0,0 +1,41 @@ +// +// NSURLResponse+Courier.h +// Courier +// +// Created by Andrew Smith on 8/31/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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> + +@interface NSURLResponse (Courier) + +/** + @returns The HTTP status code for the response. + */ +@property (readonly) NSInteger cou_statusCode; + +/** + @returns YES if the response was successful, checking the HTTP status code for 2xx. + */ +- (BOOL)cou_success; + +@end
diff --git a/Courier/NSURLResponse+Courier.m b/Courier/NSURLResponse+Courier.m new file mode 100644 index 0000000..6162da1 --- /dev/null +++ b/Courier/NSURLResponse+Courier.m
@@ -0,0 +1,41 @@ +// +// NSURLResponse+Courier.m +// Courier +// +// Created by Andrew Smith on 8/31/13. +// Copyright (c) 2013 Andrew B. Smith ( http://github.com/drewsmits ). All rights reserved. +// +// 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 "NSURLResponse+Courier.h" + +@implementation NSURLResponse (Courier) + +- (NSInteger)cou_statusCode +{ + return [(NSHTTPURLResponse *)self statusCode]; +} + +- (BOOL)cou_success +{ + NSInteger statusCode = [(NSHTTPURLResponse *)self statusCode]; + return (statusCode >= 200 && statusCode < 300); +} + +@end
diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6aaf5e1 --- /dev/null +++ b/LICENSE
@@ -0,0 +1,19 @@ +Copyright (c) 2014 Andrew B. Smith (http://github.com/Drewsmits) + +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.
diff --git a/README.md b/README.md new file mode 100644 index 0000000..87c1302 --- /dev/null +++ b/README.md
@@ -0,0 +1,56 @@ +# Courier + +Courier is a simple network layer built on NSURLSession and various categories of convenience. + +* Easy network activity display management +* Respond to changes in reachability +* Respond to 401 unauthorized errors +* Request logging +* Easily build NSURLRequests with proper encoding + + +## Getting Started + +```objc +#import <Courier/Courier.h> + +// Default config +NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration]; + +// Add additional headers +config.HTTPAdditionalHeaders = @{@"Accept" : @"application/json"}; + +// Be a good API citizen and limit to 1 HTTP connection per host +config.HTTPMaximumConnectionsPerHost = 1; + +// Build session controller +CRSessionController *sessionController = [CRSessionController sessionControllerWithConfiguration:config + delegate:controller]; + +// Build a POST request +NSMutableURLRequest *request = [NSMutableURLRequest requestWithMethod:@"POST" + path:@"https://service.com/api" + encoding:CR_URLJSONParameterEncoding + URLParameters:@{@"urlParam" : @"value"} + HTTPBodyParameters:@{@"bodyParam" : @"value"} + header:@{@"Header-Name" : @"value"}]; + + +// Build a task +NSURLSessionDataTask *task = [sessionController dataTaskForRequest:request + completionHandler:^(NSData *data, + NSURLResponse *response, + NSError *error) { + if (response.success) { + // Hurrah! + } + }]; + +// Start task +[task resume]; + +``` + +## Externals + +* [Reachability library](https://github.com/tonymillion/Reachability) was created and maintained by [Tony Million](https://github.com/tonymillion).