Initial check-in
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..7e7d55d
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,14 @@
+
+Copyright 2015-2016 IBM Corporation
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
diff --git a/OpenWhisk.xcodeproj/project.pbxproj b/OpenWhisk.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..0d20de5
--- /dev/null
+++ b/OpenWhisk.xcodeproj/project.pbxproj
@@ -0,0 +1,537 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		D64559FC1C8F49A600AA6840 /* OpenWhiskButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E3E6511C72760C00843E16 /* OpenWhiskButton.swift */; };
+		D6C9C3B71C8F53C9002BDB13 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C9C3B41C8F53C9002BDB13 /* Config.swift */; };
+		D6C9C3B81C8F53C9002BDB13 /* OpenWhiskConfig.plist in Resources */ = {isa = PBXBuildFile; fileRef = D6C9C3B51C8F53C9002BDB13 /* OpenWhiskConfig.plist */; };
+		D6C9C3B91C8F53C9002BDB13 /* OpenWhiskSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C9C3B61C8F53C9002BDB13 /* OpenWhiskSDK.swift */; };
+		D6C9C3BA1C8F53D3002BDB13 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C9C3B41C8F53C9002BDB13 /* Config.swift */; };
+		D6C9C3BB1C8F53D8002BDB13 /* OpenWhiskSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C9C3B61C8F53C9002BDB13 /* OpenWhiskSDK.swift */; };
+		D6C9C3BC1C8F53DD002BDB13 /* OpenWhiskSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C9C3B61C8F53C9002BDB13 /* OpenWhiskSDK.swift */; };
+		D6C9C3BD1C8F53E0002BDB13 /* Config.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6C9C3B41C8F53C9002BDB13 /* Config.swift */; };
+		D6E3E63A1C72592300843E16 /* OpenWhisk.h in Headers */ = {isa = PBXBuildFile; fileRef = D6E3E6391C72592300843E16 /* OpenWhisk.h */; settings = {ATTRIBUTES = (Public, ); }; };
+		D6E3E6411C72592300843E16 /* OpenWhisk.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D6E3E6361C72592300843E16 /* OpenWhisk.framework */; };
+		D6E3E6461C72592300843E16 /* OpenWhiskTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E3E6451C72592300843E16 /* OpenWhiskTests.swift */; };
+		D6E3E6551C72760C00843E16 /* OpenWhiskButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6E3E6511C72760C00843E16 /* OpenWhiskButton.swift */; };
+		D6F56AE21C76471B00F047B6 /* OpenWhiskWatch.h in Headers */ = {isa = PBXBuildFile; fileRef = D6F56AE11C76471B00F047B6 /* OpenWhiskWatch.h */; settings = {ATTRIBUTES = (Public, ); }; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		D6E3E6421C72592300843E16 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = D6E3E62D1C72592300843E16 /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = D6E3E6351C72592300843E16;
+			remoteInfo = OpenWhisk;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+		D6C9C3B41C8F53C9002BDB13 /* Config.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Config.swift; sourceTree = "<group>"; };
+		D6C9C3B51C8F53C9002BDB13 /* OpenWhiskConfig.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = OpenWhiskConfig.plist; sourceTree = "<group>"; };
+		D6C9C3B61C8F53C9002BDB13 /* OpenWhiskSDK.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenWhiskSDK.swift; sourceTree = "<group>"; };
+		D6E3E6361C72592300843E16 /* OpenWhisk.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OpenWhisk.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		D6E3E6391C72592300843E16 /* OpenWhisk.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenWhisk.h; sourceTree = "<group>"; };
+		D6E3E63B1C72592300843E16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D6E3E6401C72592300843E16 /* OpenWhiskTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = OpenWhiskTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		D6E3E6451C72592300843E16 /* OpenWhiskTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenWhiskTests.swift; sourceTree = "<group>"; };
+		D6E3E6471C72592300843E16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		D6E3E6511C72760C00843E16 /* OpenWhiskButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OpenWhiskButton.swift; sourceTree = "<group>"; };
+		D6F56ADF1C76471B00F047B6 /* OpenWhiskWatch.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = OpenWhiskWatch.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+		D6F56AE11C76471B00F047B6 /* OpenWhiskWatch.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OpenWhiskWatch.h; sourceTree = "<group>"; };
+		D6F56AE31C76471B00F047B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		D6E3E6321C72592300843E16 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6E3E63D1C72592300843E16 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6E3E6411C72592300843E16 /* OpenWhisk.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6F56ADB1C76471B00F047B6 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		D6E3E62C1C72592300843E16 = {
+			isa = PBXGroup;
+			children = (
+				D6E3E6381C72592300843E16 /* OpenWhisk */,
+				D6E3E6441C72592300843E16 /* OpenWhiskTests */,
+				D6F56AE01C76471B00F047B6 /* OpenWhiskWatch */,
+				D6E3E6371C72592300843E16 /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		D6E3E6371C72592300843E16 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				D6E3E6361C72592300843E16 /* OpenWhisk.framework */,
+				D6E3E6401C72592300843E16 /* OpenWhiskTests.xctest */,
+				D6F56ADF1C76471B00F047B6 /* OpenWhiskWatch.framework */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		D6E3E6381C72592300843E16 /* OpenWhisk */ = {
+			isa = PBXGroup;
+			children = (
+				D6C9C3B41C8F53C9002BDB13 /* Config.swift */,
+				D6C9C3B51C8F53C9002BDB13 /* OpenWhiskConfig.plist */,
+				D6C9C3B61C8F53C9002BDB13 /* OpenWhiskSDK.swift */,
+				D6E3E6511C72760C00843E16 /* OpenWhiskButton.swift */,
+				D6E3E6391C72592300843E16 /* OpenWhisk.h */,
+				D6E3E63B1C72592300843E16 /* Info.plist */,
+			);
+			path = OpenWhisk;
+			sourceTree = "<group>";
+		};
+		D6E3E6441C72592300843E16 /* OpenWhiskTests */ = {
+			isa = PBXGroup;
+			children = (
+				D6E3E6451C72592300843E16 /* OpenWhiskTests.swift */,
+				D6E3E6471C72592300843E16 /* Info.plist */,
+			);
+			path = OpenWhiskTests;
+			sourceTree = "<group>";
+		};
+		D6F56AE01C76471B00F047B6 /* OpenWhiskWatch */ = {
+			isa = PBXGroup;
+			children = (
+				D6F56AE11C76471B00F047B6 /* OpenWhiskWatch.h */,
+				D6F56AE31C76471B00F047B6 /* Info.plist */,
+			);
+			path = OpenWhiskWatch;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		D6E3E6331C72592300843E16 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6E3E63A1C72592300843E16 /* OpenWhisk.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6F56ADC1C76471B00F047B6 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6F56AE21C76471B00F047B6 /* OpenWhiskWatch.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		D6E3E6351C72592300843E16 /* OpenWhisk */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D6E3E64A1C72592300843E16 /* Build configuration list for PBXNativeTarget "OpenWhisk" */;
+			buildPhases = (
+				D6E3E6311C72592300843E16 /* Sources */,
+				D6E3E6321C72592300843E16 /* Frameworks */,
+				D6E3E6331C72592300843E16 /* Headers */,
+				D6E3E6341C72592300843E16 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = OpenWhisk;
+			productName = OpenWhisk;
+			productReference = D6E3E6361C72592300843E16 /* OpenWhisk.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+		D6E3E63F1C72592300843E16 /* OpenWhiskTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D6E3E64D1C72592300843E16 /* Build configuration list for PBXNativeTarget "OpenWhiskTests" */;
+			buildPhases = (
+				D6E3E63C1C72592300843E16 /* Sources */,
+				D6E3E63D1C72592300843E16 /* Frameworks */,
+				D6E3E63E1C72592300843E16 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				D6E3E6431C72592300843E16 /* PBXTargetDependency */,
+			);
+			name = OpenWhiskTests;
+			productName = OpenWhiskTests;
+			productReference = D6E3E6401C72592300843E16 /* OpenWhiskTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		D6F56ADE1C76471B00F047B6 /* OpenWhiskWatch */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = D6F56AE61C76471B00F047B6 /* Build configuration list for PBXNativeTarget "OpenWhiskWatch" */;
+			buildPhases = (
+				D6F56ADA1C76471B00F047B6 /* Sources */,
+				D6F56ADB1C76471B00F047B6 /* Frameworks */,
+				D6F56ADC1C76471B00F047B6 /* Headers */,
+				D6F56ADD1C76471B00F047B6 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = OpenWhiskWatch;
+			productName = OpenWhiskWatch;
+			productReference = D6F56ADF1C76471B00F047B6 /* OpenWhiskWatch.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		D6E3E62D1C72592300843E16 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 0720;
+				LastUpgradeCheck = 0720;
+				ORGANIZATIONNAME = IBM;
+				TargetAttributes = {
+					D6E3E6351C72592300843E16 = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+					D6E3E63F1C72592300843E16 = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+					D6F56ADE1C76471B00F047B6 = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+				};
+			};
+			buildConfigurationList = D6E3E6301C72592300843E16 /* Build configuration list for PBXProject "OpenWhisk" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = D6E3E62C1C72592300843E16;
+			productRefGroup = D6E3E6371C72592300843E16 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				D6E3E6351C72592300843E16 /* OpenWhisk */,
+				D6E3E63F1C72592300843E16 /* OpenWhiskTests */,
+				D6F56ADE1C76471B00F047B6 /* OpenWhiskWatch */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		D6E3E6341C72592300843E16 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6C9C3B81C8F53C9002BDB13 /* OpenWhiskConfig.plist in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6E3E63E1C72592300843E16 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6F56ADD1C76471B00F047B6 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		D6E3E6311C72592300843E16 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6C9C3B91C8F53C9002BDB13 /* OpenWhiskSDK.swift in Sources */,
+				D6E3E6551C72760C00843E16 /* OpenWhiskButton.swift in Sources */,
+				D6C9C3B71C8F53C9002BDB13 /* Config.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6E3E63C1C72592300843E16 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6C9C3BC1C8F53DD002BDB13 /* OpenWhiskSDK.swift in Sources */,
+				D6C9C3BD1C8F53E0002BDB13 /* Config.swift in Sources */,
+				D64559FC1C8F49A600AA6840 /* OpenWhiskButton.swift in Sources */,
+				D6E3E6461C72592300843E16 /* OpenWhiskTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		D6F56ADA1C76471B00F047B6 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				D6C9C3BA1C8F53D3002BDB13 /* Config.swift in Sources */,
+				D6C9C3BB1C8F53D8002BDB13 /* OpenWhiskSDK.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		D6E3E6431C72592300843E16 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = D6E3E6351C72592300843E16 /* OpenWhisk */;
+			targetProxy = D6E3E6421C72592300843E16 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin XCBuildConfiguration section */
+		D6E3E6481C72592300843E16 /* 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;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				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 = 9.2;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		D6E3E6491C72592300843E16 /* 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 = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				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 = 9.2;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		D6E3E64B1C72592300843E16 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				INFOPLIST_FILE = OpenWhisk/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = org.openwhisk.OpenWhisk;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		D6E3E64C1C72592300843E16 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_ENABLE_MODULES = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				INFOPLIST_FILE = OpenWhisk/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = org.openwhisk.OpenWhisk;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+			};
+			name = Release;
+		};
+		D6E3E64E1C72592300843E16 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				INFOPLIST_FILE = OpenWhiskTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.ibm.mobile.openwhisk.OpenWhiskTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Debug;
+		};
+		D6E3E64F1C72592300843E16 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				INFOPLIST_FILE = OpenWhiskTests/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.ibm.mobile.openwhisk.OpenWhiskTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+			};
+			name = Release;
+		};
+		D6F56AE41C76471B00F047B6 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				INFOPLIST_FILE = OpenWhiskWatch/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.ibm.mobile.openwhisk.OpenWhiskWatch;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = watchos;
+				SKIP_INSTALL = YES;
+				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 2.1;
+			};
+			name = Debug;
+		};
+		D6F56AE51C76471B00F047B6 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				APPLICATION_EXTENSION_API_ONLY = YES;
+				DEFINES_MODULE = YES;
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				INFOPLIST_FILE = OpenWhiskWatch/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = com.ibm.mobile.openwhisk.OpenWhiskWatch;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SDKROOT = watchos;
+				SKIP_INSTALL = YES;
+				TARGETED_DEVICE_FAMILY = 4;
+				WATCHOS_DEPLOYMENT_TARGET = 2.1;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		D6E3E6301C72592300843E16 /* Build configuration list for PBXProject "OpenWhisk" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6E3E6481C72592300843E16 /* Debug */,
+				D6E3E6491C72592300843E16 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D6E3E64A1C72592300843E16 /* Build configuration list for PBXNativeTarget "OpenWhisk" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6E3E64B1C72592300843E16 /* Debug */,
+				D6E3E64C1C72592300843E16 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D6E3E64D1C72592300843E16 /* Build configuration list for PBXNativeTarget "OpenWhiskTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6E3E64E1C72592300843E16 /* Debug */,
+				D6E3E64F1C72592300843E16 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		D6F56AE61C76471B00F047B6 /* Build configuration list for PBXNativeTarget "OpenWhiskWatch" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				D6F56AE41C76471B00F047B6 /* Debug */,
+				D6F56AE51C76471B00F047B6 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = D6E3E62D1C72592300843E16 /* Project object */;
+}
diff --git a/OpenWhisk.xcodeproj/xcshareddata/xcschemes/OpenWhisk.xcscheme b/OpenWhisk.xcodeproj/xcshareddata/xcschemes/OpenWhisk.xcscheme
new file mode 100644
index 0000000..32961c3
--- /dev/null
+++ b/OpenWhisk.xcodeproj/xcshareddata/xcschemes/OpenWhisk.xcscheme
@@ -0,0 +1,99 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0720"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D6E3E6351C72592300843E16"
+               BuildableName = "OpenWhisk.framework"
+               BlueprintName = "OpenWhisk"
+               ReferencedContainer = "container:OpenWhisk.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "D6E3E63F1C72592300843E16"
+               BuildableName = "OpenWhiskTests.xctest"
+               BlueprintName = "OpenWhiskTests"
+               ReferencedContainer = "container:OpenWhisk.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D6E3E6351C72592300843E16"
+            BuildableName = "OpenWhisk.framework"
+            BlueprintName = "OpenWhisk"
+            ReferencedContainer = "container:OpenWhisk.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D6E3E6351C72592300843E16"
+            BuildableName = "OpenWhisk.framework"
+            BlueprintName = "OpenWhisk"
+            ReferencedContainer = "container:OpenWhisk.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "D6E3E6351C72592300843E16"
+            BuildableName = "OpenWhisk.framework"
+            BlueprintName = "OpenWhisk"
+            ReferencedContainer = "container:OpenWhisk.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/OpenWhisk/Config.swift b/OpenWhisk/Config.swift
new file mode 100644
index 0000000..a7fa2f7
--- /dev/null
+++ b/OpenWhisk/Config.swift
@@ -0,0 +1,98 @@
+/*
+* Copyright 2015-2016 IBM Corporation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import Foundation
+
+/*
+ Retrieves basic configuration information for the SDK specified in WhiskConfig.plist or environment variables
+*/
+public class Config {
+    
+    public class func getHostAndPath(type type:String) -> String? {
+        
+        var url: String? = nil
+        if let dict = getConfigDictionary() {
+            url = dict.valueForKey(type) as? String
+        } else {
+            print("Configuration file missing, cannot config network call")
+        }
+        
+        return url
+    }
+    
+    
+    private class func getConfigDictionary() -> NSDictionary? {
+        
+        // Attempt 1, load the bundle from a local reference to this classes bundle
+        // I'am assuming the WhiskResources bundle is in the framework's root bundle
+        let frameworkBundle = NSBundle(forClass: Config.self)
+        
+        if let bundlePath = frameworkBundle.pathForResource("OpenWhiskResources", ofType: "bundle") {
+            if let bundle = NSBundle(path: bundlePath) {
+                let configFile = bundle.pathForResource("OpenWhiskConfig", ofType: "plist")
+                
+                if let configFile = configFile {
+                    let config = NSDictionary(contentsOfFile: configFile) as? [String: AnyObject]
+                    if let config = config {
+                        let urlConfig = config["Locations"] as? [String: String]
+                        return urlConfig
+                    }
+                }
+            }
+        } else if let bundlePath = frameworkBundle.pathForResource("OpenWhiskWatchResources", ofType: "bundle") {
+            if let bundle = NSBundle(path: bundlePath) {
+                let configFile = bundle.pathForResource("OpenWhiskConfig", ofType: "plist")
+                
+                if let configFile = configFile {
+                    let config = NSDictionary(contentsOfFile: configFile) as? [String: AnyObject]
+                    if let config = config {
+                        let urlConfig = config["Locations"] as? [String: String]
+                        return urlConfig
+                    }
+                }
+            }
+        } else {
+            if let configFile = frameworkBundle.pathForResource("OpenWhiskConfig", ofType: "plist") {
+                let config = NSDictionary(contentsOfFile: configFile) as? [String: AnyObject]
+                if let config = config {
+                    let urlConfig = config["Locations"] as? [String: String]
+                    return urlConfig
+                }
+            } else {
+                print("Can't find configuration information")
+            }
+        }
+        
+        return nil
+        
+    }
+    
+    
+    
+    /*
+     Can be used to read authentication credentials from env variables.  Useful for unit tests and maybe some build tasks
+     but not much else?
+    */
+    public class func getAuthToken() -> (apiKey: String?, apiSecret: String?)? {
+        
+        let dict = NSProcessInfo.processInfo().environment
+        let key = dict["TESTAPIKEY"]
+        let secret = dict["TESTAPISECRET"]
+        
+        return(key, secret)
+    }
+    
+}
\ No newline at end of file
diff --git a/OpenWhisk/Info.plist b/OpenWhisk/Info.plist
new file mode 100644
index 0000000..d3de8ee
--- /dev/null
+++ b/OpenWhisk/Info.plist
@@ -0,0 +1,26 @@
+<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/OpenWhisk/OpenWhisk.h b/OpenWhisk/OpenWhisk.h
new file mode 100644
index 0000000..ca8b511
--- /dev/null
+++ b/OpenWhisk/OpenWhisk.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015-2016 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <UIKit/UIKit.h>
+
+//! Project version number for OpenWhisk.
+FOUNDATION_EXPORT double OpenWhiskVersionNumber;
+
+//! Project version string for OpenWhisk.
+FOUNDATION_EXPORT const unsigned char OpenWhiskVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <OpenWhisk/PublicHeader.h>
+
+
diff --git a/OpenWhisk/OpenWhiskButton.swift b/OpenWhisk/OpenWhiskButton.swift
new file mode 100644
index 0000000..ddbfca8
--- /dev/null
+++ b/OpenWhisk/OpenWhiskButton.swift
@@ -0,0 +1,128 @@
+/*
+* Copyright 2015-2016 IBM Corporation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import UIKit
+
+/*
+
+Convenience UI element that allows you to invoke whisk actions.  You can use like a normal UIButton and handle all the press events yourself, or you can have the button "self-contained".  If you set listenForPressEvents = true it will listen for its own press events and invoke the the configured action.
+
+*/
+@objc(WhiskButton) public class WhiskButton: UIButton {
+    
+    var whisk: Whisk?
+    var urlSession: NSURLSession?
+    
+    var actionParameters: Dictionary<String,AnyObject>?
+    var actionHasResult: Bool = false
+    var actionName: String?
+    var actionPackage: String?
+    var actionNamespace: String?
+    var isListeningToSelf: Bool?
+    
+    public required init?(coder aDecoder: NSCoder) {
+        super.init(coder: aDecoder)
+    }
+    
+    public override init(frame: CGRect) {
+        super.init(frame: frame)
+    }
+    
+    public var listenForPressEvents: Bool? {
+        get {
+            return isListeningToSelf
+        }
+        
+        set {
+            if newValue == true {
+                self.addTarget(self, action: #selector(WhiskButton.buttonPressed), forControlEvents: .TouchUpInside)
+            } else {
+                self.removeTarget(self, action: #selector(WhiskButton.buttonPressed), forControlEvents: .TouchUpInside)
+            }
+            
+            isListeningToSelf = newValue
+        }
+    }
+    public var actionButtonCallback: ((reply: Dictionary<String,AnyObject>?, error: WhiskError?) -> Void)?
+    
+    
+    func buttonPressed() {
+        invokeAction(parameters: nil, actionCallback: actionButtonCallback)
+    }
+    
+    public func setupWhiskAction(qualifiedName qName:String, credentials: WhiskCredentials, hasResult: Bool = false,parameters: Dictionary<String, AnyObject>? = nil, urlSession: NSURLSession? = nil, baseUrl: String? = nil) throws {
+        
+        let pathParts = try Whisk.processQualifiedName(qName)
+        
+        setupWhiskAction(pathParts.name, package: pathParts.package, namespace: pathParts.namespace, credentials: credentials, hasResult: hasResult, parameters: parameters, urlSession: urlSession, baseUrl: baseUrl)
+    }
+    
+    public func setupWhiskAction(name: String, package: String? = nil, namespace: String = "_", credentials: WhiskCredentials, hasResult: Bool = false,parameters: Dictionary<String, AnyObject>? = nil, urlSession: NSURLSession? = nil, baseUrl: String? = nil) {
+        
+        whisk = Whisk(credentials: credentials)
+        
+        if let session = urlSession {
+            whisk?.urlSession = session
+        }
+        
+        if let baseUrl = baseUrl {
+            whisk?.baseURL = baseUrl
+        }
+        
+        actionParameters = parameters
+        actionName = name
+        actionPackage = package
+        actionNamespace = namespace
+        actionHasResult = hasResult
+    }
+    
+    public func invokeAction(parameters parameters: [String:AnyObject]? = nil, actionCallback: ((reply: Dictionary<String,AnyObject>?, error: WhiskError?) -> Void)?) {
+        
+        if let whisk = whisk, actionName = actionName, actionNamespace = actionNamespace {
+            
+            
+            dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {
+                do {
+                    
+                    var params:[String:AnyObject]?
+                    
+                    if let parameters = parameters {
+                        params = parameters
+                    } else {
+                        params = self.actionParameters
+                    }
+                    
+                    try whisk.invokeAction(name: actionName, package: self.actionPackage, namespace: actionNamespace, parameters: params, hasResult: self.actionHasResult, callback: {(reply, error) in
+                        
+                        
+                        // note callback is in main queue already we should be on the UI thread
+                        if let actionCallback = actionCallback {
+                            actionCallback(reply:reply, error: error)
+                        }
+                        
+                        
+                    })
+                } catch {
+                    print("Error invoking action: \(error)")
+                }
+            }
+        } else {
+            print("WhiskButton action not initialized")
+        }
+        
+    }
+    
+}
diff --git a/OpenWhisk/OpenWhiskConfig.plist b/OpenWhisk/OpenWhiskConfig.plist
new file mode 100644
index 0000000..282d66c
--- /dev/null
+++ b/OpenWhisk/OpenWhiskConfig.plist
@@ -0,0 +1,13 @@
+<?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>Locations</key>
+	<dict>
+		<key>actions</key>
+		<string>https://openwhisk.ng.bluemix.net/api/v1/</string>
+		<key>triggers</key>
+		<string>https://openwhisk.ng.bluemix.net/api/v1/</string>
+	</dict>
+</dict>
+</plist>
diff --git a/OpenWhisk/OpenWhiskSDK.swift b/OpenWhisk/OpenWhiskSDK.swift
new file mode 100644
index 0000000..272f4ac
--- /dev/null
+++ b/OpenWhisk/OpenWhiskSDK.swift
@@ -0,0 +1,435 @@
+/*
+* Copyright 2015-2016 IBM Corporation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import Foundation
+
+/*
+
+Hold the Whisk access key and access token.  The session token and jwtToken can be used to implement
+custom authentication flows
+
+*/
+public struct WhiskCredentials {
+    public init(accessKey: String?, accessToken: String?, sessionToken:String? = nil, jwtToken: String? = nil) {
+        self.accessToken = accessToken
+        self.accessKey = accessKey
+        self.sessionToken = sessionToken
+        self.jwtToken = jwtToken
+    }
+    
+    // whisk credentials
+    public var accessKey: String?
+    public var accessToken: String?
+    public var sessionToken: String?
+    
+    // optional app credentials
+    public var appKey: String?
+    public var appSecret: String?
+    
+    // optional token for custom authentication flow
+    public var jwtToken: String?
+}
+
+/* Error types for Whisk calls */
+public enum WhiskError: ErrorType {
+    case HTTPError(description: String, statusCode: Int) // something went wrong with the http call
+    case JsonError(description: String) // json wasn't right
+    case CredentialError(description: String) // something is wrong with the whisk credentials
+    case QualifiedNameFormat(description: String) // something is wrong in qualified name
+    case WhiskProcessingError(description: String, errorCode: Int) // something went wrong on the whisk side.
+}
+
+/* Type of Whisk operation requested */
+enum WhiskType {
+    case Action
+    case Trigger
+}
+
+/* Main class to hold the calls to invoke Actions and fire Triggers */
+public class Whisk {
+    
+    // Secrets needed to call Whisk API
+    let AccessKey: String? // Whisk key
+    let AccessToken: String? // Whisk token
+    let AppKey: String? // application Key (currently not used)
+    let AppSecret: String? // application Secret (curently not used)
+    
+    // api Host for Whisk backend
+    public var whiskBaseURL: String?
+    
+    // set to non-nil if using a custom session
+    public var urlSession: NSURLSession?
+    
+    public var verboseReplies: Bool = false
+    
+    // Set these if you want to run unit tests and mock
+    // calls to Whisk backend.
+    public var useMock: Bool = false
+    public var mockReply: [String: AnyObject]?
+    public var mockError: WhiskError?
+    
+    
+    // return base URL of backend including common path for all API calls
+    public var baseURL: String? {
+        set {
+            if let url = newValue {
+                
+                let c = url.characters.last
+                
+                let separater =  c == "/" ? "" : "/"
+                
+                whiskBaseURL = url + separater + "api/v1/"
+                
+            } else {
+                whiskBaseURL = nil
+            }
+        }
+        get {
+            return whiskBaseURL
+        }
+    }
+    
+    // Initialize with credentials, region currently not used
+    public init(credentials: WhiskCredentials, region: String = "US-East-1") {
+        // initialize
+        AccessKey = credentials.accessKey
+        AccessToken = credentials.accessToken
+        AppKey = credentials.appKey
+        AppSecret = credentials.appSecret
+        
+    }
+    
+    
+    /* Base function to fire Whisk Trigger identified by qualified name */
+    public func fireTrigger(qualifiedName qualifiedName: String, parameters: AnyObject? = nil, callback: (reply: Dictionary<String,AnyObject>?, error:WhiskError?)->Void) throws {
+        
+        let pathParts = try Whisk.processQualifiedName(qualifiedName)
+        try fireTrigger(name: pathParts.name, package: pathParts.package, namespace: pathParts.namespace, parameters: parameters, callback: callback)
+    }
+    
+    /* Base function to invoke Whisk Action identified by qualified name */
+    public func invokeAction(qualifiedName qualifiedName: String, parameters: AnyObject?, hasResult: Bool = false, callback: (reply: Dictionary<String,AnyObject>?, error:WhiskError?)->Void) throws {
+        
+        let pathParts = try Whisk.processQualifiedName(qualifiedName)
+        try invokeAction(name: pathParts.name, package: pathParts.package, namespace: pathParts.namespace, parameters: parameters, hasResult: hasResult, callback: callback)
+    }
+    
+    
+    /* Base function to fire Whisk Trigger identified by components */
+    public func fireTrigger(name name: String, package: String? = nil, namespace: String = "_", parameters: AnyObject? = nil, callback: (reply: Dictionary<String,AnyObject>?, error:WhiskError?)->Void) throws {
+        
+        if let accessKey = AccessKey, accessToken = AccessToken {
+            try httpRequestWhiskAPI(accessKey: accessKey, accessToken: accessToken, namespace: namespace, verb: "POST", type: .Trigger, package: package, name:name, parameters: parameters, isSync: false, callback: { (jsonArray, error) in
+                if let error = error {
+                    callback(reply: nil, error: error)
+                } else {
+                    callback(reply: jsonArray, error: nil)
+                }
+            })
+        } else {
+            throw WhiskError.CredentialError(description: "Access key and token not set")
+        }
+        
+        
+    }
+    
+    /* Base function to invoke Whisk Action identified by components */
+    public func invokeAction(name name: String, package: String? = nil, namespace: String = "_", parameters: AnyObject?, hasResult:Bool = false, callback: (reply: Dictionary<String,AnyObject>?, error: WhiskError?)-> Void) throws {
+        if let accessKey = AccessKey, accessToken = AccessToken {
+            
+            try httpRequestWhiskAPI(accessKey: accessKey, accessToken: accessToken, namespace: namespace, verb: "POST", type: .Action, package: package, name: name, parameters: parameters, isSync: hasResult, callback: {(jsonDict, error) in
+                if let error = error {
+                    callback(reply: nil, error: error)
+                } else {
+                    callback(reply: jsonDict, error: nil)
+                }
+                
+            })
+        } else {
+            throw WhiskError.CredentialError(description: "Access key and token not set")
+        }
+        
+    }
+    
+    /* can redirect call here, e.g. if mocking */
+    func httpRequestWhiskAPI(accessKey accessKey: String, accessToken: String, namespace: String, verb: String, type: WhiskType, package: String?, name: String, parameters: AnyObject?, isSync: Bool, callback: (reply: Dictionary<String,AnyObject>?, error:WhiskError?) ->Void) throws {
+        
+        if useMock {
+            callback(reply:mockReply, error: mockError)
+            
+        } else {
+            try whiskAPI(accessKey: accessKey, accessToken: accessToken, namespace: namespace, verb: verb, type: type, package: package, name: name, parameters: parameters, isSync: isSync, callback: callback)
+        }
+    }
+    
+    
+    /* Network call */
+    func whiskAPI(accessKey accessKey: String, accessToken: String, namespace: String, verb: String, type: WhiskType, package: String?, name: String, parameters: AnyObject?, isSync: Bool, callback: (reply: Dictionary<String,AnyObject>?, error:WhiskError?) ->Void) throws {
+        
+        // set parameters
+        var paramsIsDict = false
+        if let parameters = parameters {
+            if parameters is Dictionary<String, AnyObject> {
+                paramsIsDict = true
+            }
+        }
+        
+        // set authorization string
+        let loginString = NSString(format: "%@:%@", accessKey, accessToken)
+        let loginData: NSData = loginString.dataUsingEncoding(NSUTF8StringEncoding)!
+        let base64LoginString = loginData.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
+        
+        let typeStr: String!
+        
+        // set type
+        switch type {
+        case .Action:
+            typeStr = "actions"
+        case .Trigger:
+            typeStr = "triggers"
+        }
+        
+        // get base URL
+        guard let actionURL = baseURL != nil ? baseURL : Config.getHostAndPath(type: typeStr) else {
+            callback(reply: nil, error: WhiskError.HTTPError(description: "Base URL not set, try using whisk.baseUrl setting", statusCode: 400))
+            return
+        }
+        
+        // append namespace and trigger/action path
+        var syncName = "namespaces/"
+        
+        if let package = package {
+            syncName = syncName + namespace+"/"+typeStr+"/"+package+"/"+name
+        } else {
+            syncName = syncName + namespace+"/"+typeStr+"/"+name
+        }
+        
+        // if action has results, specify as blocking
+        if isSync == true {
+            syncName += "?blocking=true"
+        }
+        
+        // create request
+        guard let url = NSURL(string:actionURL+syncName) else {
+            // send back error on main queue
+            
+            callback(reply: nil, error: WhiskError.HTTPError(description: "Malformed url \(actionURL+syncName)", statusCode: 400))
+            
+            return
+            
+            
+        }
+        
+        let request = NSMutableURLRequest(URL: url)
+        request.setValue("application/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
+        request.addValue("Basic \(base64LoginString)", forHTTPHeaderField: "Authorization")
+        request.HTTPMethod = verb
+        
+        // create JSON from parameters dictionary
+        do {
+            
+            if let parameters = parameters {
+                if paramsIsDict {
+                    request.HTTPBody = try NSJSONSerialization.dataWithJSONObject(parameters, options: NSJSONWritingOptions())
+                } else {
+                    if parameters is String {
+                        let str = "{\"payload\":\"\(parameters as! String)\"}"
+                        request.HTTPBody = str.dataUsingEncoding(NSUTF8StringEncoding)
+                    } else {
+                        let str = "{\"payload\": \(parameters)}"
+                        request.HTTPBody = str.dataUsingEncoding(NSUTF8StringEncoding)
+                    }
+                }
+            }
+            
+        } catch {
+            print("Error parsing JSON in Whisk request: \(error)")
+        }
+        
+        
+        // retrieve session as default or use developer specified session
+        let sess: NSURLSession!
+        if let _ = urlSession {
+            sess = urlSession
+        } else {
+            let sessConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
+            sess = NSURLSession(configuration: sessConfig)
+        }
+        
+        // perform network request
+        let task = sess.dataTaskWithRequest(request) {
+            data, response, error in
+            let statusCode: Int!
+            
+            if let error = error {
+                
+                if let httpResponse = response as? NSHTTPURLResponse {
+                    statusCode = httpResponse.statusCode
+                } else {
+                    statusCode = -1
+                }
+                // return network transport error call on main queue
+                dispatch_async(dispatch_get_main_queue()) {
+                    callback(reply: nil, error: WhiskError.HTTPError(description: "\(error.localizedDescription)", statusCode: statusCode))
+                }
+                
+                return
+                
+            } else {
+                
+                if let httpResponse = response as? NSHTTPURLResponse {
+                    statusCode = httpResponse.statusCode
+                    do {
+                        // success
+                        if statusCode < 300 {
+                            
+                            switch verb {
+                                // is an action invocation
+                            case "POST":
+                                var jsonDict = [String:AnyObject]()
+                                
+                                let respDict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! Dictionary<String, AnyObject>
+                                jsonDict = respDict
+                                
+                                
+                                if let whiskError = jsonDict["error"] as? String {
+                                    
+                                    var errorCode = -1
+                                    if let code = jsonDict["code"] as? Int {
+                                        errorCode = code
+                                    }
+                                    // send back error on main queue
+                                    dispatch_async(dispatch_get_main_queue()) {
+                                        callback(reply: nil, error: WhiskError.WhiskProcessingError(description: "errorCode:\(errorCode), \(whiskError)", errorCode: errorCode))
+                                    }
+                                    
+                                } else {
+                                    
+                                    var whiskReply = [String:AnyObject]()
+                                    
+                                    if self.verboseReplies == true {
+                                        whiskReply = jsonDict
+                                    } else {
+                                        let reply = jsonDict
+                                        whiskReply["activationId"] = reply["activationId"]
+                                        
+                                        if isSync == true {
+                                            if let whiskResponse = reply["response"] as? [String:AnyObject] {
+                                                
+                                                if let actionResult = whiskResponse["result"] {
+                                                    
+                                                    //if let payload = actionResult["payload"] {
+                                                    
+                                                    let payload:AnyObject? = actionResult
+                                                    if payload is String {
+                                                        do {
+                                                            let payloadObj:AnyObject? = try NSJSONSerialization.JSONObjectWithData(payload!.dataUsingEncoding(NSUTF8StringEncoding)!, options:[])
+                                                            whiskReply["result"] = (payloadObj as? [String:AnyObject])!
+                                                        } catch {
+                                                            print("Error parsing payload into JSON, defaulting to string")
+                                                            whiskReply = ["result" : "\(payload!)"]
+                                                        }
+                                                    } else {
+                                                        whiskReply["result"] = (payload as? [String:AnyObject])!
+                                                    }
+                                                    //}
+                                                }
+                                            }
+                                        }
+                                    }
+                                    
+                                    // send back successful response on main queue
+                                    dispatch_async(dispatch_get_main_queue()) {
+                                        callback(reply: whiskReply, error: nil)
+                                    }
+                                }
+                                
+                                // get info about actions/triggers
+                                // not used right now
+                            case "GET":
+                                let jsonArray = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSArray
+                                let jsonDict:Dictionary<String, AnyObject> = ["array":jsonArray]
+                                
+                                dispatch_async(dispatch_get_main_queue()) {
+                                    callback(reply: jsonDict, error: nil)
+                                }
+                                
+                            default:
+                                break
+                                
+                            }
+                        } else {
+                            dispatch_async(dispatch_get_main_queue()) {
+                                callback(reply: nil, error: WhiskError.HTTPError(description: "Whisk returned HTTP error code", statusCode: statusCode))
+                            }
+                        }
+                        
+                    } catch {
+                        print("Error parsing JSON from Whisk response: \(error)")
+                        dispatch_async(dispatch_get_main_queue()) {
+                            callback(reply: nil, error: WhiskError.JsonError(description: "\(error)"))
+                        }
+                    }
+                }
+            }
+        }
+        
+        task.resume()
+        
+        
+    }
+    
+    /* Convert qualified name string into component parts of action or trigger call */
+    class func processQualifiedName(qName: String) throws -> (namespace:String, package: String?, name: String) {
+        var namespace = "_"
+        var package: String? = nil
+        var name = ""
+        var doesSpecifyNamespace = false
+        
+        if qName.characters.first == "/" {
+            doesSpecifyNamespace = true
+        }
+        
+        let pathParts = qName.characters.split { $0 == "/" }.map(String.init)
+        
+        if doesSpecifyNamespace == true {
+            if pathParts.count == 2 {
+                namespace = pathParts[0]
+                name = pathParts[1]
+            } else if pathParts.count == 3 {
+                namespace = pathParts[0]
+                package = pathParts[1]
+                name = pathParts[2]
+            } else {
+                throw WhiskError.QualifiedNameFormat(description: "Cannot parse \(qName)")
+            }
+        } else {
+            if pathParts.count == 1 {
+                name = pathParts[0]
+            } else if pathParts.count == 2 {
+                package = pathParts[0]
+                name = pathParts[1]
+            } else {
+                throw WhiskError.QualifiedNameFormat(description: "Cannot parse \(qName)")
+            }
+        }
+        
+        return (namespace, package, name)
+    }
+    
+}
+
+
diff --git a/OpenWhiskTests/Info.plist b/OpenWhiskTests/Info.plist
new file mode 100644
index 0000000..ba72822
--- /dev/null
+++ b/OpenWhiskTests/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>$(PRODUCT_BUNDLE_IDENTIFIER)</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/OpenWhiskTests/OpenWhiskTests.swift b/OpenWhiskTests/OpenWhiskTests.swift
new file mode 100644
index 0000000..20024c3
--- /dev/null
+++ b/OpenWhiskTests/OpenWhiskTests.swift
@@ -0,0 +1,328 @@
+/*
+* Copyright 2015-2016 IBM Corporation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import UIKit
+import XCTest
+import OpenWhisk
+
+class NetworkUtilsDelegate: NSObject, NSURLSessionDelegate {
+    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
+        
+        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
+    }
+}
+
+class OpenWhiskTests: XCTestCase {
+    
+    let Timeout:NSTimeInterval = 100 // time to wait for whisk action to complete
+    
+    var apiKey: String?
+    var apiSecret: String?
+    
+    override func setUp() {
+        super.setUp()
+        
+        
+        if let config = Config.getAuthToken() {
+            apiKey = config.apiKey
+            apiSecret = config.apiSecret
+        }
+        
+    }
+    
+    override func tearDown() {
+        super.tearDown()
+    }
+    
+    
+    
+    func testWhiskParameterizedAction() {
+        
+        if let apiKey = apiKey, apiSecret = apiSecret {
+            // allow for self-signed certificates
+            let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: NetworkUtilsDelegate(), delegateQueue:NSOperationQueue.mainQueue())
+            
+            let credentials = WhiskCredentials(accessKey: apiKey, accessToken: apiSecret)
+            let whisk = Whisk(credentials: credentials)
+            whisk.urlSession = session
+            
+            // setup for async testing
+            let expectation = expectationWithDescription("Whisk Callback")
+            
+            do {
+                try whisk.invokeAction(name: "date", package: "util", namespace: "whisk.system", parameters: nil, hasResult: true,
+                    callback: {(reply, error) in
+                        
+                        if let error = error {
+                            if case let WhiskError.HTTPError(description, statusCode) = error {
+                                
+                                print("Error: \(description) statusCode: \(statusCode))")
+                                
+                                if statusCode != 401 && statusCode != 404 && statusCode != 408 && statusCode != 500 {
+                                    XCTFail("Error: \(description) statusCode: \(statusCode))")
+                                }
+                                
+                            }
+                        }
+                        
+                        if let reply = reply {
+                            
+                            print("Reply is \(reply)")
+                            XCTAssertNotNil(reply["activationId"])
+                            let id = reply["activationId"] as! String
+                            print("Got id \(id)")
+                        }
+                        
+                        expectation.fulfill()
+                        
+                        
+                })
+            } catch {
+                print(error)
+                XCTFail("Error invoking action \(error)")
+            }
+            
+            waitForExpectationsWithTimeout(Timeout, handler: { error in
+                
+                if let error = error {
+                    print("Error: \(error)")
+                }
+            })
+        } else {
+            XCTFail("No credentials available to run test")
+        }
+    }
+    
+    func testWhiskQualifiedNameAction() {
+        
+        if let apiKey = apiKey, apiSecret = apiSecret {
+            // setup for async testing
+            let expectation = expectationWithDescription("Whisk Callback")
+            // allow for self-signed certificates
+            let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: NetworkUtilsDelegate(), delegateQueue:NSOperationQueue.mainQueue())
+            
+            let credentials = WhiskCredentials(accessKey: apiKey, accessToken: apiSecret)
+            let whisk = Whisk(credentials: credentials)
+            whisk.urlSession = session
+            do {
+                try whisk.invokeAction(qualifiedName: "/whisk.system/util/date", parameters: nil, hasResult: true, callback: {(reply, error) in
+                    
+                    if let error = error {
+                        if case let WhiskError.HTTPError(description, statusCode) = error {
+                            
+                            print("Error: \(description) statusCode: \(statusCode))")
+                            
+                            if statusCode != 401 && statusCode != 404 && statusCode != 408 && statusCode != 500 {
+                                XCTFail("Error: \(description) statusCode: \(statusCode))")
+                            }
+                            
+                        }
+                    }
+                    
+                    if let reply = reply {
+                        
+                        print("Reply is \(reply)")
+                        XCTAssertNotNil(reply["activationId"])
+                        let id = reply["activationId"] as! String
+                        print("Got id \(id)")
+                    }
+                    
+                    expectation.fulfill()
+                    
+                    
+                })
+            } catch {
+                print(error)
+                XCTFail("Error invoking action \(error)")
+            }
+            
+            waitForExpectationsWithTimeout(Timeout, handler: { error in
+                
+                if let error = error {
+                    print("Error: \(error)")
+                }
+            })
+        } else {
+            XCTFail("No credentials available to run test")
+        }
+    }
+    
+    func testWhiskSettingBaseUrl() {
+        
+        if let apiKey = apiKey, apiSecret = apiSecret {
+            // setup for async testing
+            let expectation = expectationWithDescription("Whisk Callback")
+            
+            // allow for self-signed certificates
+            let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: NetworkUtilsDelegate(), delegateQueue:NSOperationQueue.mainQueue())
+            
+            let credentials = WhiskCredentials(accessKey: apiKey, accessToken: apiSecret)
+            let whisk = Whisk(credentials: credentials)
+            whisk.urlSession = session
+            
+            do {
+                whisk.baseURL = "https://openwhisk.ng.bluemix.net"
+                
+                try whisk.invokeAction(qualifiedName: "/whisk.system/util/date", parameters: nil, hasResult: true, callback: {(reply, error) in
+                    
+                    if let error = error {
+                        if case let WhiskError.HTTPError(description, statusCode) = error {
+                            
+                            print("Error: \(description) statusCode: \(statusCode))")
+                            
+                            if statusCode != 401 && statusCode != 404 && statusCode != 408 && statusCode != 500 {
+                                XCTFail("Error: \(description) statusCode: \(statusCode))")
+                            }
+                            
+                        }
+                    }
+                    
+                    if let reply = reply {
+                        
+                        print("Reply is \(reply)")
+                        XCTAssertNotNil(reply["activationId"])
+                        let id = reply["activationId"] as! String
+                        print("Got id \(id)")
+                    }
+                    
+                    expectation.fulfill()
+                    
+                    
+                })
+            } catch {
+                print(error)
+                XCTFail("Error invoking action \(error)")
+            }
+            
+            waitForExpectationsWithTimeout(Timeout, handler: { error in
+                
+                if let error = error {
+                    print("Error: \(error)")
+                }
+            })
+            
+        } else {
+            XCTFail("No credentials available to run test")
+        }
+    }
+    
+    func testWhiskVerboseReplies() {
+        
+        if let apiKey = apiKey, apiSecret = apiSecret {
+            // setup for async testing
+            let expectation = expectationWithDescription("Whisk Callback")
+            
+            // allow for self-signed certificates
+            let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: NetworkUtilsDelegate(), delegateQueue:NSOperationQueue.mainQueue())
+            
+            let credentials = WhiskCredentials(accessKey: apiKey, accessToken: apiSecret)
+            let whisk = Whisk(credentials: credentials)
+            whisk.urlSession = session
+            whisk.baseURL = "https://openwhisk.ng.bluemix.net"
+            
+            do {
+                whisk.verboseReplies = true
+                
+                try whisk.invokeAction(qualifiedName: "/whisk.system/util/date", parameters: nil, hasResult: true, callback: {(reply, error) in
+                    
+                    if let error = error {
+                        if case let WhiskError.HTTPError(description, statusCode) = error {
+                            
+                            print("Error: \(description) statusCode: \(statusCode))")
+                            
+                            if statusCode != 401 && statusCode != 404 && statusCode != 408 && statusCode != 500 {
+                                XCTFail("Error: \(description) statusCode: \(statusCode))")
+                            }
+                            
+                        }
+                    }
+                    
+                    if let reply = reply {
+                        
+                        print("Reply is \(reply)")
+                        XCTAssertNotNil(reply["activationId"])
+                        let id = reply["activationId"] as! String
+                        print("Got id \(id)")
+                    }
+                    
+                    expectation.fulfill()
+                    
+                    
+                })
+            } catch {
+                print(error)
+                XCTFail("Error invoking action \(error)")
+            }
+            
+            waitForExpectationsWithTimeout(Timeout, handler: { error in
+                
+                if let error = error {
+                    print("Error: \(error)")
+                }
+            })
+            
+        } else {
+            XCTFail("No credentials available to run test")
+        }
+    }
+    
+    func testWhiskTrigger() {
+        
+        if let apiKey = apiKey, apiSecret = apiSecret {
+            // setup for async testing
+            let expectation = expectationWithDescription("Whisk Callback")
+            
+            // allow for self-signed certificates
+            let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: NetworkUtilsDelegate(), delegateQueue:NSOperationQueue.mainQueue())
+            
+            let credentials = WhiskCredentials(accessKey: apiKey, accessToken: apiSecret)
+            let whisk = Whisk(credentials: credentials)
+            whisk.urlSession = session
+            whisk.baseURL = "https://openwhisk.ng.bluemix.net"
+            
+            do {
+                
+                try whisk.fireTrigger(name: "myTrigger", callback: { (reply, error) in
+                    
+                    if let error = error {
+                        print("\(error)")
+                    } else if let reply = reply {
+                        print("\(reply)")
+                    } else {
+                        print("No error or response")
+                    }
+                })
+                
+                expectation.fulfill()
+                
+            } catch {
+                print(error)
+                XCTFail("Error invoking trigger \(error)")
+            }
+            
+            waitForExpectationsWithTimeout(Timeout, handler: { error in
+                
+                if let error = error {
+                    print("Error: \(error)")
+                }
+            })
+            
+        } else {
+            XCTFail("No credentials available to run test")
+        }
+    }
+    
+}
diff --git a/OpenWhiskWatch/Info.plist b/OpenWhiskWatch/Info.plist
new file mode 100644
index 0000000..d3de8ee
--- /dev/null
+++ b/OpenWhiskWatch/Info.plist
@@ -0,0 +1,26 @@
+<?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>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>
diff --git a/OpenWhiskWatch/OpenWhiskWatch.h b/OpenWhiskWatch/OpenWhiskWatch.h
new file mode 100644
index 0000000..a70c70b
--- /dev/null
+++ b/OpenWhiskWatch/OpenWhiskWatch.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2015-2016 IBM Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#import <WatchKit/WatchKit.h>
+
+//! Project version number for OpenWhiskWatch.
+FOUNDATION_EXPORT double OpenWhiskWatchVersionNumber;
+
+//! Project version string for OpenWhiskWatch.
+FOUNDATION_EXPORT const unsigned char OpenWhiskWatchVersionString[];
+
+// In this header, you should import all the public headers of your framework using statements like #import <OpenWhiskWatch/PublicHeader.h>
+
+
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..7c04fb6
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,22 @@
+/*
+* Copyright 2015-2016 IBM Corporation
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+import PackageDescription
+
+let package = Package(
+    name: "OpenWhisk", 
+    exclude: ["OpenWhiskWatch","OpenWhiskTests", "OpenWhisk/OpenWhiskButton.swift"]
+)
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..af4cd59
--- /dev/null
+++ b/README.md
@@ -0,0 +1,231 @@
+# Swift Client SDK for OpenWhisk
+This is a Swift-based client SDK to access [OpenWhisk](https://github.com/openwhisk/openwhisk).  It partially implements the OpenWhisk [REST API](https://github.com/openwhisk/openwhisk/blob/master/docs/reference.md#rest-api) and allows you to invoke actions and fire triggers. The client SDK is compatible with Swift 2.x and runs on iOS 9, WatchOS 2, and Darwin.  Since this code uses classes like NSURLSession, Linux support is linked to the current status of [Foundation on Linux](https://github.com/apple/swift-corelibs-foundation).
+
+## Installation
+You can install the SDK using the source code in this repo, as a Cocoapod for iOS and WatchOS 2 apps, Carthage, and as a package using the Swift Package Manager for Darwin CLI apps.
+
+### Source Code Installation
+To build the source code:
+- Clone this repo.
+- Open the `OpenWhisk.xcodeproj` file in XCode 7.2 or 7.3.
+- Build the OpenWhisk target for an iOS app or the OpenWhiskWatch target for a WatchOS 2 app.
+- Locate the binary framework file (usually in `debug` or `release` directories at `~/Library/Developer/Xcode/DerivedData/$projectName-*`) and add it to the "Embedded Binaries" list in the General settings of your apps' target.
+
+### CocoaPods Installation
+The following lines in a Podfile will install the SDK for an iOS app with a watch OS 2 extension: 
+
+```
+source 'https://github.com/openwhisk/openwhisk-podspecs.git'
+
+use_frameworks!
+
+target 'MyApp' do
+     platform :ios, '9.0'
+     pod 'OpenWhisk'
+end
+
+target 'MyApp WatchKit Extension' do 
+     platform :watchos, '2.0'
+     pod 'OpenWhiskWatch'
+end
+```
+
+### Carthage Installation
+Here is an example Cartfile for iOS installation using Carthage:  
+
+```
+github "openwhisk/swift-client-sdk.git" ~> 0.1.0 # Or latest version
+
+```
+
+### Swift Package Manager
+Use the Swift Package Manager to install into a Darwin CLI app.  Below is an example Package.swift manifest file you can use:
+
+```
+import PackageDescription
+
+let package = Package(
+  name:  "PackageTest",
+  dependencies: [
+    .Package(url:  "https://github.com/openwhisk/swift-client-sdk.git", versions: Version(0,0,0)..<Version(1,0,0)),
+  ]
+)
+```
+
+## Usage
+
+To get up and running quickly, create a WhiskCredentials object with your OpenWhisk API credentials and create a Whisk instance from that.
+
+In Swift 2.x, you create a credentials object as:
+
+```
+let credentialsConfiguration = WhiskCredentials(accessKey: "myKey", accessToken: "myToken")
+
+let whisk = Whisk(credentials: credentialsConfiguration!)
+```
+
+You can retrieve the key and token with the following CLI command:
+
+```
+wsk property get --auth
+```
+
+```
+whisk auth              kkkkkkkk-kkkk-kkkk-kkkk-kkkkkkkkkkkk:tttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttttt
+```
+
+The strings before and after the colon are your key and token, respectively.
+
+### Invoke an OpenWhisk Action
+Call "invokeAction" with the action name to invoke a remote action. You can specify the namespace the action belongs to, or just leave it blank to accept the default namespace.  Use a dictionary to pass parameters to the action as required.
+
+```
+// In this example, we are invoking an action to print a message to the OpenWhisk Console
+var params = Dictionary<String, String>()
+params["payload"] = "Hi from mobile"
+
+do {
+    try whisk.invokeAction(name: "helloConsole", package: "mypackage", namespace: "mynamespace", parameters: params, hasResult: false, callback: {(reply, error) -> Void in 
+        if let error = error {
+            //do something
+            print("Error invoking action \(error.localizedDescription)")
+        } else {
+            print("Action invoked!")
+        }
+        
+    })
+} catch {
+    print("Error \(error)")
+}
+```
+
+In the above example, we are invoking the "helloConsole" action using the default namespace. 
+
+### Fire an OpenWhisk Trigger
+To fire a remote OpenWhisk trigger, call the "fireTrigger" method.  Pass in parameters as required using a dictionary.
+```
+// In this example we are firing a trigger when our location has changed by a certain amount
+
+var locationParams = Dictionary<String, String>()
+locationParams["payload"] = "{\"lat\":41.27093, \"lon\":-73.77763}"
+
+do {
+    try whisk.fireTrigger(name: "locationChanged", package: "mypackage", namespace: "mynamespace", parameters: locationParams, callback: {(reply, error) -> Void in
+        
+        if let error = error {
+            print("Error firing trigger \(error.localizedDescription)")
+        } else {
+            print("Trigger fired!")
+        }
+    })
+} catch {
+    print("Error \(error)")
+}
+```
+In the above example, we are firing a trigger "locationChanged".
+
+### Actions that return a result
+If the action returns a result, set hasResult to true in the invokeAction call. The result of the action is returned in the reply dictionary, for example:
+```
+do {
+    try whisk.invokeAction(name: "actionWithResult", package: "mypackage", namespace: "mynamespace", parameters: params, hasResult: true, callback: {(reply, error) -> Void in
+        
+        if let error = error {
+            //do something
+            print("Error invoking action \(error.localizedDescription)")
+            
+        } else {
+            var result = reply["result"]
+            print("Got result \(result)")
+        }
+        
+        
+    })
+} catch {
+    print("Error \(error)")
+}
+```
+By default, the SDK will only return the activationId and any result produced by the invoked action.  To get metadata of the entire response object, which includes the HTTP response status code, use this setting:
+
+```
+whisk.verboseReplies = true
+```
+
+## SDK configuration
+
+You can configure the SDK to work with different installations of OpenWhisk using the baseURL parameter. For instance:
+
+```
+whisk.baseURL = "http://localhost:8080"
+```
+will use an OpenWhisk running at localhost:8080.  If you do not specify the baseUrl, the Mobile SDK will use the instance running at https://openwhisk.ng.bluemix.net
+
+You can pass in a custom NSURLSession in case you require special network handling.  For example, you may have your own OpenWhisk installation that uses self-signed certificates:
+
+```
+
+// create a network delegate that trusts everything
+class NetworkUtilsDelegate: NSObject, NSURLSessionDelegate {
+    func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
+        
+        completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
+    }
+}
+
+// create an NSURLSession that uses the trusting delegate
+let session = NSURLSession(configuration: NSURLSessionConfiguration.defaultSessionConfiguration(), delegate: NetworkUtilsDelegate(), delegateQueue:NSOperationQueue.mainQueue())
+
+// set the SDK to use this urlSession instead of the default shared one
+whisk.urlSession = session
+```
+#### Support for Qualified Names
+All actions and triggers have a fully qualified name which is made up of a namespace, a package, and an action/trigger name. The SDK can accept these as parameters when invoking an action or firing a trigger. The SDK also provides a function that accepts a fully qualified name that looks like "/mynamespace/mypackage/nameOfActionOrTrigger". The qualified name string supports unnamed default values for namespaces and packages that all OpenWhisk users have, so the following parsing rules apply:
+
+1. qName = "foo" will result in namespace = default, package = default, action/trigger = "foo"
+2. qName = "mypackage/foo" will result in namespace = default, package = mypackage, action/trigger = "foo"
+3. qName = "/mynamespace/foo" will result in namespace = mynamespace, package = default, action/trigger = "foo"
+4. qName = "/mynamespace/mypackage/foo will result in namespace = mynamespace, package = mypackage, action/trigger = "foo" 
+
+All other combinations will throw a WhiskError.QualifiedName error. When using qualified names, you must wrap the call in a do/try/catch block.
+
+#### SDK Button
+For convenience, the iOS version of the SDK includes a WhiskButton, which extends the UIButton to allow it to invoke OpenWhisk actions.  To use this:
+
+```
+var whiskButton = WhiskButton(frame: CGRectMake(0,0,20,20))
+
+whiskButton.setupWhiskAction("helloConsole", package: "mypackage", namespace: "_", credentials: credentialsConfiguration!, hasResult: false, parameters: nil, urlSession: nil)
+
+let myParams = ["name":"value"]
+
+// Call this when you detect a press event, e.g. in an IBAction, to invoke the action 
+whiskButton.invokeAction(parameters: myParams, callback: { reply, error in
+    if let error = error {
+        print("Oh no, error: \(error)")
+    } else {
+        print("Success: \(reply)")
+    }
+})
+
+// or alternatively you can setup a "self contained" button that listens for press events on itself and invokes an action
+
+var whiskButtonSelfContained = WhiskButton(frame: CGRectMake(0,0,20,20))
+whiskButtonSelfContained.listenForPressEvents = true
+do { 
+
+   // use qualified name API which requires do/try/catch
+   try whiskButtonSelfContained.setupWhiskAction("mypackage/helloConsole", credentials: credentialsConfiguration!, hasResult: false, parameters: nil, urlSession: nil)
+   whiskButtonSelfContained.actionButtonCallback = { reply, error in
+
+       if let error = error {
+           print("Oh no, error: \(error)")
+       } else {
+           print("Success: \(reply)")
+       }
+   }
+} catch {
+   print("Error setting up button \(error)")
+}
+
+```
+