Project import
diff --git a/App/CourierTestApp.xcodeproj/project.pbxproj b/App/CourierTestApp.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..c4615de
--- /dev/null
+++ b/App/CourierTestApp.xcodeproj/project.pbxproj
@@ -0,0 +1,490 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		09FFE9651ACB2395002A6A32 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 09FFE9641ACB2395002A6A32 /* main.m */; };
+		09FFE9681ACB2395002A6A32 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 09FFE9671ACB2395002A6A32 /* AppDelegate.m */; };
+		09FFE96B1ACB2396002A6A32 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 09FFE96A1ACB2396002A6A32 /* ViewController.m */; };
+		09FFE96E1ACB2396002A6A32 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 09FFE96C1ACB2396002A6A32 /* Main.storyboard */; };
+		09FFE9701ACB2396002A6A32 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 09FFE96F1ACB2396002A6A32 /* Images.xcassets */; };
+		09FFE9731ACB2396002A6A32 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 09FFE9711ACB2396002A6A32 /* LaunchScreen.xib */; };
+		09FFE97F1ACB2396002A6A32 /* CourierTestAppTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 09FFE97E1ACB2396002A6A32 /* CourierTestAppTests.m */; };
+		9C591CC5D71BD9A448553B40 /* libPods.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3DA7B28904FB1698F426DBD6 /* libPods.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		09FFE9791ACB2396002A6A32 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 09FFE9571ACB2395002A6A32 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 09FFE95E1ACB2395002A6A32;
+			remoteInfo = CourierTestApp;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		09FFE95F1ACB2395002A6A32 /* CourierTestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CourierTestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		09FFE9631ACB2395002A6A32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		09FFE9641ACB2395002A6A32 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		09FFE9661ACB2395002A6A32 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		09FFE9671ACB2395002A6A32 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		09FFE9691ACB2396002A6A32 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+		09FFE96A1ACB2396002A6A32 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+		09FFE96D1ACB2396002A6A32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		09FFE96F1ACB2396002A6A32 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		09FFE9721ACB2396002A6A32 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = "<group>"; };
+		09FFE9781ACB2396002A6A32 /* CourierTestAppTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CourierTestAppTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		09FFE97D1ACB2396002A6A32 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		09FFE97E1ACB2396002A6A32 /* CourierTestAppTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CourierTestAppTests.m; sourceTree = "<group>"; };
+		312CFCA0271C95DA4B15CF14 /* Pods.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.release.xcconfig; path = "Pods/Target Support Files/Pods/Pods.release.xcconfig"; sourceTree = "<group>"; };
+		3DA7B28904FB1698F426DBD6 /* libPods.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libPods.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		BD9309BA9B26825399B6427C /* Pods.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = Pods.debug.xcconfig; path = "Pods/Target Support Files/Pods/Pods.debug.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		09FFE95C1ACB2395002A6A32 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				9C591CC5D71BD9A448553B40 /* libPods.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		09FFE9751ACB2396002A6A32 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		028C0C610717DB87144BB0E5 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				BD9309BA9B26825399B6427C /* Pods.debug.xcconfig */,
+				312CFCA0271C95DA4B15CF14 /* Pods.release.xcconfig */,
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+		02DB9B2E5188BE500AEEEE46 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				3DA7B28904FB1698F426DBD6 /* libPods.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		09FFE9561ACB2395002A6A32 = {
+			isa = PBXGroup;
+			children = (
+				09FFE9611ACB2395002A6A32 /* CourierTestApp */,
+				09FFE97B1ACB2396002A6A32 /* CourierTestAppTests */,
+				09FFE9601ACB2395002A6A32 /* Products */,
+				028C0C610717DB87144BB0E5 /* Pods */,
+				02DB9B2E5188BE500AEEEE46 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		09FFE9601ACB2395002A6A32 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				09FFE95F1ACB2395002A6A32 /* CourierTestApp.app */,
+				09FFE9781ACB2396002A6A32 /* CourierTestAppTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		09FFE9611ACB2395002A6A32 /* CourierTestApp */ = {
+			isa = PBXGroup;
+			children = (
+				09FFE9661ACB2395002A6A32 /* AppDelegate.h */,
+				09FFE9671ACB2395002A6A32 /* AppDelegate.m */,
+				09FFE9691ACB2396002A6A32 /* ViewController.h */,
+				09FFE96A1ACB2396002A6A32 /* ViewController.m */,
+				09FFE96C1ACB2396002A6A32 /* Main.storyboard */,
+				09FFE96F1ACB2396002A6A32 /* Images.xcassets */,
+				09FFE9711ACB2396002A6A32 /* LaunchScreen.xib */,
+				09FFE9621ACB2395002A6A32 /* Supporting Files */,
+			);
+			path = CourierTestApp;
+			sourceTree = "<group>";
+		};
+		09FFE9621ACB2395002A6A32 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				09FFE9631ACB2395002A6A32 /* Info.plist */,
+				09FFE9641ACB2395002A6A32 /* main.m */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		09FFE97B1ACB2396002A6A32 /* CourierTestAppTests */ = {
+			isa = PBXGroup;
+			children = (
+				09FFE97E1ACB2396002A6A32 /* CourierTestAppTests.m */,
+				09FFE97C1ACB2396002A6A32 /* Supporting Files */,
+			);
+			path = CourierTestAppTests;
+			sourceTree = "<group>";
+		};
+		09FFE97C1ACB2396002A6A32 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				09FFE97D1ACB2396002A6A32 /* Info.plist */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		09FFE95E1ACB2395002A6A32 /* CourierTestApp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 09FFE9821ACB2396002A6A32 /* Build configuration list for PBXNativeTarget "CourierTestApp" */;
+			buildPhases = (
+				86481C906538BEFFFEC24753 /* Check Pods Manifest.lock */,
+				09FFE95B1ACB2395002A6A32 /* Sources */,
+				09FFE95C1ACB2395002A6A32 /* Frameworks */,
+				09FFE95D1ACB2395002A6A32 /* Resources */,
+				4BB7C20D3F8FD9B6AA12B90B /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = CourierTestApp;
+			productName = CourierTestApp;
+			productReference = 09FFE95F1ACB2395002A6A32 /* CourierTestApp.app */;
+			productType = "com.apple.product-type.application";
+		};
+		09FFE9771ACB2396002A6A32 /* CourierTestAppTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 09FFE9851ACB2396002A6A32 /* Build configuration list for PBXNativeTarget "CourierTestAppTests" */;
+			buildPhases = (
+				09FFE9741ACB2396002A6A32 /* Sources */,
+				09FFE9751ACB2396002A6A32 /* Frameworks */,
+				09FFE9761ACB2396002A6A32 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				09FFE97A1ACB2396002A6A32 /* PBXTargetDependency */,
+			);
+			name = CourierTestAppTests;
+			productName = CourierTestAppTests;
+			productReference = 09FFE9781ACB2396002A6A32 /* CourierTestAppTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		09FFE9571ACB2395002A6A32 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0610;
+				ORGANIZATIONNAME = "Andrew B. Smith";
+				TargetAttributes = {
+					09FFE95E1ACB2395002A6A32 = {
+						CreatedOnToolsVersion = 6.1;
+					};
+					09FFE9771ACB2396002A6A32 = {
+						CreatedOnToolsVersion = 6.1;
+						TestTargetID = 09FFE95E1ACB2395002A6A32;
+					};
+				};
+			};
+			buildConfigurationList = 09FFE95A1ACB2395002A6A32 /* Build configuration list for PBXProject "CourierTestApp" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 09FFE9561ACB2395002A6A32;
+			productRefGroup = 09FFE9601ACB2395002A6A32 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				09FFE95E1ACB2395002A6A32 /* CourierTestApp */,
+				09FFE9771ACB2396002A6A32 /* CourierTestAppTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		09FFE95D1ACB2395002A6A32 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				09FFE96E1ACB2396002A6A32 /* Main.storyboard in Resources */,
+				09FFE9731ACB2396002A6A32 /* LaunchScreen.xib in Resources */,
+				09FFE9701ACB2396002A6A32 /* Images.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		09FFE9761ACB2396002A6A32 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		4BB7C20D3F8FD9B6AA12B90B /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods/Pods-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		86481C906538BEFFFEC24753 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		09FFE95B1ACB2395002A6A32 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				09FFE96B1ACB2396002A6A32 /* ViewController.m in Sources */,
+				09FFE9681ACB2395002A6A32 /* AppDelegate.m in Sources */,
+				09FFE9651ACB2395002A6A32 /* main.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		09FFE9741ACB2396002A6A32 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				09FFE97F1ACB2396002A6A32 /* CourierTestAppTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		09FFE97A1ACB2396002A6A32 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 09FFE95E1ACB2395002A6A32 /* CourierTestApp */;
+			targetProxy = 09FFE9791ACB2396002A6A32 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		09FFE96C1ACB2396002A6A32 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				09FFE96D1ACB2396002A6A32 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		09FFE9711ACB2396002A6A32 /* LaunchScreen.xib */ = {
+			isa = PBXVariantGroup;
+			children = (
+				09FFE9721ACB2396002A6A32 /* Base */,
+			);
+			name = LaunchScreen.xib;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		09FFE9801ACB2396002A6A32 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		09FFE9811ACB2396002A6A32 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = YES;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 8.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		09FFE9831ACB2396002A6A32 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = BD9309BA9B26825399B6427C /* Pods.debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = CourierTestApp/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		09FFE9841ACB2396002A6A32 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 312CFCA0271C95DA4B15CF14 /* Pods.release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = CourierTestApp/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		09FFE9861ACB2396002A6A32 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(SDKROOT)/Developer/Library/Frameworks",
+					"$(inherited)",
+				);
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = CourierTestAppTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CourierTestApp.app/CourierTestApp";
+			};
+			name = Debug;
+		};
+		09FFE9871ACB2396002A6A32 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(SDKROOT)/Developer/Library/Frameworks",
+					"$(inherited)",
+				);
+				INFOPLIST_FILE = CourierTestAppTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CourierTestApp.app/CourierTestApp";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		09FFE95A1ACB2395002A6A32 /* Build configuration list for PBXProject "CourierTestApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				09FFE9801ACB2396002A6A32 /* Debug */,
+				09FFE9811ACB2396002A6A32 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		09FFE9821ACB2396002A6A32 /* Build configuration list for PBXNativeTarget "CourierTestApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				09FFE9831ACB2396002A6A32 /* Debug */,
+				09FFE9841ACB2396002A6A32 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		09FFE9851ACB2396002A6A32 /* Build configuration list for PBXNativeTarget "CourierTestAppTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				09FFE9861ACB2396002A6A32 /* Debug */,
+				09FFE9871ACB2396002A6A32 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 09FFE9571ACB2395002A6A32 /* Project object */;
+}
diff --git a/App/CourierTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/App/CourierTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..8bedd13
--- /dev/null
+++ b/App/CourierTestApp.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:CourierTestApp.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/App/CourierTestApp/AppDelegate.h b/App/CourierTestApp/AppDelegate.h
new file mode 100644
index 0000000..0e2fa29
--- /dev/null
+++ b/App/CourierTestApp/AppDelegate.h
@@ -0,0 +1,14 @@
+#import <UIKit/UIKit.h>
+
+@class CRSessionController;
+
+@interface AppDelegate : UIResponder <UIApplicationDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@property (nonatomic, strong, readonly) CRSessionController *sessionController;
+
++ (instancetype)sharedAppDelegate;
+
+@end
+
diff --git a/App/CourierTestApp/AppDelegate.m b/App/CourierTestApp/AppDelegate.m
new file mode 100644
index 0000000..a503ac3
--- /dev/null
+++ b/App/CourierTestApp/AppDelegate.m
@@ -0,0 +1,50 @@
+#import "AppDelegate.h"
+
+#import <Courier/Courier.h>
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
++ (instancetype)sharedAppDelegate {
+    static AppDelegate *_sharedAppDelegate;
+    static dispatch_once_t once;
+    dispatch_once(&once, ^{
+        _sharedAppDelegate = [[UIApplication sharedApplication] delegate];
+    });
+    return _sharedAppDelegate;
+}
+
+- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
+//    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
+//    configuration.HTTPMaximumConnectionsPerHost = 1;
+//    configuration.URLCache = nil;
+//    _sessionController = [CRSessionController sessionControllerWithConfiguration:configuration];
+    return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application {
+    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
+    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application {
+    // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
+    // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application {
+    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application {
+    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application {
+    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
+}
+
+@end
diff --git a/App/CourierTestApp/Base.lproj/LaunchScreen.xib b/App/CourierTestApp/Base.lproj/LaunchScreen.xib
new file mode 100644
index 0000000..c3ca81e
--- /dev/null
+++ b/App/CourierTestApp/Base.lproj/LaunchScreen.xib
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="6214" systemVersion="14A314h" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6207"/>
+        <capability name="Constraints with non-1.0 multipliers" minToolsVersion="5.1"/>
+    </dependencies>
+    <objects>
+        <placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
+        <placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
+        <view contentMode="scaleToFill" id="iN0-l3-epB">
+            <rect key="frame" x="0.0" y="0.0" width="480" height="480"/>
+            <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+            <subviews>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="  Copyright (c) 2015 Andrew B. Smith. All rights reserved." textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="8ie-xW-0ye">
+                    <rect key="frame" x="20" y="439" width="441" height="21"/>
+                    <fontDescription key="fontDescription" type="system" pointSize="17"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+                <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CourierTestApp" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="kId-c2-rCX">
+                    <rect key="frame" x="20" y="140" width="441" height="43"/>
+                    <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
+                    <color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
+                    <nil key="highlightedColor"/>
+                </label>
+            </subviews>
+            <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+            <constraints>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="centerY" secondItem="iN0-l3-epB" secondAttribute="bottom" multiplier="1/3" constant="1" id="5cJ-9S-tgC"/>
+                <constraint firstAttribute="centerX" secondItem="kId-c2-rCX" secondAttribute="centerX" id="Koa-jz-hwk"/>
+                <constraint firstAttribute="bottom" secondItem="8ie-xW-0ye" secondAttribute="bottom" constant="20" id="Kzo-t9-V3l"/>
+                <constraint firstItem="8ie-xW-0ye" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="MfP-vx-nX0"/>
+                <constraint firstAttribute="centerX" secondItem="8ie-xW-0ye" secondAttribute="centerX" id="ZEH-qu-HZ9"/>
+                <constraint firstItem="kId-c2-rCX" firstAttribute="leading" secondItem="iN0-l3-epB" secondAttribute="leading" constant="20" symbolic="YES" id="fvb-Df-36g"/>
+            </constraints>
+            <nil key="simulatedStatusBarMetrics"/>
+            <freeformSimulatedSizeMetrics key="simulatedDestinationMetrics"/>
+            <point key="canvasLocation" x="548" y="455"/>
+        </view>
+    </objects>
+</document>
diff --git a/App/CourierTestApp/Base.lproj/Main.storyboard b/App/CourierTestApp/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..84078e8
--- /dev/null
+++ b/App/CourierTestApp/Base.lproj/Main.storyboard
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="6751" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="vXZ-lx-hvc">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="6736"/>
+        <capability name="Constraints to layout margins" minToolsVersion="6.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="ufC-wZ-h7g">
+            <objects>
+                <viewController id="vXZ-lx-hvc" customClass="ViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="jyV-Pf-zRb"/>
+                        <viewControllerLayoutGuide type="bottom" id="2fi-mo-0CV"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="kh9-bI-dsS">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
+                        <subviews>
+                            <imageView userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="W7v-tO-sDv">
+                                <rect key="frame" x="16" y="300" width="568" height="300"/>
+                                <constraints>
+                                    <constraint firstAttribute="height" constant="300" id="9ln-1S-2xz"/>
+                                </constraints>
+                            </imageView>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="mbg-b7-n8O">
+                                <rect key="frame" x="260" y="20" width="80" height="30"/>
+                                <constraints>
+                                    <constraint firstAttribute="width" constant="80" id="iW8-UA-c2D"/>
+                                </constraints>
+                                <state key="normal" title="Start">
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="startNetworkRequests:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="cKE-jB-pBa"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="nnb-Ze-y8n">
+                                <rect key="frame" x="260" y="58" width="80" height="30"/>
+                                <state key="normal" title="Stop">
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="stopNetworkRequests:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="O0J-go-24z"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="412-5d-1qH">
+                                <rect key="frame" x="277" y="96" width="50" height="30"/>
+                                <state key="normal" title="dealloc">
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="nilSessionController:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="Qew-M4-FgQ"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="siR-Xe-rkd">
+                                <rect key="frame" x="348" y="20" width="80" height="30"/>
+                                <state key="normal" title="Start Async">
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="startNetworkRequestsOnAnotherThread:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="roa-Jl-I6f"/>
+                                </connections>
+                            </button>
+                            <button opaque="NO" contentMode="scaleToFill" fixedFrame="YES" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="QBn-lF-tWF">
+                                <rect key="frame" x="64" y="20" width="81" height="30"/>
+                                <state key="normal" title="Print Status">
+                                    <color key="titleShadowColor" white="0.5" alpha="1" colorSpace="calibratedWhite"/>
+                                </state>
+                                <connections>
+                                    <action selector="printHasTasks:" destination="vXZ-lx-hvc" eventType="touchUpInside" id="S0l-JL-Rcc"/>
+                                </connections>
+                            </button>
+                        </subviews>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                        <constraints>
+                            <constraint firstItem="mbg-b7-n8O" firstAttribute="centerX" secondItem="nnb-Ze-y8n" secondAttribute="centerX" id="AZz-8n-EFE"/>
+                            <constraint firstItem="W7v-tO-sDv" firstAttribute="leading" secondItem="kh9-bI-dsS" secondAttribute="leadingMargin" id="FMj-fl-lFa"/>
+                            <constraint firstItem="siR-Xe-rkd" firstAttribute="leading" secondItem="mbg-b7-n8O" secondAttribute="trailing" constant="8" id="LoR-x2-pYm"/>
+                            <constraint firstAttribute="centerX" secondItem="mbg-b7-n8O" secondAttribute="centerX" id="OBI-aV-Myj"/>
+                            <constraint firstItem="mbg-b7-n8O" firstAttribute="width" secondItem="nnb-Ze-y8n" secondAttribute="width" id="Sjw-7O-wF8"/>
+                            <constraint firstAttribute="trailingMargin" secondItem="W7v-tO-sDv" secondAttribute="trailing" id="VLe-NO-k9a"/>
+                            <constraint firstItem="siR-Xe-rkd" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" id="W7s-jw-bWi"/>
+                            <constraint firstItem="mbg-b7-n8O" firstAttribute="top" secondItem="jyV-Pf-zRb" secondAttribute="bottom" id="shb-wi-QIM"/>
+                            <constraint firstItem="nnb-Ze-y8n" firstAttribute="top" secondItem="mbg-b7-n8O" secondAttribute="bottom" constant="8" id="uOL-me-FGM"/>
+                            <constraint firstItem="2fi-mo-0CV" firstAttribute="top" secondItem="W7v-tO-sDv" secondAttribute="bottom" id="xDd-zA-WT6"/>
+                        </constraints>
+                    </view>
+                    <connections>
+                        <outlet property="imageView" destination="W7v-tO-sDv" id="w5n-mO-yQb"/>
+                    </connections>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="x5A-6p-PRh" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="305" y="276"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/App/CourierTestApp/Images.xcassets/AppIcon.appiconset/Contents.json b/App/CourierTestApp/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..118c98f
--- /dev/null
+++ b/App/CourierTestApp/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,38 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/App/CourierTestApp/Info.plist b/App/CourierTestApp/Info.plist
new file mode 100644
index 0000000..214cba0
--- /dev/null
+++ b/App/CourierTestApp/Info.plist
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>andrewbsmith.$(PRODUCT_NAME:rfc1034identifier)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/App/CourierTestApp/ViewController.h b/App/CourierTestApp/ViewController.h
new file mode 100644
index 0000000..9c7dfc5
--- /dev/null
+++ b/App/CourierTestApp/ViewController.h
@@ -0,0 +1,6 @@
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+
+@end
+
diff --git a/App/CourierTestApp/ViewController.m b/App/CourierTestApp/ViewController.m
new file mode 100644
index 0000000..1daa4b1
--- /dev/null
+++ b/App/CourierTestApp/ViewController.m
@@ -0,0 +1,116 @@
+#import "ViewController.h"
+#import "AppDelegate.h"
+
+#import <Courier/Courier.h>
+
+static NSString * const TestTaskGroup1 = @"TestTaskGroup1";
+
+static NSString * const TestTaskGroup2 = @"TestTaskGroup2";
+
+@interface ViewController ()
+
+@property (nonatomic, strong) CRSessionController *sessionController;
+
+@property (nonatomic, weak) IBOutlet UIImageView *imageView;
+
+@end
+
+@implementation ViewController
+
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
+    configuration.HTTPMaximumConnectionsPerHost = 1;
+    configuration.URLCache = nil;
+    _sessionController = [CRSessionController sessionControllerWithConfiguration:configuration];
+    
+    [_sessionController setReachabilityStatusChangeBlock:^(NetworkStatus status) {
+        if (status == NotReachable) {
+            NSLog(@"Not reachable!");
+        } else {
+            NSLog(@"Reachable!");
+        }
+    }];
+    
+    [_sessionController startReachabilityMonitoring];
+}
+
+- (void)didReceiveMemoryWarning {
+    [super didReceiveMemoryWarning];
+    // Dispose of any resources that can be recreated.
+}
+
+#pragma mark - IBAction
+
+- (IBAction)startNetworkRequests:(id)sender {
+    for (NSInteger i = 0; i < 10; i++) {
+        BOOL even = i % 2;
+        if (even) {
+            [self startImageDownloadTaskInTaskGroup:TestTaskGroup1];
+        } else {
+            [self startImageDownloadTaskInTaskGroup:TestTaskGroup2];
+        }
+    }
+}
+
+- (IBAction)startNetworkRequestsOnAnotherThread:(id)sender {
+    for (NSInteger i = 0; i < 10; i++) {
+        BOOL even = i % 2;
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
+            if (even) {
+                [self startImageDownloadTaskInTaskGroup:TestTaskGroup1];
+            } else {
+                [self startImageDownloadTaskInTaskGroup:TestTaskGroup2];
+            }
+        });
+    }
+}
+
+- (IBAction)stopNetworkRequests:(id)sender {
+    [self.sessionController cancelTasksInGroup:TestTaskGroup1];
+    [self.sessionController cancelTasksInGroup:TestTaskGroup2];
+}
+
+- (IBAction)nilSessionController:(id)sender {
+    @autoreleasepool {
+        [self.sessionController.session invalidateAndCancel];
+        self.sessionController = nil;
+    }
+}
+
+- (IBAction)printHasTasks:(id)sender {
+    NSLog(@"Has tasks %i", [self.sessionController hasTasksInGroup:TestTaskGroup1
+                                                         withState:NSURLSessionTaskStateRunning]);
+}
+
+#pragma mark - 
+
+- (void)startImageDownloadTaskInTaskGroup:(NSString *)taskGroup {
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://static.fjcdn.com/pictures/Space_03b120_799520.jpg"]];
+    NSURLSessionDataTask *task =
+    [self.sessionController dataTaskForRequest:request
+                                     taskGroup:taskGroup
+                             completionHandler: ^(NSData *data,
+                                                  NSURLResponse *response,
+                                                  BOOL cachedResponse,
+                                                  NSError *error) {
+                                 
+                                 if (error) {
+                                     NSLog(@"Error: %@", error);
+                                 }
+                                 
+                                 if (response.cou_success) {
+                                     NSLog(@"Downloaded image!");
+                                     UIImage *image = [UIImage imageWithData:data];
+                                     dispatch_async(dispatch_get_main_queue(), ^{
+                                         self.imageView.image = image;
+                                     });
+                                 }
+                                 else {
+                                     NSLog(@"Error: %li", response.cou_statusCode);
+                                 }
+                             }];
+    [task resume];
+}
+
+@end
diff --git a/App/CourierTestApp/main.m b/App/CourierTestApp/main.m
new file mode 100644
index 0000000..6d96fe8
--- /dev/null
+++ b/App/CourierTestApp/main.m
@@ -0,0 +1,16 @@
+//
+//  main.m
+//  CourierTestApp
+//
+//  Created by absmith on 3/31/15.
+//  Copyright (c) 2015 Andrew B. Smith. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+    @autoreleasepool {
+        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}
diff --git a/App/CourierTestAppTests/CourierTestAppTests.m b/App/CourierTestAppTests/CourierTestAppTests.m
new file mode 100644
index 0000000..4d3222f
--- /dev/null
+++ b/App/CourierTestAppTests/CourierTestAppTests.m
@@ -0,0 +1,40 @@
+//
+//  CourierTestAppTests.m
+//  CourierTestAppTests
+//
+//  Created by absmith on 3/31/15.
+//  Copyright (c) 2015 Andrew B. Smith. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+#import <XCTest/XCTest.h>
+
+@interface CourierTestAppTests : XCTestCase
+
+@end
+
+@implementation CourierTestAppTests
+
+- (void)setUp {
+    [super setUp];
+    // Put setup code here. This method is called before the invocation of each test method in the class.
+}
+
+- (void)tearDown {
+    // Put teardown code here. This method is called after the invocation of each test method in the class.
+    [super tearDown];
+}
+
+- (void)testExample {
+    // This is an example of a functional test case.
+    XCTAssert(YES, @"Pass");
+}
+
+- (void)testPerformanceExample {
+    // This is an example of a performance test case.
+    [self measureBlock:^{
+        // Put the code you want to measure the time of here.
+    }];
+}
+
+@end
diff --git a/App/CourierTestAppTests/Info.plist b/App/CourierTestAppTests/Info.plist
new file mode 100644
index 0000000..213cbc0
--- /dev/null
+++ b/App/CourierTestAppTests/Info.plist
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>andrewbsmith.$(PRODUCT_NAME:rfc1034identifier)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/App/Podfile b/App/Podfile
new file mode 100644
index 0000000..cb3f67b
--- /dev/null
+++ b/App/Podfile
@@ -0,0 +1,6 @@
+source 'https://github.com/CocoaPods/Specs.git'
+
+workspace 'CourierTestApp'
+xcodeproj 'CourierTestApp'
+
+pod 'Courier', :path => '../'
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..db7fc5e
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,18 @@
+# 0.1.6
+
+* Added ability to check and see if a task group has a task with a given state
+* Added some tests, fixed a couple old tests to properlly stub out the network
+
+# 0.1.5
+
+* Added hooks to start/stop reachability monitoring
+* Fixed bug in logRequests method
+
+# 0.1.4
+
+* Bug fix for race condition where you could add/remove tasks from different threads which mutated
+the task array while enumerating
+
+# 0.1.3
+
+* Forward authentication challenges to CRSessionController NSURLSessionTask delegates.
diff --git a/Courier.podspec b/Courier.podspec
new file mode 100644
index 0000000..7f790d3
--- /dev/null
+++ b/Courier.podspec
@@ -0,0 +1,21 @@
+Pod::Spec.new do |s|
+  s.name         = 'Courier'
+  s.version      = '0.1.6'
+  s.summary      = 'A lightweight network layer built on NSURLSession.'
+  s.license      = 'MIT'
+  s.author       = { 
+    'Andrew Smith' => 'drewsmits@gmail.com'
+  }
+  s.social_media_url = 'http://twitter.com/drewsmits'
+  s.requires_arc = true
+  s.platform     = :ios, '7.0'
+  s.source       = { 
+    :git => 'ssh://git@stash.nestlabs.com:7999/clients/courier.git', 
+    :tag => s.version.to_s 
+  }
+  s.homepage = 'http://github.com/Drewsmits/Courier'
+  s.source_files  = 'Courier/*.{h,m}'
+  s.public_header_files = 'Courier/*.h'
+  s.dependency 'Reachability', '3.2'
+  s.frameworks = 'UIKit', 'SystemConfiguration', 'Foundation'
+end
diff --git a/Courier.xcodeproj/project.pbxproj b/Courier.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..ecf13ae
--- /dev/null
+++ b/Courier.xcodeproj/project.pbxproj
@@ -0,0 +1,628 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		091DDCE71A7859B800F840C3 /* CRSessionControllerReachabilityTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 091DDCE61A7859B800F840C3 /* CRSessionControllerReachabilityTests.m */; };
+		09C21A4C1A65BFB50037A886 /* CRSessionControllerDelegateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 09C21A4B1A65BFB50037A886 /* CRSessionControllerDelegateTests.m */; };
+		09FFE98A1ACB73B1002A6A32 /* CRSessionDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 09FFE9891ACB73B1002A6A32 /* CRSessionDelegate.m */; };
+		345E01F400FE40740A733605 /* libPods-Courier.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3359CC05E7F1B8DF244A8D2E /* libPods-Courier.a */; };
+		8815655B18A0807D00E54D82 /* CourierLog.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8815655A18A0803400E54D82 /* CourierLog.h */; };
+		8831EB8317A205B800B6E1D7 /* NSMutableURLRequest+Courier.m in Sources */ = {isa = PBXBuildFile; fileRef = 8831EB8117A205B800B6E1D7 /* NSMutableURLRequest+Courier.m */; };
+		888360651889AC7C004322BA /* CRSessionControllerTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 888360641889AC7C004322BA /* CRSessionControllerTests.m */; };
+		888360791889F0DA004322BA /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB9DEFAF1573F9ED00E05274 /* UIKit.framework */; };
+		889B8974188477A90099C1A3 /* XCTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB487DBE17C019D400D189D3 /* XCTest.framework */; };
+		889B8975188477A90099C1A3 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB64C482144F4BCD003D766A /* Foundation.framework */; };
+		889B8976188477A90099C1A3 /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB9DEFAF1573F9ED00E05274 /* UIKit.framework */; };
+		889B897C188477A90099C1A3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 889B897A188477A90099C1A3 /* InfoPlist.strings */; };
+		889B8986188477E10099C1A3 /* NSDictionaryCourierTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 889B8985188477E10099C1A3 /* NSDictionaryCourierTests.m */; };
+		889B8987188479680099C1A3 /* libCourier.a in Frameworks */ = {isa = PBXBuildFile; fileRef = DB64C47F144F4BCD003D766A /* libCourier.a */; };
+		889B8988188479840099C1A3 /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB3A04B4160A7E4D00B833CA /* SystemConfiguration.framework */; };
+		889B898A1884A3400099C1A3 /* NSStringCourierTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 889B89891884A3400099C1A3 /* NSStringCourierTests.m */; };
+		889B89931884A9E20099C1A3 /* NSMutableURLRequestCourierTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 889B89921884A9E20099C1A3 /* NSMutableURLRequestCourierTests.m */; };
+		889B89951884B73C0099C1A3 /* CRTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = 889B89941884B73C0099C1A3 /* CRTestCase.m */; };
+		889B89981884BA7C0099C1A3 /* NSURLResponseCourierTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 889B89971884BA7C0099C1A3 /* NSURLResponseCourierTests.m */; };
+		889B8B101889ABC10099C1A3 /* Courier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DBA2A87117EF9A7B005E2D9F /* Courier.h */; };
+		889B8B111889ABC10099C1A3 /* CRSessionController.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DBA2A86917EF8D67005E2D9F /* CRSessionController.h */; };
+		889B8B121889ABC10099C1A3 /* NSDictionary+Courier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DBC30B5816E047D600C795BA /* NSDictionary+Courier.h */; };
+		889B8B131889ABC10099C1A3 /* NSMutableURLRequest+Courier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 8831EB8017A205B800B6E1D7 /* NSMutableURLRequest+Courier.h */; };
+		889B8B141889ABC10099C1A3 /* NSString+Courier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DB83D103144F9BC400D94890 /* NSString+Courier.h */; };
+		889B8B151889ABC10099C1A3 /* NSURLResponse+Courier.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = DB2FC63617D291DA00EDF9F0 /* NSURLResponse+Courier.h */; };
+		8B139EE619DE0AED00513819 /* CRAsyncTestHelpers.m in Sources */ = {isa = PBXBuildFile; fileRef = 8B139EE519DE0AED00513819 /* CRAsyncTestHelpers.m */; };
+		D17BE5CDA0931A4A6FDB1D19 /* libPods-CourierTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0ACB06113518CDDEB9B6D4D8 /* libPods-CourierTests.a */; };
+		DB2FC63917D291DA00EDF9F0 /* NSURLResponse+Courier.m in Sources */ = {isa = PBXBuildFile; fileRef = DB2FC63717D291DA00EDF9F0 /* NSURLResponse+Courier.m */; };
+		DB3A04B5160A7E4D00B833CA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB3A04B4160A7E4D00B833CA /* SystemConfiguration.framework */; };
+		DB64C483144F4BCD003D766A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DB64C482144F4BCD003D766A /* Foundation.framework */; };
+		DB83D106144F9BC400D94890 /* NSString+Courier.m in Sources */ = {isa = PBXBuildFile; fileRef = DB83D104144F9BC400D94890 /* NSString+Courier.m */; };
+		DBA2A86C17EF8D67005E2D9F /* CRSessionController.m in Sources */ = {isa = PBXBuildFile; fileRef = DBA2A86A17EF8D67005E2D9F /* CRSessionController.m */; };
+		DBC30B5B16E047D600C795BA /* NSDictionary+Courier.m in Sources */ = {isa = PBXBuildFile; fileRef = DBC30B5916E047D600C795BA /* NSDictionary+Courier.m */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		889B8980188477A90099C1A3 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = DB64C474144F4BB0003D766A /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = DB64C47E144F4BCD003D766A;
+			remoteInfo = Courier;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		889B8B0F1889AB9D0099C1A3 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "include/$(PRODUCT_NAME)";
+			dstSubfolderSpec = 16;
+			files = (
+				8815655B18A0807D00E54D82 /* CourierLog.h in CopyFiles */,
+				889B8B101889ABC10099C1A3 /* Courier.h in CopyFiles */,
+				889B8B111889ABC10099C1A3 /* CRSessionController.h in CopyFiles */,
+				889B8B121889ABC10099C1A3 /* NSDictionary+Courier.h in CopyFiles */,
+				889B8B131889ABC10099C1A3 /* NSMutableURLRequest+Courier.h in CopyFiles */,
+				889B8B141889ABC10099C1A3 /* NSString+Courier.h in CopyFiles */,
+				889B8B151889ABC10099C1A3 /* NSURLResponse+Courier.h in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		091DDCE61A7859B800F840C3 /* CRSessionControllerReachabilityTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRSessionControllerReachabilityTests.m; sourceTree = "<group>"; };
+		09C21A4B1A65BFB50037A886 /* CRSessionControllerDelegateTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRSessionControllerDelegateTests.m; sourceTree = "<group>"; };
+		09FFE9881ACB73B1002A6A32 /* CRSessionDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRSessionDelegate.h; sourceTree = "<group>"; };
+		09FFE9891ACB73B1002A6A32 /* CRSessionDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRSessionDelegate.m; sourceTree = "<group>"; };
+		0ACB06113518CDDEB9B6D4D8 /* libPods-CourierTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CourierTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		3359CC05E7F1B8DF244A8D2E /* libPods-Courier.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Courier.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+		3685C0D7C3414538BBE122EE /* Pods-CourierTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CourierTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CourierTests/Pods-CourierTests.debug.xcconfig"; sourceTree = "<group>"; };
+		8815655A18A0803400E54D82 /* CourierLog.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CourierLog.h; sourceTree = "<group>"; };
+		8831EB8017A205B800B6E1D7 /* NSMutableURLRequest+Courier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSMutableURLRequest+Courier.h"; sourceTree = "<group>"; };
+		8831EB8117A205B800B6E1D7 /* NSMutableURLRequest+Courier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSMutableURLRequest+Courier.m"; sourceTree = "<group>"; };
+		888360641889AC7C004322BA /* CRSessionControllerTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRSessionControllerTests.m; sourceTree = "<group>"; };
+		889B8973188477A80099C1A3 /* CourierTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CourierTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		889B8979188477A90099C1A3 /* CourierTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "CourierTests-Info.plist"; sourceTree = "<group>"; };
+		889B897B188477A90099C1A3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
+		889B897F188477A90099C1A3 /* CourierTests-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CourierTests-Prefix.pch"; sourceTree = "<group>"; };
+		889B8985188477E10099C1A3 /* NSDictionaryCourierTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSDictionaryCourierTests.m; sourceTree = "<group>"; };
+		889B89891884A3400099C1A3 /* NSStringCourierTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSStringCourierTests.m; sourceTree = "<group>"; };
+		889B89921884A9E20099C1A3 /* NSMutableURLRequestCourierTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSMutableURLRequestCourierTests.m; sourceTree = "<group>"; };
+		889B89941884B73C0099C1A3 /* CRTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRTestCase.m; sourceTree = "<group>"; };
+		889B89961884B7630099C1A3 /* CRTestCase.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CRTestCase.h; sourceTree = "<group>"; };
+		889B89971884BA7C0099C1A3 /* NSURLResponseCourierTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NSURLResponseCourierTests.m; sourceTree = "<group>"; };
+		8B139EE419DE0AED00513819 /* CRAsyncTestHelpers.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CRAsyncTestHelpers.h; sourceTree = "<group>"; };
+		8B139EE519DE0AED00513819 /* CRAsyncTestHelpers.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRAsyncTestHelpers.m; sourceTree = "<group>"; };
+		A4AA1841317E4D321AF5B30A /* Pods-Courier.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Courier.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Courier/Pods-Courier.debug.xcconfig"; sourceTree = "<group>"; };
+		D7C37F2CB20683FB2C4BEE07 /* Pods-CourierTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CourierTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CourierTests/Pods-CourierTests.release.xcconfig"; sourceTree = "<group>"; };
+		DB2FC63617D291DA00EDF9F0 /* NSURLResponse+Courier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURLResponse+Courier.h"; sourceTree = "<group>"; };
+		DB2FC63717D291DA00EDF9F0 /* NSURLResponse+Courier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURLResponse+Courier.m"; sourceTree = "<group>"; };
+		DB3A04B4160A7E4D00B833CA /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; };
+		DB487DBE17C019D400D189D3 /* XCTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = XCTest.framework; path = Library/Frameworks/XCTest.framework; sourceTree = DEVELOPER_DIR; };
+		DB64C47F144F4BCD003D766A /* libCourier.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCourier.a; sourceTree = BUILT_PRODUCTS_DIR; };
+		DB64C482144F4BCD003D766A /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
+		DB64C486144F4BCD003D766A /* Courier-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Courier-Prefix.pch"; sourceTree = "<group>"; };
+		DB83D103144F9BC400D94890 /* NSString+Courier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSString+Courier.h"; sourceTree = "<group>"; };
+		DB83D104144F9BC400D94890 /* NSString+Courier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSString+Courier.m"; sourceTree = "<group>"; };
+		DB9DEFAF1573F9ED00E05274 /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
+		DBA2A86917EF8D67005E2D9F /* CRSessionController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = CRSessionController.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+		DBA2A86A17EF8D67005E2D9F /* CRSessionController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CRSessionController.m; sourceTree = "<group>"; };
+		DBA2A87117EF9A7B005E2D9F /* Courier.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = Courier.h; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; };
+		DBC30B5816E047D600C795BA /* NSDictionary+Courier.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Courier.h"; sourceTree = "<group>"; };
+		DBC30B5916E047D600C795BA /* NSDictionary+Courier.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Courier.m"; sourceTree = "<group>"; };
+		EF731401BABF443A636FA578 /* Pods-Courier.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Courier.release.xcconfig"; path = "Pods/Target Support Files/Pods-Courier/Pods-Courier.release.xcconfig"; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		889B8970188477A80099C1A3 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				889B8988188479840099C1A3 /* SystemConfiguration.framework in Frameworks */,
+				889B8987188479680099C1A3 /* libCourier.a in Frameworks */,
+				889B8974188477A90099C1A3 /* XCTest.framework in Frameworks */,
+				889B8976188477A90099C1A3 /* UIKit.framework in Frameworks */,
+				889B8975188477A90099C1A3 /* Foundation.framework in Frameworks */,
+				D17BE5CDA0931A4A6FDB1D19 /* libPods-CourierTests.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DB64C47C144F4BCD003D766A /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				888360791889F0DA004322BA /* UIKit.framework in Frameworks */,
+				DB3A04B5160A7E4D00B833CA /* SystemConfiguration.framework in Frameworks */,
+				DB64C483144F4BCD003D766A /* Foundation.framework in Frameworks */,
+				345E01F400FE40740A733605 /* libPods-Courier.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		887304FA1940D6A400DE799B /* Helpers */ = {
+			isa = PBXGroup;
+			children = (
+				8B139EE419DE0AED00513819 /* CRAsyncTestHelpers.h */,
+				8B139EE519DE0AED00513819 /* CRAsyncTestHelpers.m */,
+			);
+			name = Helpers;
+			sourceTree = "<group>";
+		};
+		889B8977188477A90099C1A3 /* CourierTests */ = {
+			isa = PBXGroup;
+			children = (
+				09C21A4B1A65BFB50037A886 /* CRSessionControllerDelegateTests.m */,
+				091DDCE61A7859B800F840C3 /* CRSessionControllerReachabilityTests.m */,
+				888360641889AC7C004322BA /* CRSessionControllerTests.m */,
+				889B89961884B7630099C1A3 /* CRTestCase.h */,
+				889B89941884B73C0099C1A3 /* CRTestCase.m */,
+				887304FA1940D6A400DE799B /* Helpers */,
+				889B8985188477E10099C1A3 /* NSDictionaryCourierTests.m */,
+				889B89921884A9E20099C1A3 /* NSMutableURLRequestCourierTests.m */,
+				889B89891884A3400099C1A3 /* NSStringCourierTests.m */,
+				889B89971884BA7C0099C1A3 /* NSURLResponseCourierTests.m */,
+				889B8978188477A90099C1A3 /* Supporting Files */,
+			);
+			path = CourierTests;
+			sourceTree = "<group>";
+		};
+		889B8978188477A90099C1A3 /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				889B8979188477A90099C1A3 /* CourierTests-Info.plist */,
+				889B897A188477A90099C1A3 /* InfoPlist.strings */,
+				889B897F188477A90099C1A3 /* CourierTests-Prefix.pch */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+		90F8E56AB6E6B283A4672882 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				A4AA1841317E4D321AF5B30A /* Pods-Courier.debug.xcconfig */,
+				EF731401BABF443A636FA578 /* Pods-Courier.release.xcconfig */,
+				3685C0D7C3414538BBE122EE /* Pods-CourierTests.debug.xcconfig */,
+				D7C37F2CB20683FB2C4BEE07 /* Pods-CourierTests.release.xcconfig */,
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+		DB64C472144F4BB0003D766A = {
+			isa = PBXGroup;
+			children = (
+				DB64C484144F4BCD003D766A /* Courier */,
+				889B8977188477A90099C1A3 /* CourierTests */,
+				DB64C481144F4BCD003D766A /* Frameworks */,
+				DB64C480144F4BCD003D766A /* Products */,
+				90F8E56AB6E6B283A4672882 /* Pods */,
+			);
+			sourceTree = "<group>";
+		};
+		DB64C480144F4BCD003D766A /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				DB64C47F144F4BCD003D766A /* libCourier.a */,
+				889B8973188477A80099C1A3 /* CourierTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		DB64C481144F4BCD003D766A /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				DB487DBE17C019D400D189D3 /* XCTest.framework */,
+				DB3A04B4160A7E4D00B833CA /* SystemConfiguration.framework */,
+				DB9DEFAF1573F9ED00E05274 /* UIKit.framework */,
+				DB64C482144F4BCD003D766A /* Foundation.framework */,
+				3359CC05E7F1B8DF244A8D2E /* libPods-Courier.a */,
+				0ACB06113518CDDEB9B6D4D8 /* libPods-CourierTests.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		DB64C484144F4BCD003D766A /* Courier */ = {
+			isa = PBXGroup;
+			children = (
+				DBA2A87117EF9A7B005E2D9F /* Courier.h */,
+				8815655A18A0803400E54D82 /* CourierLog.h */,
+				DBA2A86917EF8D67005E2D9F /* CRSessionController.h */,
+				DBA2A86A17EF8D67005E2D9F /* CRSessionController.m */,
+				09FFE9881ACB73B1002A6A32 /* CRSessionDelegate.h */,
+				09FFE9891ACB73B1002A6A32 /* CRSessionDelegate.m */,
+				DBC30B5816E047D600C795BA /* NSDictionary+Courier.h */,
+				DBC30B5916E047D600C795BA /* NSDictionary+Courier.m */,
+				8831EB8017A205B800B6E1D7 /* NSMutableURLRequest+Courier.h */,
+				8831EB8117A205B800B6E1D7 /* NSMutableURLRequest+Courier.m */,
+				DB83D103144F9BC400D94890 /* NSString+Courier.h */,
+				DB83D104144F9BC400D94890 /* NSString+Courier.m */,
+				DB2FC63617D291DA00EDF9F0 /* NSURLResponse+Courier.h */,
+				DB2FC63717D291DA00EDF9F0 /* NSURLResponse+Courier.m */,
+				DB64C485144F4BCD003D766A /* Supporting Files */,
+			);
+			path = Courier;
+			sourceTree = "<group>";
+		};
+		DB64C485144F4BCD003D766A /* Supporting Files */ = {
+			isa = PBXGroup;
+			children = (
+				DB64C486144F4BCD003D766A /* Courier-Prefix.pch */,
+			);
+			name = "Supporting Files";
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		889B8972188477A80099C1A3 /* CourierTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 889B8982188477A90099C1A3 /* Build configuration list for PBXNativeTarget "CourierTests" */;
+			buildPhases = (
+				7475F0FD68E8A304B099446E /* Check Pods Manifest.lock */,
+				889B896F188477A80099C1A3 /* Sources */,
+				889B8970188477A80099C1A3 /* Frameworks */,
+				889B8971188477A80099C1A3 /* Resources */,
+				41B41366D6ED7D866DB0F09C /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				889B8981188477A90099C1A3 /* PBXTargetDependency */,
+			);
+			name = CourierTests;
+			productName = CourierTests;
+			productReference = 889B8973188477A80099C1A3 /* CourierTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		DB64C47E144F4BCD003D766A /* Courier */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = DB64C4A1144F4BCD003D766A /* Build configuration list for PBXNativeTarget "Courier" */;
+			buildPhases = (
+				72927E23A067C335828E5663 /* Check Pods Manifest.lock */,
+				DB64C47B144F4BCD003D766A /* Sources */,
+				DB64C47C144F4BCD003D766A /* Frameworks */,
+				889B8B0F1889AB9D0099C1A3 /* CopyFiles */,
+				0A4BAF728059F091E607BA78 /* Copy Pods Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Courier;
+			productName = Courier;
+			productReference = DB64C47F144F4BCD003D766A /* libCourier.a */;
+			productType = "com.apple.product-type.library.static";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		DB64C474144F4BB0003D766A /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0510;
+				ORGANIZATIONNAME = "Andrew B. Smith";
+				TargetAttributes = {
+					889B8972188477A80099C1A3 = {
+						TestTargetID = DB64C47E144F4BCD003D766A;
+					};
+				};
+			};
+			buildConfigurationList = DB64C477144F4BB0003D766A /* Build configuration list for PBXProject "Courier" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = DB64C472144F4BB0003D766A;
+			productRefGroup = DB64C480144F4BCD003D766A /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				DB64C47E144F4BCD003D766A /* Courier */,
+				889B8972188477A80099C1A3 /* CourierTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		889B8971188477A80099C1A3 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				889B897C188477A90099C1A3 /* InfoPlist.strings in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		0A4BAF728059F091E607BA78 /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Courier/Pods-Courier-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		41B41366D6ED7D866DB0F09C /* Copy Pods Resources */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Copy Pods Resources";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-CourierTests/Pods-CourierTests-resources.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		72927E23A067C335828E5663 /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+		7475F0FD68E8A304B099446E /* Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Check Pods Manifest.lock";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [[ $? != 0 ]] ; then\n    cat << EOM\nerror: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\nEOM\n    exit 1\nfi\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		889B896F188477A80099C1A3 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				889B89981884BA7C0099C1A3 /* NSURLResponseCourierTests.m in Sources */,
+				888360651889AC7C004322BA /* CRSessionControllerTests.m in Sources */,
+				09C21A4C1A65BFB50037A886 /* CRSessionControllerDelegateTests.m in Sources */,
+				889B89951884B73C0099C1A3 /* CRTestCase.m in Sources */,
+				889B89931884A9E20099C1A3 /* NSMutableURLRequestCourierTests.m in Sources */,
+				8B139EE619DE0AED00513819 /* CRAsyncTestHelpers.m in Sources */,
+				889B8986188477E10099C1A3 /* NSDictionaryCourierTests.m in Sources */,
+				889B898A1884A3400099C1A3 /* NSStringCourierTests.m in Sources */,
+				091DDCE71A7859B800F840C3 /* CRSessionControllerReachabilityTests.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		DB64C47B144F4BCD003D766A /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				DBA2A86C17EF8D67005E2D9F /* CRSessionController.m in Sources */,
+				DB83D106144F9BC400D94890 /* NSString+Courier.m in Sources */,
+				09FFE98A1ACB73B1002A6A32 /* CRSessionDelegate.m in Sources */,
+				DB2FC63917D291DA00EDF9F0 /* NSURLResponse+Courier.m in Sources */,
+				DBC30B5B16E047D600C795BA /* NSDictionary+Courier.m in Sources */,
+				8831EB8317A205B800B6E1D7 /* NSMutableURLRequest+Courier.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		889B8981188477A90099C1A3 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = DB64C47E144F4BCD003D766A /* Courier */;
+			targetProxy = 889B8980188477A90099C1A3 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		889B897A188477A90099C1A3 /* InfoPlist.strings */ = {
+			isa = PBXVariantGroup;
+			children = (
+				889B897B188477A90099C1A3 /* en */,
+			);
+			name = InfoPlist.strings;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		889B8983188477A90099C1A3 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 3685C0D7C3414538BBE122EE /* Pods-CourierTests.debug.xcconfig */;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+				GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "CourierTests/CourierTests-Prefix.pch";
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				INFOPLIST_FILE = "CourierTests/CourierTests-Info.plist";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				WRAPPER_EXTENSION = xctest;
+			};
+			name = Debug;
+		};
+		889B8984188477A90099C1A3 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = D7C37F2CB20683FB2C4BEE07 /* Pods-CourierTests.release.xcconfig */;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = YES;
+				ENABLE_NS_ASSERTIONS = NO;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "CourierTests/CourierTests-Prefix.pch";
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				INFOPLIST_FILE = "CourierTests/CourierTests-Info.plist";
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				VALIDATE_PRODUCT = YES;
+				WRAPPER_EXTENSION = xctest;
+			};
+			name = Release;
+		};
+		DB64C479144F4BB0003D766A /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+				ONLY_ACTIVE_ARCH = YES;
+				OTHER_LDFLAGS = "-ObjC";
+				SCAN_ALL_SOURCE_FILES_FOR_INCLUDES = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		DB64C47A144F4BB0003D766A /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				IPHONEOS_DEPLOYMENT_TARGET = 7.0;
+				OTHER_LDFLAGS = "-ObjC";
+				SCAN_ALL_SOURCE_FILES_FOR_INCLUDES = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Release;
+		};
+		DB64C4A2144F4BCD003D766A /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = A4AA1841317E4D321AF5B30A /* Pods-Courier.debug.xcconfig */;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_ARC = YES;
+				COPY_PHASE_STRIP = NO;
+				DSTROOT = /tmp/Courier.dst;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_GENERATE_TEST_COVERAGE_FILES = YES;
+				GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "Courier/Courier-Prefix.pch";
+				GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+			};
+			name = Debug;
+		};
+		DB64C4A3144F4BCD003D766A /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = EF731401BABF443A636FA578 /* Pods-Courier.release.xcconfig */;
+			buildSettings = {
+				CLANG_ENABLE_OBJC_ARC = YES;
+				COPY_PHASE_STRIP = YES;
+				DSTROOT = /tmp/Courier.dst;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_PRECOMPILE_PREFIX_HEADER = YES;
+				GCC_PREFIX_HEADER = "Courier/Courier-Prefix.pch";
+				GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+				GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = iphoneos;
+				SKIP_INSTALL = YES;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		889B8982188477A90099C1A3 /* Build configuration list for PBXNativeTarget "CourierTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				889B8983188477A90099C1A3 /* Debug */,
+				889B8984188477A90099C1A3 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		DB64C477144F4BB0003D766A /* Build configuration list for PBXProject "Courier" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				DB64C479144F4BB0003D766A /* Debug */,
+				DB64C47A144F4BB0003D766A /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		DB64C4A1144F4BCD003D766A /* Build configuration list for PBXNativeTarget "Courier" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				DB64C4A2144F4BCD003D766A /* Debug */,
+				DB64C4A3144F4BCD003D766A /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = DB64C474144F4BB0003D766A /* Project object */;
+}
diff --git a/Courier.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Courier.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..8f4d83d
--- /dev/null
+++ b/Courier.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:Courier.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/Courier.xcodeproj/project.xcworkspace/xcshareddata/Courier.xccheckout b/Courier.xcodeproj/project.xcworkspace/xcshareddata/Courier.xccheckout
new file mode 100644
index 0000000..fc6e44f
--- /dev/null
+++ b/Courier.xcodeproj/project.xcworkspace/xcshareddata/Courier.xccheckout
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDESourceControlProjectFavoriteDictionaryKey</key>
+	<false/>
+	<key>IDESourceControlProjectIdentifier</key>
+	<string>874E602F-EC37-4A6A-BFEE-57E47ECB0BC5</string>
+	<key>IDESourceControlProjectName</key>
+	<string>Courier</string>
+	<key>IDESourceControlProjectOriginsDictionary</key>
+	<dict>
+		<key>42936AFBAF2C56BADE64D01CF9417A41B06B1AE2</key>
+		<string>ssh://stash.nestlabs.com:7999/clients/courier.git</string>
+	</dict>
+	<key>IDESourceControlProjectPath</key>
+	<string>Courier.xcodeproj</string>
+	<key>IDESourceControlProjectRelativeInstallPathDictionary</key>
+	<dict>
+		<key>42936AFBAF2C56BADE64D01CF9417A41B06B1AE2</key>
+		<string>../..</string>
+	</dict>
+	<key>IDESourceControlProjectURL</key>
+	<string>ssh://stash.nestlabs.com:7999/clients/courier.git</string>
+	<key>IDESourceControlProjectVersion</key>
+	<integer>111</integer>
+	<key>IDESourceControlProjectWCCIdentifier</key>
+	<string>42936AFBAF2C56BADE64D01CF9417A41B06B1AE2</string>
+	<key>IDESourceControlProjectWCConfigurations</key>
+	<array>
+		<dict>
+			<key>IDESourceControlRepositoryExtensionIdentifierKey</key>
+			<string>public.vcs.git</string>
+			<key>IDESourceControlWCCIdentifierKey</key>
+			<string>42936AFBAF2C56BADE64D01CF9417A41B06B1AE2</string>
+			<key>IDESourceControlWCCName</key>
+			<string>Courier</string>
+		</dict>
+	</array>
+</dict>
+</plist>
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-Prefix.pch b/Courier/Courier-Prefix.pch
new file mode 100644
index 0000000..b4fc214
--- /dev/null
+++ b/Courier/Courier-Prefix.pch
@@ -0,0 +1,8 @@
+//
+// Prefix header for all source files of the 'Courier' target in the 'Courier' project
+//
+
+#ifdef __OBJC__
+    #import <Foundation/Foundation.h>
+    #import <UIKit/UIKit.h>
+#endif
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/CourierTests/CRAsyncTestHelpers.h b/CourierTests/CRAsyncTestHelpers.h
new file mode 100644
index 0000000..29e9edc
--- /dev/null
+++ b/CourierTests/CRAsyncTestHelpers.h
@@ -0,0 +1,15 @@
+//
+//  CRAsyncTestHelpers.h
+//  Courier
+//
+//  Created by Andrew Smith on 10/2/14.
+//  Copyright (c) 2014 Andrew B. Smith. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@interface CRAsyncTestHelpers : NSObject
+
+void runInMainLoopUntilDone(void(^block)(BOOL *done));
+
+@end
diff --git a/CourierTests/CRAsyncTestHelpers.m b/CourierTests/CRAsyncTestHelpers.m
new file mode 100644
index 0000000..35b081d
--- /dev/null
+++ b/CourierTests/CRAsyncTestHelpers.m
@@ -0,0 +1,22 @@
+//
+//  CRAsyncTestHelpers.m
+//  Courier
+//
+//  Created by Andrew Smith on 10/2/14.
+//  Copyright (c) 2014 Andrew B. Smith. All rights reserved.
+//
+
+#import "CRAsyncTestHelpers.h"
+
+@implementation CRAsyncTestHelpers
+
+void runInMainLoopUntilDone(void(^block)(BOOL *done)) {
+    __block BOOL done = NO;
+    block(&done);
+    while (!done) {
+        [[NSRunLoop mainRunLoop] runUntilDate:
+         [NSDate dateWithTimeIntervalSinceNow:.1]];
+    }
+}
+
+@end
diff --git a/CourierTests/CRSessionControllerDelegateTests.m b/CourierTests/CRSessionControllerDelegateTests.m
new file mode 100644
index 0000000..82de922
--- /dev/null
+++ b/CourierTests/CRSessionControllerDelegateTests.m
@@ -0,0 +1,203 @@
+//
+//  CRSessionControllerDelegateTests.m
+//  Courier
+//
+//  Created by absmith on 1/13/15.
+//  Copyright (c) 2014 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 <UIKit/UIKit.h>
+#import <XCTest/XCTest.h>
+#import "CRTestCase.h"
+
+#import "CRSessionController.h"
+#import "CRAsyncTestHelpers.h"
+
+#import <OHHTTPStubs/OHHTTPStubs.h>
+#import <OCMock/OCMock.h>
+
+static NSString * const kTestRedirectURL = @"http://www.redirect.com";
+
+@interface CRSessionController (Private)
+
+@property (nonatomic, readonly) NSOperationQueue *serialQueue;
+
+- (id <NSURLSessionTaskDelegate>)NSURLSessionTaskDelegateForTask:(NSURLSessionTask *)task;
+
+- (void)addTask:(NSURLSessionTask *)task
+      withToken:(NSString *)token
+        toGroup:(NSString *)group;
+
+- (void)removeTaskWithToken:(NSString *)token;
+
+@end
+
+@interface CRSessionControllerDelegateTests : CRTestCase
+@property (nonatomic, strong) CRSessionController *sessionController;
+@end
+
+@implementation CRSessionControllerDelegateTests
+
+- (void)setUp
+{
+    [super setUp];
+    
+    _sessionController = [CRSessionController sessionControllerWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
+    
+    [OHHTTPStubs removeAllStubs];
+    
+    [[NSURLSessionConfiguration ephemeralSessionConfiguration].URLCache removeAllCachedResponses];
+}
+
+- (void)tearDown
+{
+    [OHHTTPStubs removeAllStubs];
+    [super tearDown];
+}
+
+- (void)testAddDelegate
+{
+    NSURLSessionDataTask *task = OCMClassMock([NSURLSessionDataTask class]);
+    id mockDelegate = OCMProtocolMock(@protocol(NSURLSessionTaskDelegate));
+    [_sessionController addNSURLSessionTaskDelegate:mockDelegate
+                                            forTask:task];
+
+    id shouldBeDelegate = [_sessionController NSURLSessionTaskDelegateForTask:task];
+    
+    XCTAssertEqualObjects(shouldBeDelegate,
+                          mockDelegate,
+                          @"Session controller should return the correct delegate");
+}
+
+- (void)testRemoveDelegate
+{
+    NSURLSessionDataTask *task = OCMClassMock([NSURLSessionDataTask class]);
+    id mockDelegate = OCMProtocolMock(@protocol(NSURLSessionTaskDelegate));
+    [_sessionController addNSURLSessionTaskDelegate:mockDelegate
+                                            forTask:task];
+
+    [_sessionController removeNSURLSessionTaskDelegate:mockDelegate];
+    id delegate = [_sessionController NSURLSessionTaskDelegateForTask:task];
+    
+    XCTAssertNil(delegate);
+}
+
+- (void)testRemoveTaskWithDelegate
+{
+    // Add task first
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:@"blah"
+                        toGroup:@"blah"];
+    
+    id mockDelegate = OCMProtocolMock(@protocol(NSURLSessionTaskDelegate));
+    [_sessionController addNSURLSessionTaskDelegate:mockDelegate
+                                            forTask:task];
+
+    [_sessionController removeTaskWithToken:@"blah"];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    id delegate = [_sessionController NSURLSessionTaskDelegateForTask:task];
+    
+    XCTAssertNil(delegate);
+}
+
+- (void)testRedirectResponseDelegate
+{
+    //
+    // Stub redirect responses
+    //
+    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
+        return [request.URL.absoluteString isEqualToString:kTestRedirectURL];
+    } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
+        return [OHHTTPStubsResponse responseWithData:[@"dummyData" dataUsingEncoding:NSUTF8StringEncoding]
+                                          statusCode:301
+                                             headers:@{@"Location" : @"http://www.nest.com"}];
+    }];
+    
+    XCTestExpectation *expectation = [self expectationWithDescription:@"taskFinished"];
+    
+    NSURLRequest *redirectRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:kTestRedirectURL]];
+    NSURLSessionDataTask *task = [_sessionController dataTaskForRequest:redirectRequest
+                                                      completionHandler:^(NSData *data,
+                                                                          NSURLResponse *response,
+                                                                          BOOL cachedResponse,
+                                                                          NSError *error) {
+                                                          [expectation fulfill];
+                                                      }];
+
+    //
+    // Add delegate and expect the redirect is invoked
+    //
+    id mockDelegate = OCMProtocolMock(@protocol(NSURLSessionTaskDelegate));
+
+    // forward redirect to self. This lets the redirect complete.
+    OCMStub([mockDelegate URLSession:OCMOCK_ANY
+                                task:OCMOCK_ANY
+          willPerformHTTPRedirection:OCMOCK_ANY
+                          newRequest:OCMOCK_ANY
+                   completionHandler:OCMOCK_ANY]).andCall(self,
+                                                          @selector(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:));
+
+    // forward auth challenge to self. This lets the redirect complete.
+    OCMStub([mockDelegate URLSession:OCMOCK_ANY
+                                task:OCMOCK_ANY
+                 didReceiveChallenge:OCMOCK_ANY
+                   completionHandler:OCMOCK_ANY]).andCall(self,
+                                                          @selector(URLSession:task:didReceiveChallenge:completionHandler:));
+
+    [_sessionController addNSURLSessionTaskDelegate:mockDelegate
+                                            forTask:task];
+    [task resume];
+
+    [self waitForExpectationsWithTimeout:1.0
+                                 handler:nil];
+
+    //
+    // Verify that the delegate gets called.
+    //
+    OCMVerify([mockDelegate URLSession:OCMOCK_ANY
+                                  task:OCMOCK_ANY
+            willPerformHTTPRedirection:OCMOCK_ANY
+                            newRequest:OCMOCK_ANY
+                     completionHandler:OCMOCK_ANY]);
+}
+
+- (void)URLSession:(NSURLSession *)session
+              task:(NSURLSessionTask *)task
+willPerformHTTPRedirection:(NSHTTPURLResponse *)response
+        newRequest:(NSURLRequest *)request
+ completionHandler:(void (^)(NSURLRequest *))completionHandler
+{
+    if (completionHandler) completionHandler(request);
+}
+
+- (void)URLSession:(NSURLSession *)session
+              task:(NSURLSessionTask *)task
+didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
+ completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential *))completionHandler
+{
+    if (completionHandler) {
+        completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, nil);
+    }
+    
+}
+
+@end
diff --git a/CourierTests/CRSessionControllerReachabilityTests.m b/CourierTests/CRSessionControllerReachabilityTests.m
new file mode 100644
index 0000000..59e75d9
--- /dev/null
+++ b/CourierTests/CRSessionControllerReachabilityTests.m
@@ -0,0 +1,97 @@
+//
+//  CRSessionControllerReachabilityTests.m
+//  Courier
+//
+//  Created by absmith on 1/27/15.
+//  Copyright (c) 2014 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 <UIKit/UIKit.h>
+#import <XCTest/XCTest.h>
+#import <OCMock/OCMock.h>
+#import "CRTestCase.h"
+
+#import "CRSessionController.h"
+
+@interface CRSessionController (Private)
+
+@property (nonatomic, strong) Reachability *reachabilityObject;
+
+@end
+
+@interface CRSessionControllerReachabilityTests : CRTestCase
+
+@property (nonatomic, strong) CRSessionController *sessionController;
+
+@end
+
+@implementation CRSessionControllerReachabilityTests
+
+- (void)setUp {
+    [super setUp];
+    _sessionController = [CRSessionController sessionControllerWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
+}
+
+- (void)tearDown {
+    [super tearDown];
+}
+
+- (void)testIsNotReachable {
+    id mockReachability = OCMPartialMock(_sessionController.reachabilityObject);
+    OCMStub([mockReachability isReachable]).andReturn(NO);
+    _sessionController.reachabilityObject = mockReachability;
+    XCTAssertEqual(_sessionController.isInternetReachable,
+                   NO,
+                   @"CRSessionController report not reachable");
+}
+
+- (void)testIsReachable {
+    id mockReachability = OCMPartialMock(_sessionController.reachabilityObject);
+    OCMStub([mockReachability isReachable]).andReturn(YES);
+    _sessionController.reachabilityObject = mockReachability;
+    XCTAssertEqual(_sessionController.isInternetReachable,
+                   YES,
+                   @"CRSessionController report not reachable");
+}
+
+- (void)testReachabilityChangedReachable {
+    __block BOOL blockRan = NO;
+    [_sessionController setReachabilityStatusChangeBlock:^(NetworkStatus status) {
+        blockRan = YES;
+    }];
+    
+    _sessionController.reachabilityObject.reachableBlock(nil);
+    
+    XCTAssertTrue(blockRan);
+}
+
+- (void)testReachabilityChangedUnreachable {
+    __block BOOL blockRan = NO;
+    [_sessionController setReachabilityStatusChangeBlock:^(NetworkStatus status) {
+        blockRan = YES;
+    }];
+    
+    _sessionController.reachabilityObject.unreachableBlock(nil);
+    
+    XCTAssertTrue(blockRan);
+}
+
+@end
diff --git a/CourierTests/CRSessionControllerTests.m b/CourierTests/CRSessionControllerTests.m
new file mode 100644
index 0000000..a7078f6
--- /dev/null
+++ b/CourierTests/CRSessionControllerTests.m
@@ -0,0 +1,595 @@
+//
+//  CRSessionControllerTests.m
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 "CRTestCase.h"
+
+#import "CRSessionController.h"
+#import "CRAsyncTestHelpers.h"
+
+#import <OHHTTPStubs/OHHTTPStubs.h>
+
+#define kTestGroupName @"kBurritoCrew"
+#define kTestGroup2Name @"kTacoCrew"
+
+#define kTestTaskToken @"aToken"
+#define kTestTask2Token @"bToken"
+
+@interface CRSessionController (UnitTests)
+
+@property (nonatomic, readonly) NSMutableDictionary *groups;
+
+@property (nonatomic, readonly) NSMutableDictionary *tasksByToken;
+
+@property (nonatomic, readonly) NSMutableDictionary *tasksByIdentifier;
+
+@property (nonatomic, readonly) NSOperationQueue *serialQueue;
+
+- (void)addTask:(NSURLSessionTask *)task
+      withToken:(NSString *)token
+        toGroup:(NSString *)group;
+
+- (void)removeTaskWithToken:(NSString *)token;
+
+- (void)logResponse:(NSURLResponse *)response
+               data:(NSData *)data
+              error:(NSError *)error;
+
+@end
+
+@interface CRSessionControllerTests : CRTestCase
+
+@property (nonatomic, strong) CRSessionController *sessionController;
+
+@end
+
+@implementation CRSessionControllerTests
+
+- (void)setUp
+{
+    [super setUp];
+
+    _sessionController = [CRSessionController sessionControllerWithConfiguration:[NSURLSessionConfiguration ephemeralSessionConfiguration]];
+    
+    [OHHTTPStubs removeAllStubs];
+    
+    [[NSURLSessionConfiguration ephemeralSessionConfiguration].URLCache removeAllCachedResponses];
+}
+
+- (void)tearDown
+{
+    [OHHTTPStubs removeAllStubs];
+    [super tearDown];
+}
+
+- (NSURLSessionTask *)googleTask
+{
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];
+    NSURLSessionTask *task = [_sessionController dataTaskForRequest:request completionHandler:nil];
+    return task;
+}
+
+#pragma mark - Config
+
+- (void)testSessionConfig
+{
+    XCTAssertNotNil(_sessionController.configuration,
+                    @"Should have a non nil configuration");
+}
+
+#pragma mark -
+
+- (void)testDataTaskForGroup
+{
+    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
+        return YES;
+    } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
+        return [OHHTTPStubsResponse responseWithData:[@"dummyData" dataUsingEncoding:NSUTF8StringEncoding]
+                                          statusCode:301
+                                             headers:@{@"Location" : @"http://www.nest.com"}];
+    }];
+    
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];
+
+    XCTestExpectation *expectation = [self expectationWithDescription:@"task finished"];
+    NSURLSessionDataTask *task = [_sessionController dataTaskForRequest:request
+                                                              taskGroup:kTestGroupName
+                                                      completionHandler:^(NSData *data,
+                                                                          NSURLResponse *response,
+                                                                          BOOL cachedResponse,
+                                                                          NSError *error) {
+                                                          [expectation fulfill];
+                                                    }];
+    
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    // check group
+    NSArray *tasks = [_sessionController.groups valueForKey:kTestGroupName];
+    XCTAssertEqualObjects([tasks firstObject],
+                          task,
+                          @"Should be the same task");
+    
+    // Check tasks
+    XCTAssertEqual(_sessionController.tasksByToken.count,
+                   1U,
+                   @"Should have one task");
+    
+    id key = [[_sessionController.tasksByToken allKeys] firstObject];
+    NSDictionary *object = [_sessionController.tasksByToken objectForKey:key];
+    XCTAssertEqualObjects(object[@"group"],
+                          kTestGroupName,
+                          @"Should have correc group name");
+    XCTAssertEqualObjects(object[@"task"],
+                          task,
+                          @"Should be the same task");
+    
+    [task resume];
+    
+    //
+    // Wait until finished
+    //
+    [self waitForExpectationsWithTimeout:1.0 handler:nil];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertEqual(_sessionController.groups.count,
+                   0U,
+                   @"Should have no task groups after completion");
+    
+    XCTAssertEqual(_sessionController.tasksByToken.count,
+                   0U,
+                   @"Should have no tasks after completion");
+    
+    XCTAssertEqual(_sessionController.tasksByIdentifier.count,
+                   0U,
+                   @"Should have no tasks after completion");
+}
+
+#pragma mark - Task Management
+
+- (void)testAddTask
+{
+    NSArray *tasks = [_sessionController.groups valueForKey:kTestGroupName];
+    XCTAssertNil(tasks, @"Should not have a task array for group");
+    
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    tasks = [_sessionController.groups valueForKey:kTestGroupName];
+    
+    XCTAssertNotNil(tasks, @"Should have a task array for group");
+    XCTAssertEqual(tasks.count, 1U, @"Should have one task for group");
+    XCTAssertEqualObjects([tasks firstObject], task, @"Should be the same task");
+    
+    XCTAssertNotNil(_sessionController.tasksByToken, @"Should have a tasks by token dictionary");
+    XCTAssertEqual(_sessionController.tasksByToken.count, 1U, @"Should have one task for group");
+
+    XCTAssertNotNil(_sessionController.tasksByIdentifier, @"Should have a tasks by token dictionary");
+    XCTAssertEqual(_sessionController.tasksByIdentifier.count, 1U, @"Should have one task for group");
+    XCTAssertNotNil([_sessionController.tasksByIdentifier objectForKey:@(task.taskIdentifier)], @"Should have a task for task identifier");
+}
+
+- (void)testAddNilTask
+{
+    NSURLSessionTask *task;
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    NSArray *tasks = [_sessionController.groups valueForKey:kTestGroupName];
+    XCTAssertNil(tasks, @"Should not have a task array for group");
+    XCTAssertEqual(_sessionController.tasksByToken.count, 0U, @"Should have one task for group");
+    XCTAssertEqual(_sessionController.tasksByIdentifier.count, 0U, @"Should have one task for group");
+}
+
+- (void)testAddTaskToNilGroup
+{
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:nil];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertEqual(_sessionController.groups.count,
+                   1U,
+                   @"Should have one task group");
+    
+    NSArray *tasks = [_sessionController.groups valueForKey:@"kCRSessionControllerGenericTaskGroup"];
+   
+    XCTAssertNotNil(tasks,
+                    @"Should have a generic task group");
+    XCTAssertEqual(_sessionController.tasksByToken.count, 1U, @"Should have one task for group");
+    XCTAssertEqual(_sessionController.tasksByIdentifier.count, 1U, @"Should have one task for group");
+}
+
+- (void)testAddTaskToEmptyStringGroup
+{
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:@""];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertEqual(_sessionController.groups.count,
+                   1U,
+                   @"Should have one task group");
+    
+    NSArray *tasks = [_sessionController.groups valueForKey:@"kCRSessionControllerGenericTaskGroup"];
+    XCTAssertNotNil(tasks,
+                    @"Should not have a task array for group");
+}
+
+- (void)testRemoveTask
+{
+    // Add task first
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+
+    // Remove task
+    [_sessionController removeTaskWithToken:kTestTaskToken];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertEqual(_sessionController.groups.count,
+                   0U,
+                   @"Should not have task group for tasks when empty");
+    
+    XCTAssertEqual(_sessionController.tasksByToken.count,
+                   0U,
+                   @"Should not have any tasks by token when empty");
+    
+    XCTAssertEqual(_sessionController.tasksByIdentifier.count,
+                   0U,
+                   @"Should not have any tasks by identifier when empty");
+}
+
+- (void)testRemoveTaskFromNilGroup
+{
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:nil];
+
+    // Remove task
+    [_sessionController removeTaskWithToken:kTestTaskToken];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertEqual(_sessionController.groups.count,
+                   0U,
+                   @"Should not have task group for tasks when empty");
+}
+
+- (void)testTaskWithIdentifier
+{
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:nil];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    NSURLSessionTask *shouldBeTask = [_sessionController taskWithIdentifier:task.taskIdentifier];
+    XCTAssertEqualObjects(shouldBeTask, task, @"Tasks should be the same");
+    
+    [_sessionController removeTaskWithToken:kTestTaskToken];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertNil([_sessionController taskWithIdentifier:task.taskIdentifier], @"Should not have a task with task identifier");
+}
+
+- (void)testHasTaskWithIdentifier
+{
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:nil];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertTrue([_sessionController hasTaskWithIdentifier:task.taskIdentifier], @"Should have task with identifier");
+    
+    [_sessionController removeTaskWithToken:kTestTaskToken];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertFalse([_sessionController hasTaskWithIdentifier:task.taskIdentifier], @"Should not have task with identifier");
+}
+
+- (void)testHasTaskInGroupWithState
+{
+    NSURLSessionTask *task = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertTrue([_sessionController hasTasksInGroup:kTestGroupName
+                                            withState:NSURLSessionTaskStateRunning],
+                  @"Should have task in group with running state");
+    
+    [_sessionController removeTaskWithToken:kTestTaskToken];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertFalse([_sessionController hasTasksInGroup:kTestGroupName
+                                             withState:NSURLSessionTaskStateRunning],
+                  @"Should not have task in group with running state");
+}
+
+- (void)testEndToEndHasTaskInGroupWithState
+{
+    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
+        return YES;
+    } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
+        return [OHHTTPStubsResponse responseWithData:[@"dummyData" dataUsingEncoding:NSUTF8StringEncoding]
+                                          statusCode:301
+                                             headers:@{@"Location" : @"http://www.nest.com"}];
+    }];
+    
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://www.google.com"]];
+    
+    XCTestExpectation *expectation = [self expectationWithDescription:@"task finished"];
+    NSURLSessionDataTask *task = [_sessionController dataTaskForRequest:request
+                                                              taskGroup:kTestGroupName
+                                                      completionHandler:^(NSData *data,
+                                                                          NSURLResponse *response,
+                                                                          BOOL cachedResponse,
+                                                                          NSError *error) {
+                                                          [expectation fulfill];
+                                                      }];
+    
+    XCTAssertTrue([_sessionController hasTasksInGroup:kTestGroupName
+                                            withState:NSURLSessionTaskStateSuspended],
+                  @"Should have task in group with running state");
+    
+    [task resume];
+    
+    XCTAssertTrue([_sessionController hasTasksInGroup:kTestGroupName
+                                            withState:NSURLSessionTaskStateRunning],
+                  @"Should have task in group with running state");
+    
+    XCTAssertFalse([_sessionController hasTasksInGroup:kTestGroupName
+                                            withState:NSURLSessionTaskStateSuspended],
+                  @"Should have task in group with running state");
+    
+    //
+    // Wait until finished
+    //
+    [self waitForExpectationsWithTimeout:1.0 handler:nil];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertFalse([_sessionController hasTasksInGroup:kTestGroupName
+                                             withState:NSURLSessionTaskStateSuspended],
+                   @"Should have task in group with running state");
+
+    XCTAssertFalse([_sessionController hasTasksInGroup:kTestGroupName
+                                             withState:NSURLSessionTaskStateRunning],
+                   @"Should have task in group with running state");
+}
+
+#pragma mark - State Management
+
+- (void)testSuspendTasksInGroup
+{
+    // Add task first
+    NSURLSessionTask *task = [self googleTask];
+
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    
+    // Add task first
+    NSURLSessionTask *task2 = [NSURLSessionTask new];
+    [_sessionController addTask:task
+                      withToken:kTestTask2Token
+                        toGroup:kTestGroup2Name];
+    
+    [_sessionController suspendTasksInGroup:kTestGroupName];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+    
+    XCTAssertEqual(task.state,
+                   NSURLSessionTaskStateSuspended,
+                   @"Task should be suspended");
+    
+    XCTAssertEqual(task2.state,
+                   NSURLSessionTaskStateRunning,
+                   @"Task should be running");
+}
+
+- (void)testResumeTasksInGroup
+{
+    NSURLSessionTask *task = [self googleTask];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertEqual(task.state,
+                   NSURLSessionTaskStateSuspended,
+                   @"Task should be suspended");
+    
+    [_sessionController resumeTasksInGroup:kTestGroupName];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertEqual(task.state,
+                   NSURLSessionTaskStateRunning,
+                   @"Task should be running");
+}
+
+- (void)testCancelTasksInGroup
+{
+    NSURLSessionTask *task = [self googleTask];
+    
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+
+    [_sessionController cancelTasksInGroup:kTestGroupName];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertEqual(task.state,
+                   NSURLSessionTaskStateCanceling,
+                   @"Task should be canceled");
+}
+
+- (void)testSuspendAllTasks
+{
+    NSURLSessionTask *task = [self googleTask];
+    [task resume];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    
+    // Add task first
+    NSURLSessionTask *task2 = [self googleTask];
+    [task2 resume];
+    [_sessionController addTask:task2
+                      withToken:kTestTask2Token
+                        toGroup:kTestGroup2Name];
+
+    [_sessionController suspendAllTasks];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertEqual(task.state,
+                   NSURLSessionTaskStateSuspended,
+                   @"Task should be suspended");
+    
+    
+    XCTAssertEqual(task2.state,
+                   NSURLSessionTaskStateSuspended,
+                   @"Task should be suspended");
+}
+
+- (void)testResumeAllTasks
+{
+    NSURLSessionTask *task = [self googleTask];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    
+    // Add task first
+    NSURLSessionTask *task2 = [self googleTask];
+    [_sessionController addTask:task2
+                      withToken:kTestTask2Token
+                        toGroup:kTestGroup2Name];
+    
+    [_sessionController resumeAllTasks];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    XCTAssertEqual(task.state,
+                   NSURLSessionTaskStateRunning,
+                   @"Task should be suspended");
+    
+    
+    XCTAssertEqual(task2.state,
+                   NSURLSessionTaskStateRunning,
+                   @"Task should be suspended");
+}
+
+- (void)testCancelAllTasks
+{
+    NSURLSessionTask *task = [self googleTask];
+    [_sessionController addTask:task
+                      withToken:kTestTaskToken
+                        toGroup:kTestGroupName];
+    
+    // Add task first
+    NSURLSessionTask *task2 = [self googleTask];
+    [_sessionController addTask:task2
+                      withToken:kTestTask2Token
+                        toGroup:kTestGroup2Name];
+
+    [_sessionController cancelAllTasks];
+    [_sessionController.serialQueue waitUntilAllOperationsAreFinished];
+
+    BOOL result = task.state == NSURLSessionTaskStateCanceling
+                  || task.state == NSURLSessionTaskStateCompleted;
+    
+    XCTAssertTrue(result,
+                  @"Task should be canceled or completed");
+    
+    result = task2.state == NSURLSessionTaskStateCanceling
+             || task2.state == NSURLSessionTaskStateCompleted;
+    
+    XCTAssertTrue(result,
+                  @"Task should be canceled or completed");
+}
+
+#pragma mark - Cache
+
+- (void)testCachedResponse
+{
+    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://drewsmits.com/wp-content/uploads/2013/04/logo.png"]];
+    NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
+    [config.URLCache removeAllCachedResponses];
+    config.URLCache = [[NSURLCache alloc] initWithMemoryCapacity:1024 diskCapacity:0 diskPath:nil];
+    
+    //
+    // Stub 200 responses
+    //
+    [OHHTTPStubs stubRequestsPassingTest:^BOOL(NSURLRequest *request) {
+        return YES;
+    } withStubResponse:^OHHTTPStubsResponse *(NSURLRequest *request) {
+        return [OHHTTPStubsResponse responseWithData:[@"dummyData" dataUsingEncoding:NSUTF8StringEncoding]
+                                          statusCode:200
+                                             headers:nil];
+    }];
+
+    CRSessionController *sessionController = [CRSessionController sessionControllerWithConfiguration:config];
+    
+    __block BOOL taskOneCached = NO;
+    
+    XCTestExpectation *expectation1 = [self expectationWithDescription:@"task finished"];
+    NSURLSessionDataTask *task = [sessionController dataTaskForRequest:request
+                                                     completionHandler:^(NSData *data,
+                                                                         NSURLResponse *response,
+                                                                         BOOL cachedResponse,
+                                                                         NSError *error) {
+                                                         taskOneCached = cachedResponse;
+                                                         [expectation1 fulfill];
+                                                     }];
+    [task resume];
+    
+    [self waitForExpectationsWithTimeout:1.0 handler:nil];
+    
+    __block BOOL taskTwoCached = NO;
+    XCTestExpectation *expectation2 = [self expectationWithDescription:@"cached task finished"];
+    NSURLSessionDataTask *cachedTask = [sessionController dataTaskForRequest:request
+                                                           completionHandler:^(NSData *data,
+                                                                               NSURLResponse *response,
+                                                                               BOOL cachedResponse,
+                                                                               NSError *error) {
+                                                               taskTwoCached = cachedResponse;
+                                                               [expectation2 fulfill];
+                                                           }];
+    [cachedTask resume];
+    
+    [self waitForExpectationsWithTimeout:1.0 handler:nil];
+    
+    XCTAssertFalse(taskOneCached, @"Response should not be cached");
+    XCTAssertTrue(taskTwoCached, @"Response should be cached");
+}
+
+@end
diff --git a/CourierTests/CRTestCase.h b/CourierTests/CRTestCase.h
new file mode 100644
index 0000000..4d986e7
--- /dev/null
+++ b/CourierTests/CRTestCase.h
@@ -0,0 +1,31 @@
+//
+//  CRTestCase.h
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 <XCTest/XCTest.h>
+
+@interface CRTestCase : XCTestCase
+
+@end
diff --git a/CourierTests/CRTestCase.m b/CourierTests/CRTestCase.m
new file mode 100644
index 0000000..82c4857
--- /dev/null
+++ b/CourierTests/CRTestCase.m
@@ -0,0 +1,42 @@
+//
+//  CRTestCase.m
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 "CRTestCase.h"
+
+
+@implementation CRTestCase
+
+- (void)setUp
+{
+    [super setUp];
+}
+
+- (void)tearDown
+{
+    [super tearDown];
+}
+
+@end
diff --git a/CourierTests/CourierTests-Info.plist b/CourierTests/CourierTests-Info.plist
new file mode 100644
index 0000000..973b7a3
--- /dev/null
+++ b/CourierTests/CourierTests-Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>${EXECUTABLE_NAME}</string>
+	<key>CFBundleIdentifier</key>
+	<string>com.andrewbsmith.${PRODUCT_NAME:rfc1034identifier}</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/CourierTests/CourierTests-Prefix.pch b/CourierTests/CourierTests-Prefix.pch
new file mode 100644
index 0000000..3fdee9d
--- /dev/null
+++ b/CourierTests/CourierTests-Prefix.pch
@@ -0,0 +1,10 @@
+//
+//  Prefix header
+//
+//  The contents of this file are implicitly included at the beginning of every source file.
+//
+
+#ifdef __OBJC__
+    #import <UIKit/UIKit.h>
+    #import <Foundation/Foundation.h>
+#endif
diff --git a/CourierTests/NSDictionaryCourierTests.m b/CourierTests/NSDictionaryCourierTests.m
new file mode 100644
index 0000000..5a9cf69
--- /dev/null
+++ b/CourierTests/NSDictionaryCourierTests.m
@@ -0,0 +1,100 @@
+//
+//  NSDictionaryCourierTests.m
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 "CRTestCase.h"
+
+#import "NSDictionary+Courier.h"
+
+@interface NSDictionaryCourierTests : CRTestCase
+
+@end
+
+@implementation NSDictionaryCourierTests
+
+- (void)setUp
+{
+    [super setUp];
+    // Put setup code here; it will be run once, before the first test case.
+}
+
+- (void)tearDown
+{
+    // Put teardown code here; it will be run once, after the last test case.
+    [super tearDown];
+}
+
+- (void)testAsJSONString
+{
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+
+    NSString *jsonParams = [params cou_asJSONString];
+    
+    XCTAssertEqualObjects(jsonParams,
+                          @"{\"key1\":\"value1\",\"key2\":\"value2\"}",
+                          @"Should be a correctly formatted JSON string");
+}
+
+- (void)testAsJSONData
+{
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    NSData *jsonData = [params cou_asJSONData];
+
+    NSData *expectedData = [NSJSONSerialization dataWithJSONObject:params
+                                                           options:0
+                                                             error:nil];
+    
+    XCTAssertEqualObjects(jsonData, expectedData, @"Should correctly format as json data");
+}
+
+- (void)testAsFormURLEncodedData
+{
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+
+    NSData *data = [params cou_asFormURLEncodedData];
+    
+    NSString *dataString = [[NSString alloc] initWithData:data
+                                                 encoding:NSUTF8StringEncoding];
+    
+    XCTAssertNotNil(dataString, @"Should properly format form URL string as UTF8 string encoded data");
+
+    XCTAssertEqualObjects(dataString,
+                          @"key1=value1&key2=value2",
+                          @"Should properly format as form URL encoded string");
+
+}
+
+- (void)testAsForURLEncodedString
+{
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    
+    NSString *string = [params cou_asFormURLEncodedString];
+    
+    XCTAssertEqualObjects(string,
+                          @"key1=value1&key2=value2",
+                          @"Should properly format as form URL encoded string");
+}
+
+@end
diff --git a/CourierTests/NSMutableURLRequestCourierTests.m b/CourierTests/NSMutableURLRequestCourierTests.m
new file mode 100644
index 0000000..794f31f
--- /dev/null
+++ b/CourierTests/NSMutableURLRequestCourierTests.m
@@ -0,0 +1,246 @@
+//
+//  NSMutableURLRequestCourierTests.m
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 "CRTestCase.h"
+
+#import "NSMutableURLRequest+Courier.h"
+#import "NSDictionary+Courier.h"
+
+// Declare private methods as public for tests
+@interface NSMutableURLRequest (CourierTests)
+- (void)setURLPath:(NSString *)path withParameters:(NSDictionary *)parameters;
+- (void)addHeaderContentTypeForEncoding:(CRURLRequestEncoding)encoding;
+- (void)setHTTPBodyDataWithParameters:(NSDictionary *)parameters encoding:(CRURLRequestEncoding)encoding;
+@end
+
+@interface NSMutableURLRequestCourierTests : CRTestCase
+
+@end
+
+@implementation NSMutableURLRequestCourierTests
+
+- (void)setUp
+{
+    [super setUp];
+    // Put setup code here; it will be run once, before the first test case.
+}
+
+- (void)tearDown
+{
+    // Put teardown code here; it will be run once, after the last test case.
+    [super tearDown];
+}
+
+#pragma mark - Initialization
+
+- (void)testRequestWithMethodPartial
+{
+    NSString *path = @"http://business.com/api/v1?param=value";
+    
+    NSMutableURLRequest *request = [NSMutableURLRequest cou_requestWithMethod:@"GET"
+                                                                     path:path];
+    
+    XCTAssertNotNil(request.URL, @"Request should have a URL");
+    XCTAssertEqualObjects(request.HTTPMethod, @"GET", @"Request should have the correct HTTPmethod");
+    XCTAssertNil(request.HTTPBody, @"Request should have HTTPBody data");
+}
+
+- (void)testRequestWithMethodFull
+{
+    NSString *path = @"http://business.com/api/v1?param=value";
+    NSDictionary *urlParams = @{@"key1" : @"value1", @"key2" : @"value2"};
+    NSDictionary *bodyParams = @{@"key1" : @"value1", @"key2" : @"value2"};
+    NSDictionary *header = @{@"Header-Key" : @"Value"};
+    
+    NSMutableURLRequest *request = [NSMutableURLRequest cou_requestWithMethod:@"GET"
+                                                                         path:path
+                                                                     encoding:CRURLJSONParameterEncoding
+                                                                URLParameters:urlParams
+                                                           HTTPBodyParameters:bodyParams
+                                                                       header:header];
+    
+    XCTAssertNotNil(request.URL, @"Request should have a URL");
+    XCTAssertEqualObjects(request.HTTPMethod, @"GET", @"Request should have the correct HTTPmethod");
+    XCTAssertNotNil(request.HTTPBody, @"Request should have HTTPBody data");
+    
+    NSMutableDictionary *expectedHeader = [NSMutableDictionary dictionary];
+    [expectedHeader addEntriesFromDictionary:header];
+    expectedHeader[@"Content-Type"] = @"application/json,text/json,text/javascript";
+    
+    XCTAssertEqualObjects(request.allHTTPHeaderFields, expectedHeader, @"Request should have the correct header");
+}
+
+#pragma mark - setURLPath:withParameters:
+
+- (void)testSetURLWithNoParams
+{
+    NSString *path = @"http://business.com/api/v1";
+    
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    [request setURLPath:path withParameters:nil];
+    
+    XCTAssertEqualObjects(request.URL,
+                          [NSURL URLWithString:path],
+                          @"Request should have the correct URL");
+}
+
+- (void)testSetURLPathWithParameters
+{
+    NSString *path = @"http://business.com/api/v1";
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    [request setURLPath:path withParameters:params];
+    
+    NSString *expectedPath = [NSString stringWithFormat:@"%@%@", path, @"?key1=value1&key2=value2"];
+    
+    XCTAssertEqualObjects(request.URL,
+                          [NSURL URLWithString:expectedPath],
+                          @"Request should have the correct URL");
+    
+}
+
+- (void)testSetURLPathWithParametersAlreadInPath
+{
+    NSString *path = @"http://business.com/api/v1?param=value";
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    [request setURLPath:path withParameters:params];
+    
+    NSString *expectedPath = [NSString stringWithFormat:@"%@%@", path, @"&key1=value1&key2=value2"];
+    
+    XCTAssertEqualObjects(request.URL,
+                          [NSURL URLWithString:expectedPath],
+                          @"Request should have the correct URL");
+}
+
+#pragma mark - addHeaderContentTypeForEncoding
+
+- (void)testAddHeaderContentTypeWhenAlreadySet
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    [request addValue:@"application/custom-content-type" forHTTPHeaderField:@"Content-Type"];
+    
+    [request addHeaderContentTypeForEncoding:CRURLFormURLParameterEncoding];
+    
+    XCTAssertEqualObjects(request.allHTTPHeaderFields[@"Content-Type"],
+                          @"application/custom-content-type",
+                          @"Should not override existing content type");
+}
+
+- (void)testAddHeaderContentTypeForEncodingUnknown
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+
+    [request addHeaderContentTypeForEncoding:CRURLRequestEncodingUnknown];
+    
+    XCTAssertNil(request.allHTTPHeaderFields[@"Content-Type"],
+                 @"Request should not have a Content-Type header");
+}
+
+- (void)testAddHeaderContentTypeForEncodingFormURL
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    [request addHeaderContentTypeForEncoding:CRURLFormURLParameterEncoding];
+    
+    XCTAssertEqualObjects(request.allHTTPHeaderFields[@"Content-Type"],
+                          @"application/x-www-form-urlencoded; charset=utf-8",
+                          @"Request should have correct content type");
+}
+
+- (void)testAddHeaderContentTypeForEncodingJSON
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    [request addHeaderContentTypeForEncoding:CRURLJSONParameterEncoding];
+    
+    XCTAssertEqualObjects(request.allHTTPHeaderFields[@"Content-Type"],
+                          @"application/json,text/json,text/javascript",
+                          @"Request should have correct content type");
+}
+
+#pragma mark - setHTTPBodyDataWithParameters:encoding:
+
+- (void)testHTTPBodyDataWithParametersUnknownEncoding
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    NSString *path = @"http://business.com/api/v1?param=value";
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    
+    [request setURLPath:path withParameters:params];
+    
+    [request setHTTPBodyDataWithParameters:params encoding:CRURLRequestEncodingUnknown];
+    
+    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:params];
+    
+    XCTAssertEqualObjects(request.HTTPBody,
+                          data,
+                          @"Request should have the correct body data");
+}
+
+- (void)testHTTPBodyDataWithParametersFormURLEncoding
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    NSString *path = @"http://business.com/api/v1?param=value";
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    
+    [request setURLPath:path withParameters:params];
+    
+    [request setHTTPBodyDataWithParameters:params encoding:CRURLFormURLParameterEncoding];
+    
+    NSData *data = [params cou_asFormURLEncodedData];
+    
+    XCTAssertEqualObjects(request.HTTPBody,
+                          data,
+                          @"Request should have the correct body data");
+}
+
+- (void)testHTTPBodyDataWithParametersJSONEncoding
+{
+    NSMutableURLRequest *request = [[NSMutableURLRequest alloc] init];
+    
+    NSString *path = @"http://business.com/api/v1?param=value";
+    NSDictionary *params = @{@"key1" : @"value1", @"key2" : @"value2"};
+    
+    [request setURLPath:path withParameters:params];
+    
+    [request setHTTPBodyDataWithParameters:params encoding:CRURLJSONParameterEncoding];
+    
+    NSData *data = [params cou_asJSONData];
+    
+    XCTAssertEqualObjects(request.HTTPBody,
+                          data,
+                          @"Request should have the correct body data");
+}
+
+@end
diff --git a/CourierTests/NSStringCourierTests.m b/CourierTests/NSStringCourierTests.m
new file mode 100644
index 0000000..65bef33
--- /dev/null
+++ b/CourierTests/NSStringCourierTests.m
@@ -0,0 +1,85 @@
+//
+//  NSStringCourierTests.m
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 "CRTestCase.h"
+
+#import "NSString+Courier.h"
+
+@interface NSStringCourierTests : CRTestCase
+
+@end
+
+@implementation NSStringCourierTests
+
+- (void)setUp
+{
+    [super setUp];
+    // Put setup code here; it will be run once, before the first test case.
+}
+
+- (void)tearDown
+{
+    // Put teardown code here; it will be run once, after the last test case.
+    [super tearDown];
+}
+
+- (void)testURLEncodedString
+{
+    //
+    // :/?#[]@!$ &'()*+,;=\"<>%{}|\\^~`
+    //
+    XCTAssertEqualObjects([@":" cou_urlEncodedString]  , @"%3A", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"/" cou_urlEncodedString]  , @"%2F", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"?" cou_urlEncodedString]  , @"%3F", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"#" cou_urlEncodedString]  , @"%23", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"[" cou_urlEncodedString]  , @"%5B", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"]" cou_urlEncodedString]  , @"%5D", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"@" cou_urlEncodedString]  , @"%40", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"!" cou_urlEncodedString]  , @"%21", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"$" cou_urlEncodedString]  , @"%24", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@" " cou_urlEncodedString]  , @"%20", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"&" cou_urlEncodedString]  , @"%26", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"'" cou_urlEncodedString]  , @"%27", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"(" cou_urlEncodedString]  , @"%28", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@")" cou_urlEncodedString]  , @"%29", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"*" cou_urlEncodedString]  , @"%2A", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"+" cou_urlEncodedString]  , @"%2B", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"," cou_urlEncodedString]  , @"%2C", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@";" cou_urlEncodedString]  , @"%3B", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"=" cou_urlEncodedString]  , @"%3D", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"\"" cou_urlEncodedString] , @"%22", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"<" cou_urlEncodedString]  , @"%3C", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@">" cou_urlEncodedString]  , @"%3E", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"%" cou_urlEncodedString]  , @"%25", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"{" cou_urlEncodedString]  , @"%7B", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"}" cou_urlEncodedString]  , @"%7D", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"|" cou_urlEncodedString]  , @"%7C", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"\\" cou_urlEncodedString] , @"%5C", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"^" cou_urlEncodedString]  , @"%5E", @"Should be encoded correctly");
+    XCTAssertEqualObjects([@"~" cou_urlEncodedString]  , @"%7E", @"Should be encoded correctly");
+}
+
+@end
diff --git a/CourierTests/NSURLResponseCourierTests.m b/CourierTests/NSURLResponseCourierTests.m
new file mode 100644
index 0000000..fce1a8e
--- /dev/null
+++ b/CourierTests/NSURLResponseCourierTests.m
@@ -0,0 +1,92 @@
+//
+//  NSURLResponseCourierTests.m
+//  Courier
+//
+//  Created by Andrew Smith on 1/13/14.
+//  Copyright (c) 2014 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 "CRTestCase.h"
+
+#import "NSURLResponse+Courier.h"
+
+@interface NSURLResponseCourierTests : CRTestCase
+
+@end
+
+@implementation NSURLResponseCourierTests
+
+- (void)setUp
+{
+    [super setUp];
+    // Put setup code here; it will be run once, before the first test case.
+}
+
+- (void)tearDown
+{
+    // Put teardown code here; it will be run once, after the last test case.
+    [super tearDown];
+}
+
+- (void)testStatusCode
+{
+    NSURLResponse *response = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"path"]
+                                                          statusCode:200
+                                                        HTTPVersion:@"HTTP/1.1"
+                                                        headerFields:nil];
+    
+    XCTAssertEqual([response cou_statusCode],
+                   200,
+                   @"Response shoudl have correct status code");
+}
+
+- (void)testSuccess
+{
+    NSURLResponse *success1 = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"path"]
+                                                          statusCode:200
+                                                         HTTPVersion:@"HTTP/1.1"
+                                                        headerFields:nil];
+    
+    XCTAssertTrue(success1.cou_success, @"Response should be a success");
+    
+    NSURLResponse *success2 = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"path"]
+                                                          statusCode:201
+                                                         HTTPVersion:@"HTTP/1.1"
+                                                        headerFields:nil];
+    
+    XCTAssertTrue(success2.cou_success, @"Response should be a success");
+    
+    NSURLResponse *fail1 = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"path"]
+                                                          statusCode:400
+                                                         HTTPVersion:@"HTTP/1.1"
+                                                        headerFields:nil];
+    
+    XCTAssertFalse(fail1.cou_success, @"Response should be a failure");
+    
+    NSURLResponse *fail2 = [[NSHTTPURLResponse alloc] initWithURL:[NSURL URLWithString:@"path"]
+                                                          statusCode:100
+                                                         HTTPVersion:@"HTTP/1.1"
+                                                        headerFields:nil];
+    
+    XCTAssertFalse(fail2.cou_success, @"Response should be a failure");
+}
+
+@end
diff --git a/CourierTests/en.lproj/InfoPlist.strings b/CourierTests/en.lproj/InfoPlist.strings
new file mode 100644
index 0000000..477b28f
--- /dev/null
+++ b/CourierTests/en.lproj/InfoPlist.strings
@@ -0,0 +1,2 @@
+/* Localized versions of Info.plist keys */
+
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/Podfile b/Podfile
new file mode 100644
index 0000000..92ebd46
--- /dev/null
+++ b/Podfile
@@ -0,0 +1,20 @@
+source 'https://github.com/CocoaPods/Specs.git'
+
+workspace 'Courier'
+xcodeproj 'Courier'
+
+def common_pods
+	pod 'Reachability', '3.2'
+end
+
+target :Courier do
+	link_with 'Courier'
+	common_pods
+end
+
+target :CourierTests do
+	link_with 'CourierTests'
+	common_pods
+	pod 'OHHTTPStubs', '3.1.11'
+	pod 'OCMock', '3.1.2'
+end
\ No newline at end of file
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).