diff --git a/.gitignore b/.gitignore
index 0a2493df..dc546ee3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,11 +1,9 @@
.DS_Store
-/.build
-/Packages
-/*.xcodeproj
-Package.pins
+*.build
+/.xcodeproj
*.pem
-/docs
-Package.resolved
.podspecs
.swiftpm
.swift-version
+xcuserdata
+Package.resolved
diff --git a/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/contents.xcworkspacedata b/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 00000000..92112301
--- /dev/null
+++ b/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
diff --git a/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 00000000..18d98100
--- /dev/null
+++ b/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 00000000..f9b0d7c5
--- /dev/null
+++ b/Examples/EndToEndDebugging/EndToEndExample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp.xcodeproj/project.pbxproj b/Examples/EndToEndDebugging/MyApp/MyApp.xcodeproj/project.pbxproj
new file mode 100644
index 00000000..e8de1fb5
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp.xcodeproj/project.pbxproj
@@ -0,0 +1,345 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ F7B6C1FE246121E800607A89 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B6C1FD246121E800607A89 /* AppDelegate.swift */; };
+ F7B6C200246121E800607A89 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B6C1FF246121E800607A89 /* SceneDelegate.swift */; };
+ F7B6C202246121E800607A89 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7B6C201246121E800607A89 /* ContentView.swift */; };
+ F7B6C204246121E900607A89 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7B6C203246121E900607A89 /* Assets.xcassets */; };
+ F7B6C207246121E900607A89 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F7B6C206246121E900607A89 /* Preview Assets.xcassets */; };
+ F7B6C20A246121E900607A89 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = F7B6C208246121E900607A89 /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+ F7B6C1FA246121E800607A89 /* MyApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MyApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ F7B6C1FD246121E800607A89 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ F7B6C1FF246121E800607A89 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
+ F7B6C201246121E800607A89 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
+ F7B6C203246121E900607A89 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ F7B6C206246121E900607A89 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
+ F7B6C209246121E900607A89 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ F7B6C20B246121E900607A89 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ F7B6C1F7246121E800607A89 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ F7B6C1F1246121E800607A89 = {
+ isa = PBXGroup;
+ children = (
+ F7B6C1FC246121E800607A89 /* MyApp */,
+ F7B6C1FB246121E800607A89 /* Products */,
+ );
+ sourceTree = "";
+ };
+ F7B6C1FB246121E800607A89 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ F7B6C1FA246121E800607A89 /* MyApp.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ F7B6C1FC246121E800607A89 /* MyApp */ = {
+ isa = PBXGroup;
+ children = (
+ F7B6C1FD246121E800607A89 /* AppDelegate.swift */,
+ F7B6C1FF246121E800607A89 /* SceneDelegate.swift */,
+ F7B6C201246121E800607A89 /* ContentView.swift */,
+ F7B6C203246121E900607A89 /* Assets.xcassets */,
+ F7B6C208246121E900607A89 /* LaunchScreen.storyboard */,
+ F7B6C20B246121E900607A89 /* Info.plist */,
+ F7B6C205246121E900607A89 /* Preview Content */,
+ );
+ path = MyApp;
+ sourceTree = "";
+ };
+ F7B6C205246121E900607A89 /* Preview Content */ = {
+ isa = PBXGroup;
+ children = (
+ F7B6C206246121E900607A89 /* Preview Assets.xcassets */,
+ );
+ path = "Preview Content";
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ F7B6C1F9246121E800607A89 /* MyApp */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = F7B6C20E246121E900607A89 /* Build configuration list for PBXNativeTarget "MyApp" */;
+ buildPhases = (
+ F7B6C1F6246121E800607A89 /* Sources */,
+ F7B6C1F7246121E800607A89 /* Frameworks */,
+ F7B6C1F8246121E800607A89 /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = MyApp;
+ productName = MyApp;
+ productReference = F7B6C1FA246121E800607A89 /* MyApp.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ F7B6C1F2246121E800607A89 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 1140;
+ LastUpgradeCheck = 1140;
+ ORGANIZATIONNAME = "Tom Doron";
+ TargetAttributes = {
+ F7B6C1F9246121E800607A89 = {
+ CreatedOnToolsVersion = 11.4.1;
+ };
+ };
+ };
+ buildConfigurationList = F7B6C1F5246121E800607A89 /* Build configuration list for PBXProject "MyApp" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = F7B6C1F1246121E800607A89;
+ productRefGroup = F7B6C1FB246121E800607A89 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ F7B6C1F9246121E800607A89 /* MyApp */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ F7B6C1F8246121E800607A89 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7B6C20A246121E900607A89 /* LaunchScreen.storyboard in Resources */,
+ F7B6C207246121E900607A89 /* Preview Assets.xcassets in Resources */,
+ F7B6C204246121E900607A89 /* Assets.xcassets in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ F7B6C1F6246121E800607A89 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ F7B6C1FE246121E800607A89 /* AppDelegate.swift in Sources */,
+ F7B6C200246121E800607A89 /* SceneDelegate.swift in Sources */,
+ F7B6C202246121E800607A89 /* ContentView.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ F7B6C208246121E900607A89 /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ F7B6C209246121E900607A89 /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ F7B6C20C246121E900607A89 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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 = 13.4;
+ MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+ MTL_FAST_MATH = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ F7B6C20D246121E900607A89 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ 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 = 13.4;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ MTL_FAST_MATH = YES;
+ SDKROOT = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ F7B6C20F246121E900607A89 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"MyApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ INFOPLIST_FILE = MyApp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.apple.swift.MyApp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ F7B6C210246121E900607A89 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_STYLE = Automatic;
+ DEVELOPMENT_ASSET_PATHS = "\"MyApp/Preview Content\"";
+ ENABLE_PREVIEWS = YES;
+ INFOPLIST_FILE = MyApp/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/Frameworks",
+ );
+ PRODUCT_BUNDLE_IDENTIFIER = com.apple.swift.MyApp;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ F7B6C1F5246121E800607A89 /* Build configuration list for PBXProject "MyApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F7B6C20C246121E900607A89 /* Debug */,
+ F7B6C20D246121E900607A89 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ F7B6C20E246121E900607A89 /* Build configuration list for PBXNativeTarget "MyApp" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ F7B6C20F246121E900607A89 /* Debug */,
+ F7B6C210246121E900607A89 /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = F7B6C1F2246121E800607A89 /* Project object */;
+}
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp.xcodeproj/xcshareddata/xcschemes/MyApp.xcscheme b/Examples/EndToEndDebugging/MyApp/MyApp.xcodeproj/xcshareddata/xcschemes/MyApp.xcscheme
new file mode 100644
index 00000000..dc471464
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp.xcodeproj/xcshareddata/xcschemes/MyApp.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/AppDelegate.swift b/Examples/EndToEndDebugging/MyApp/MyApp/AppDelegate.swift
new file mode 100644
index 00000000..068d2b7a
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/AppDelegate.swift
@@ -0,0 +1,37 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import UIKit
+
+@UIApplicationMain
+class AppDelegate: UIResponder, UIApplicationDelegate {
+ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
+ // Override point for customization after application launch.
+ true
+ }
+
+ // MARK: UISceneSession Lifecycle
+
+ func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
+ // Called when a new scene session is being created.
+ // Use this method to select a configuration to create the new scene with.
+ UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
+ }
+
+ func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
+ // Called when the user discards a scene session.
+ // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
+ // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
+ }
+}
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/Examples/EndToEndDebugging/MyApp/MyApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 00000000..9221b9bb
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "2x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "iphone",
+ "scale" : "3x",
+ "size" : "60x60"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "20x20"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "29x29"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "40x40"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "1x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "76x76"
+ },
+ {
+ "idiom" : "ipad",
+ "scale" : "2x",
+ "size" : "83.5x83.5"
+ },
+ {
+ "idiom" : "ios-marketing",
+ "scale" : "1x",
+ "size" : "1024x1024"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/Assets.xcassets/Contents.json b/Examples/EndToEndDebugging/MyApp/MyApp/Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/Base.lproj/LaunchScreen.storyboard b/Examples/EndToEndDebugging/MyApp/MyApp/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 00000000..865e9329
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/ContentView.swift b/Examples/EndToEndDebugging/MyApp/MyApp/ContentView.swift
new file mode 100644
index 00000000..c73d13ef
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/ContentView.swift
@@ -0,0 +1,79 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import SwiftUI
+
+struct ContentView: View {
+ @State var name: String = "World"
+ @State var response: String = ""
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 20) {
+ TextField("Enter your name", text: $name)
+ Button(
+ action: self.sayHello,
+ label: {
+ Text("say hello")
+ .padding()
+ .foregroundColor(.white)
+ .background(Color.black)
+ .border(Color.black, width: 2)
+ }
+ )
+ Text(response)
+ .foregroundColor(response.starts(with: "CommunicationError") ? .red : .black)
+ }.padding(100)
+ }
+
+ func sayHello() {
+ guard let url = URL(string: "http://localhost:7000/invoke") else {
+ fatalError("invalid url")
+ }
+
+ var request = URLRequest(url: url)
+ request.httpMethod = "POST"
+ request.httpBody = self.name.data(using: .utf8)
+
+ let task = URLSession.shared.dataTask(with: request as URLRequest) { data, response, error in
+ do {
+ if let error = error {
+ throw CommunicationError(reason: error.localizedDescription)
+ }
+ guard let httpResponse = response as? HTTPURLResponse else {
+ throw CommunicationError(reason: "invalid response, expected HTTPURLResponse")
+ }
+ guard httpResponse.statusCode == 200 else {
+ throw CommunicationError(reason: "invalid response code: \(httpResponse.statusCode)")
+ }
+ guard let data = data else {
+ throw CommunicationError(reason: "invald response, empty body")
+ }
+ self.response = String(data: data, encoding: .utf8)!
+ } catch {
+ self.response = "\(error)"
+ }
+ }
+ task.resume()
+ }
+}
+
+struct ContentView_Previews: PreviewProvider {
+ static var previews: some View {
+ ContentView()
+ }
+}
+
+struct CommunicationError: Error {
+ let reason: String
+}
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/Info.plist b/Examples/EndToEndDebugging/MyApp/MyApp/Info.plist
new file mode 100644
index 00000000..9742bf0f
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/Info.plist
@@ -0,0 +1,60 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ $(PRODUCT_BUNDLE_PACKAGE_TYPE)
+ CFBundleShortVersionString
+ 1.0
+ CFBundleVersion
+ 1
+ LSRequiresIPhoneOS
+
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneConfigurationName
+ Default Configuration
+ UISceneDelegateClassName
+ $(PRODUCT_MODULE_NAME).SceneDelegate
+
+
+
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIRequiredDeviceCapabilities
+
+ armv7
+
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+
+
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/Preview Content/Preview Assets.xcassets/Contents.json b/Examples/EndToEndDebugging/MyApp/MyApp/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100644
index 00000000..73c00596
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/Preview Content/Preview Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/Examples/EndToEndDebugging/MyApp/MyApp/SceneDelegate.swift b/Examples/EndToEndDebugging/MyApp/MyApp/SceneDelegate.swift
new file mode 100644
index 00000000..71e700d4
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyApp/MyApp/SceneDelegate.swift
@@ -0,0 +1,65 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import SwiftUI
+import UIKit
+
+class SceneDelegate: UIResponder, UIWindowSceneDelegate {
+ var window: UIWindow?
+
+ func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
+ // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
+ // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
+ // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
+
+ // Create the SwiftUI view that provides the window contents.
+ let contentView = ContentView()
+
+ // Use a UIHostingController as window root view controller.
+ if let windowScene = scene as? UIWindowScene {
+ let window = UIWindow(windowScene: windowScene)
+ window.rootViewController = UIHostingController(rootView: contentView)
+ self.window = window
+ window.makeKeyAndVisible()
+ }
+ }
+
+ func sceneDidDisconnect(_: UIScene) {
+ // Called as the scene is being released by the system.
+ // This occurs shortly after the scene enters the background, or when its session is discarded.
+ // Release any resources associated with this scene that can be re-created the next time the scene connects.
+ // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
+ }
+
+ func sceneDidBecomeActive(_: UIScene) {
+ // Called when the scene has moved from an inactive state to an active state.
+ // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
+ }
+
+ func sceneWillResignActive(_: UIScene) {
+ // Called when the scene will move from an active state to an inactive state.
+ // This may occur due to temporary interruptions (ex. an incoming phone call).
+ }
+
+ func sceneWillEnterForeground(_: UIScene) {
+ // Called as the scene transitions from the background to the foreground.
+ // Use this method to undo the changes made on entering the background.
+ }
+
+ func sceneDidEnterBackground(_: UIScene) {
+ // Called as the scene transitions from the foreground to the background.
+ // Use this method to save data, release shared resources, and store enough scene-specific state information
+ // to restore the scene back to its current state.
+ }
+}
diff --git a/Examples/EndToEndDebugging/MyLambda/.dockerignore b/Examples/EndToEndDebugging/MyLambda/.dockerignore
new file mode 100644
index 00000000..24e5b0a1
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/.dockerignore
@@ -0,0 +1 @@
+.build
diff --git a/Examples/EndToEndDebugging/MyLambda/Dockerfile b/Examples/EndToEndDebugging/MyLambda/Dockerfile
new file mode 100644
index 00000000..c4db2a61
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/Dockerfile
@@ -0,0 +1,3 @@
+FROM swiftlang/swift:nightly-master-amazonlinux2
+
+RUN yum -y install git zip
diff --git a/Examples/EndToEndDebugging/MyLambda/Package.swift b/Examples/EndToEndDebugging/MyLambda/Package.swift
new file mode 100644
index 00000000..c35f2a3c
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/Package.swift
@@ -0,0 +1,26 @@
+// swift-tools-version:5.2
+
+import PackageDescription
+
+let package = Package(
+ name: "MyLambda",
+ platforms: [
+ .macOS(.v10_13),
+ ],
+ products: [
+ .executable(name: "MyLambda", targets: ["MyLambda"]),
+ ],
+ dependencies: [
+ // this is the dependency on the swift-aws-lambda-runtime library
+ // in real-world projects this would say
+ // .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0")
+ .package(name: "swift-aws-lambda-runtime", path: "../../.."),
+ ],
+ targets: [
+ .target(
+ name: "MyLambda", dependencies: [
+ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
+ ]
+ ),
+ ]
+)
diff --git a/Examples/EndToEndDebugging/MyLambda/Sources/MyLambda/main.swift b/Examples/EndToEndDebugging/MyLambda/Sources/MyLambda/main.swift
new file mode 100644
index 00000000..45bd8d83
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/Sources/MyLambda/main.swift
@@ -0,0 +1,21 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import AWSLambdaRuntime
+
+try Lambda.withLocalServer {
+ Lambda.run { (_: Lambda.Context, payload: String, callback: @escaping (Result) -> Void) in
+ callback(.success("Hello, \(payload)!"))
+ }
+}
diff --git a/Examples/EndToEndDebugging/MyLambda/Tests/LinuxMain.swift b/Examples/EndToEndDebugging/MyLambda/Tests/LinuxMain.swift
new file mode 100644
index 00000000..c46de763
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/Tests/LinuxMain.swift
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+preconditionFailure("use `swift test --enable-test-discovery`")
diff --git a/Examples/EndToEndDebugging/MyLambda/scripts/deploy.sh b/Examples/EndToEndDebugging/MyLambda/scripts/deploy.sh
new file mode 100755
index 00000000..8c137d60
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/scripts/deploy.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+##===----------------------------------------------------------------------===##
+##
+## This source file is part of the SwiftAWSLambdaRuntime open source project
+##
+## Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+## Licensed under Apache License v2.0
+##
+## See LICENSE.txt for license information
+## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+##
+## SPDX-License-Identifier: Apache-2.0
+##
+##===----------------------------------------------------------------------===##
+
+set -eu
+
+lambda_name=SwiftSample
+s3_bucket=swift-lambda-test
+executables=( $(swift package dump-package | sed -e 's|: null|: ""|g' | jq '.products[] | (select(.type.executable)) | .name' | sed -e 's|"||g') )
+
+if [[ ${#executables[@]} = 0 ]]; then
+ echo "no executables found"
+ exit 1
+elif [[ ${#executables[@]} = 1 ]]; then
+ executable=${executables[0]}
+elif [[ ${#executables[@]} > 1 ]]; then
+ echo "multiple executables found:"
+ for executable in ${executables[@]}; do
+ echo " * $executable"
+ done
+ echo ""
+ read -p "select which executables to deploy: " executable
+fi
+
+echo -e "\ndeploying $executable"
+
+echo "-------------------------------------------------------------------------"
+echo "preparing docker build image"
+echo "-------------------------------------------------------------------------"
+docker build . -t builder
+
+echo "-------------------------------------------------------------------------"
+echo "building \"$executable\" lambda"
+echo "-------------------------------------------------------------------------"
+docker run --rm -v `pwd`/../../..:/workspace -w /workspace builder \
+ bash -cl "cd Examples/EndToEndDebugging/MyLambda &&
+ swift build --product $executable -c release -Xswiftc -g"
+echo "done"
+
+echo "-------------------------------------------------------------------------"
+echo "packaging \"$executable\" lambda"
+echo "-------------------------------------------------------------------------"
+docker run --rm -v `pwd`:/workspace -w /workspace builder bash -cl "./scripts/package.sh $executable"
+
+echo "-------------------------------------------------------------------------"
+echo "uploading \"$executable\" lambda to s3"
+echo "-------------------------------------------------------------------------"
+
+aws s3 cp .build/lambda/$executable/lambda.zip s3://$s3_bucket/
+
+echo "-------------------------------------------------------------------------"
+echo "updating \"$lambda_name\" to latest \"$executable\""
+echo "-------------------------------------------------------------------------"
+aws lambda update-function-code --function $lambda_name --s3-bucket $s3_bucket --s3-key lambda.zip
diff --git a/Examples/EndToEndDebugging/MyLambda/scripts/package.sh b/Examples/EndToEndDebugging/MyLambda/scripts/package.sh
new file mode 100755
index 00000000..190be3f8
--- /dev/null
+++ b/Examples/EndToEndDebugging/MyLambda/scripts/package.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+##===----------------------------------------------------------------------===##
+##
+## This source file is part of the SwiftAWSLambdaRuntime open source project
+##
+## Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+## Licensed under Apache License v2.0
+##
+## See LICENSE.txt for license information
+## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+##
+## SPDX-License-Identifier: Apache-2.0
+##
+##===----------------------------------------------------------------------===##
+
+set -eu
+
+executable=$1
+
+target=.build/lambda/$executable
+rm -rf "$target"
+mkdir -p "$target"
+cp ".build/release/$executable" "$target/"
+cp -Pv /usr/lib/swift/linux/lib*so* "$target"
+cd "$target"
+ln -s "$executable" "bootstrap"
+zip --symlinks lambda.zip *
diff --git a/Examples/EndToEndDebugging/README.md b/Examples/EndToEndDebugging/README.md
new file mode 100644
index 00000000..676125a3
--- /dev/null
+++ b/Examples/EndToEndDebugging/README.md
@@ -0,0 +1,8 @@
+# End-To-End Debugging
+
+This sample project demonstrates how to write a simple Lambda function in Swift,
+and how to use local debugging techniques that emulate how the Lambda function
+would be invoked by the AWS Lambda Runtime engine.
+
+The sample also includes a simple iOS application that calls the Lambda function
+through the emulator, providing an end-to-end debugging experience.
diff --git a/Examples/LambdaFunctions/.dockerignore b/Examples/LambdaFunctions/.dockerignore
new file mode 100644
index 00000000..24e5b0a1
--- /dev/null
+++ b/Examples/LambdaFunctions/.dockerignore
@@ -0,0 +1 @@
+.build
diff --git a/Examples/LambdaFunctions/APIGateway-template.yml b/Examples/LambdaFunctions/APIGateway-template.yml
new file mode 100644
index 00000000..4861b60c
--- /dev/null
+++ b/Examples/LambdaFunctions/APIGateway-template.yml
@@ -0,0 +1,33 @@
+AWSTemplateFormatVersion : '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: A sample SAM template for deploying Lambda functions.
+
+Resources:
+# APIGateway Function
+ apiGatewayFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: Provided
+ Runtime: provided
+ CodeUri: .build/lambda/APIGateway/lambda.zip
+# Add an API Gateway event source for the Lambda
+ Events:
+ HttpGet:
+ Type: Api
+ Properties:
+ RestApiId: !Ref lambdaApiGateway
+ Path: '/samples/apig'
+ Method: get
+# Instructs new versions to be published to an alias named "live".
+ AutoPublishAlias: live
+
+ lambdaApiGateway:
+ Type: AWS::Serverless::Api
+ Properties:
+ Name: Lambda API Gateway
+ StageName: Beta
+
+Outputs:
+ LambdaApiGatewayEndpoint:
+ Description: 'API Gateway endpoint URL.'
+ Value: !Sub 'https://${lambdaApiGateway}.execute-api.${AWS::Region}.amazonaws.com/Beta/samples/apig'
diff --git a/Examples/LambdaFunctions/Benchmark-template.yml b/Examples/LambdaFunctions/Benchmark-template.yml
new file mode 100644
index 00000000..38941a53
--- /dev/null
+++ b/Examples/LambdaFunctions/Benchmark-template.yml
@@ -0,0 +1,14 @@
+AWSTemplateFormatVersion : '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: A sample SAM template for deploying Lambda functions.
+
+Resources:
+# Benchmark Function
+ benchmarkFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: Provided
+ Runtime: provided
+ CodeUri: .build/lambda/Benchmark/lambda.zip
+# Instructs new versions to be published to an alias named "live".
+ AutoPublishAlias: live
diff --git a/Examples/LambdaFunctions/CurrencyExchange-template.yml b/Examples/LambdaFunctions/CurrencyExchange-template.yml
new file mode 100644
index 00000000..de09cd4e
--- /dev/null
+++ b/Examples/LambdaFunctions/CurrencyExchange-template.yml
@@ -0,0 +1,15 @@
+AWSTemplateFormatVersion : '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: A sample SAM template for deploying Lambda functions.
+
+Resources:
+# CurrencyExchange Function
+ currencyExchangeFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: Provided
+ Runtime: provided
+ CodeUri: .build/lambda/CurrencyExchange/lambda.zip
+ Timeout: 300
+# Instructs new versions to be published to an alias named "live".
+ AutoPublishAlias: live
diff --git a/Examples/LambdaFunctions/Dockerfile b/Examples/LambdaFunctions/Dockerfile
new file mode 100644
index 00000000..c4db2a61
--- /dev/null
+++ b/Examples/LambdaFunctions/Dockerfile
@@ -0,0 +1,3 @@
+FROM swiftlang/swift:nightly-master-amazonlinux2
+
+RUN yum -y install git zip
diff --git a/Examples/LambdaFunctions/ErrorHandling-template.yml b/Examples/LambdaFunctions/ErrorHandling-template.yml
new file mode 100644
index 00000000..dba76b8e
--- /dev/null
+++ b/Examples/LambdaFunctions/ErrorHandling-template.yml
@@ -0,0 +1,14 @@
+AWSTemplateFormatVersion : '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: A sample SAM template for deploying Lambda functions.
+
+Resources:
+# ErrorHandling Function
+ errorHandlingFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: Provided
+ Runtime: provided
+ CodeUri: .build/lambda/ErrorHandling/lambda.zip
+# Instructs new versions to be published to an alias named "live".
+ AutoPublishAlias: live
diff --git a/Examples/LambdaFunctions/HelloWorld-template.yml b/Examples/LambdaFunctions/HelloWorld-template.yml
new file mode 100644
index 00000000..7eddfbe7
--- /dev/null
+++ b/Examples/LambdaFunctions/HelloWorld-template.yml
@@ -0,0 +1,14 @@
+AWSTemplateFormatVersion : '2010-09-09'
+Transform: AWS::Serverless-2016-10-31
+Description: A sample SAM template for deploying Lambda functions.
+
+Resources:
+# HelloWorld Function
+ helloWorldFunction:
+ Type: AWS::Serverless::Function
+ Properties:
+ Handler: Provided
+ Runtime: provided
+ CodeUri: .build/lambda/HelloWorld/lambda.zip
+# Instructs new versions to be published to an alias named "live".
+ AutoPublishAlias: live
diff --git a/Examples/LambdaFunctions/Package.swift b/Examples/LambdaFunctions/Package.swift
new file mode 100644
index 00000000..5a94f362
--- /dev/null
+++ b/Examples/LambdaFunctions/Package.swift
@@ -0,0 +1,45 @@
+// swift-tools-version:5.2
+
+import PackageDescription
+
+let package = Package(
+ name: "swift-aws-lambda-runtime-samples",
+ platforms: [
+ .macOS(.v10_13),
+ ],
+ products: [
+ // introductory example
+ .executable(name: "HelloWorld", targets: ["HelloWorld"]),
+ // good for benchmarking
+ .executable(name: "Benchmark", targets: ["Benchmark"]),
+ // demonstrate different types of error handling
+ .executable(name: "ErrorHandling", targets: ["ErrorHandling"]),
+ // demostrate how to integrate with AWS API Gateway
+ .executable(name: "APIGateway", targets: ["APIGateway"]),
+ // fully featured example with domain specific business logic
+ .executable(name: "CurrencyExchange", targets: ["CurrencyExchange"]),
+ ],
+ dependencies: [
+ // this is the dependency on the swift-aws-lambda-runtime library
+ // in real-world projects this would say
+ // .package(url: "https://github.com/swift-server/swift-aws-lambda-runtime.git", from: "1.0.0")
+ .package(name: "swift-aws-lambda-runtime", path: "../.."),
+ ],
+ targets: [
+ .target(name: "HelloWorld", dependencies: [
+ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
+ ]),
+ .target(name: "Benchmark", dependencies: [
+ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
+ ]),
+ .target(name: "ErrorHandling", dependencies: [
+ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
+ ]),
+ .target(name: "APIGateway", dependencies: [
+ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
+ ]),
+ .target(name: "CurrencyExchange", dependencies: [
+ .product(name: "AWSLambdaRuntime", package: "swift-aws-lambda-runtime"),
+ ]),
+ ]
+)
diff --git a/Examples/LambdaFunctions/README.md b/Examples/LambdaFunctions/README.md
new file mode 100644
index 00000000..dda328a9
--- /dev/null
+++ b/Examples/LambdaFunctions/README.md
@@ -0,0 +1,81 @@
+# Lambda Functions Examples
+
+This sample project is a collection of Lambda functions that demonstrates
+how to write a simple Lambda function in Swift, and how to package and deploy it
+to the AWS Lambda platform.
+
+## Deployment instructions using AWS CLI
+
+Steps to deploy this sample to AWS Lambda using the AWS CLI:
+
+1. Login to AWS Console and create an AWS Lambda with the following settings:
+ * Runtime: Custom runtime
+ * Handler: Can be any string, does not matter in this case
+
+2. Build, package and deploy the Lambda
+
+ ```
+ ./scripts/deploy.sh
+ ```
+
+ Note: This script assumes you have AWS CLI installed and credentials setup in `~/.aws/credentials`.
+
+### Deployment instructions using AWS SAM (Serverless Application Model)
+
+AWS [Serverless Application Model](https://aws.amazon.com/serverless/sam/) (SAM) is an open-source framework for building serverless applications. This framework allows you to easily deploy other AWS resources and more complex deployment mechanisms such a CI pipelines.
+
+***Note:*** Deploying using SAM will automatically create resources within your AWS account. Charges may apply for these resources.
+
+To use SAM to deploy this sample to AWS:
+
+1. Install the AWS CLI by following the [instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-install.html).
+
+2. Install SAM CLI by following the [instructions](https://aws.amazon.com/serverless/sam/).
+
+3. Build, package and deploy the Lambda
+
+ ```
+ ./scripts/sam-deploy.sh --guided
+ ```
+
+The script will ask you which sample Lambda you wish to deploy. It will then guide you through the SAM setup process.
+
+ ```
+ Setting default arguments for 'sam deploy'
+ =========================================
+ Stack Name [sam-app]: swift-aws-lambda-runtime-sample
+ AWS Region [us-east-1]:
+ #Shows you resources changes to be deployed and require a 'Y' to initiate deploy
+ Confirm changes before deploy [y/N]: Y
+ #SAM needs permission to be able to create roles to connect to the resources in your template
+ Allow SAM CLI IAM role creation [Y/n]: Y
+ Save arguments to samconfig.toml [Y/n]: Y
+ ```
+
+If you said yes to confirm changes, SAM will ask you to accept changes to the infrastructure you are setting up. For more on this, see [Cloud Formation](https://aws.amazon.com/cloudformation/).
+
+The `sam-deploy` script passes through any parameters to the SAM deploy command.
+
+4. Subsequent deploys can just use the command minus the `guided` parameter:
+
+ ```
+ ./scripts/sam-deploy.sh
+ ```
+
+The script will ask you which sample Lambda you wish to deploy. If you are deploying a different sample lambda, the deploy process will pull down the previous Lambda.
+
+SAM will still ask you to confirm changes if you said yes to that initially.
+
+5. Testing
+
+For the API Gateway sample:
+
+The SAM template will provide an output labelled `LambdaApiGatewayEndpoint` which you can use to test the Lambda. For example:
+
+ ```
+ curl <>
+ ```
+
+***Warning:*** This SAM template is only intended as a sample and creates a publicly accessible HTTP endpoint.
+
+For all other samples use the AWS Lambda console.
diff --git a/Examples/LambdaFunctions/Sources/APIGateway/main.swift b/Examples/LambdaFunctions/Sources/APIGateway/main.swift
new file mode 100644
index 00000000..e3606640
--- /dev/null
+++ b/Examples/LambdaFunctions/Sources/APIGateway/main.swift
@@ -0,0 +1,85 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import AWSLambdaRuntime
+import NIO
+
+// MARK: - Run Lambda
+
+Lambda.run(APIGatewayProxyLambda())
+
+// MARK: - Handler, Request and Response
+
+// FIXME: Use proper Event abstractions once added to AWSLambdaRuntime
+struct APIGatewayProxyLambda: EventLoopLambdaHandler {
+ public typealias In = APIGatewayRequest
+ public typealias Out = APIGatewayResponse
+
+ public func handle(context: Lambda.Context, payload: APIGatewayRequest) -> EventLoopFuture {
+ context.logger.debug("hello, api gateway!")
+ return context.eventLoop.makeSucceededFuture(APIGatewayResponse(statusCode: 200,
+ headers: nil,
+ multiValueHeaders: nil,
+ body: "hello, world!",
+ isBase64Encoded: false))
+ }
+}
+
+struct APIGatewayRequest: Codable {
+ let resource: String
+ let path: String
+ let httpMethod: String?
+ let headers: [String: String]?
+ let multiValueHeaders: [String: [String]]?
+ let queryStringParameters: [String: String]?
+ let multiValueQueryStringParameters: [String: [String]]?
+ let pathParameters: [String: String]?
+ let stageVariables: [String: String]?
+ let requestContext: Context?
+ let body: String?
+ let isBase64Encoded: Bool?
+
+ struct Context: Codable {
+ let accountId: String?
+ let resourceId: String?
+ let stage: String?
+ let requestId: String?
+ let identity: Identity?
+ let resourcePath: String?
+ let httpMethod: String?
+ let apiId: String
+ }
+
+ struct Identity: Codable {
+ let cognitoIdentityPoolId: String?
+ let accountId: String?
+ let cognitoIdentityId: String?
+ let caller: String?
+ let apiKey: String?
+ let sourceIp: String?
+ let cognitoAuthenticationType: String?
+ let cognitoAuthenticationProvider: String?
+ let userArn: String?
+ let userAgent: String?
+ let user: String?
+ }
+}
+
+struct APIGatewayResponse: Codable {
+ let statusCode: Int
+ let headers: [String: String]?
+ let multiValueHeaders: [String: [String]]?
+ let body: String?
+ let isBase64Encoded: Bool?
+}
diff --git a/Examples/LambdaFunctions/Sources/Benchmark/main.swift b/Examples/LambdaFunctions/Sources/Benchmark/main.swift
new file mode 100644
index 00000000..2a61d9d3
--- /dev/null
+++ b/Examples/LambdaFunctions/Sources/Benchmark/main.swift
@@ -0,0 +1,31 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import AWSLambdaRuntime
+import NIO
+
+// If you would like to benchmark Swift's Lambda Runtime,
+// use this example which is more performant.
+// `EventLoopLambdaHandler` does not offload the Lambda processing to a separate thread
+// while the closure-based handlers do.
+Lambda.run(BenchmarkHandler())
+
+struct BenchmarkHandler: EventLoopLambdaHandler {
+ typealias In = String
+ typealias Out = String
+
+ func handle(context: Lambda.Context, payload: String) -> EventLoopFuture {
+ context.eventLoop.makeSucceededFuture("hello, world!")
+ }
+}
diff --git a/Examples/LambdaFunctions/Sources/CurrencyExchange/main.swift b/Examples/LambdaFunctions/Sources/CurrencyExchange/main.swift
new file mode 100644
index 00000000..22c30563
--- /dev/null
+++ b/Examples/LambdaFunctions/Sources/CurrencyExchange/main.swift
@@ -0,0 +1,247 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import AWSLambdaRuntime
+import Dispatch
+import Foundation
+#if canImport(FoundationNetworking) && canImport(FoundationXML)
+import FoundationNetworking
+import FoundationXML
+#endif
+import Logging
+
+// MARK: - Run Lambda
+
+Lambda.run { (context: Lambda.Context, _: Request, callback: @escaping (Result<[Exchange], Error>) -> Void) in
+ let calculator = ExchangeRatesCalculator()
+ calculator.run(logger: context.logger, callback: callback)
+}
+
+// MARK: - Business Logic
+
+// This is a contrived example performing currency exchange rate lookup and conversion using URLSession and XML parsing
+struct ExchangeRatesCalculator {
+ static let currencies = ["EUR", "USD", "JPY"]
+ static let currenciesEmojies = [
+ "EUR": "💶",
+ "JPY": "💴",
+ "USD": "💵",
+ ]
+
+ let locale: Locale
+ let calendar: Calendar
+
+ init() {
+ // This is data from HMRC, the UK tax authority. Therefore we want to use their locale when interpreting data from the server.
+ self.locale = Locale(identifier: "en_GB")
+ // Use the UK calendar, not the system one.
+ var calendar = self.locale.calendar
+ calendar.timeZone = TimeZone(identifier: "UTC")!
+ self.calendar = calendar
+ }
+
+ func run(logger: Logger, callback: @escaping (Result<[Exchange], Swift.Error>) -> Void) {
+ let startDate = Date()
+ let months = (1 ... 12).map {
+ self.calendar.date(byAdding: DateComponents(month: -$0), to: startDate)!
+ }
+
+ self.download(logger: logger,
+ months: months,
+ monthIndex: months.startIndex,
+ currencies: Self.currencies,
+ state: [:]) { result in
+
+ switch result {
+ case .failure(let error):
+ return callback(.failure(error))
+ case .success(let downloadedDataByMonth):
+ logger.debug("Downloads complete")
+
+ var result = [Exchange]()
+ var previousData: [String: Decimal?] = [:]
+ for (_, exchangeRateData) in downloadedDataByMonth.filter({ $1.period != nil }).sorted(by: { $0.key < $1.key }) {
+ for (currencyCode, rate) in exchangeRateData.ratesByCurrencyCode.sorted(by: { $0.key < $1.key }) {
+ if let rate = rate, let currencyEmoji = Self.currenciesEmojies[currencyCode] {
+ let change: Exchange.Change
+ switch previousData[currencyCode] {
+ case .some(.some(let previousRate)) where rate > previousRate:
+ change = .up
+ case .some(.some(let previousRate)) where rate < previousRate:
+ change = .down
+ case .some(.some(let previousRate)) where rate == previousRate:
+ change = .none
+ default:
+ change = .unknown
+ }
+ result.append(Exchange(date: exchangeRateData.period!.start,
+ from: .init(symbol: "GBP", emoji: "💷"),
+ to: .init(symbol: currencyCode, emoji: currencyEmoji),
+ rate: rate,
+ change: change))
+ }
+ }
+ previousData = exchangeRateData.ratesByCurrencyCode
+ }
+ callback(.success(result))
+ }
+ }
+ }
+
+ private func download(logger: Logger,
+ months: [Date],
+ monthIndex: Array.Index,
+ currencies: [String],
+ state: [Date: ExchangeRates],
+ callback: @escaping ((Result<[Date: ExchangeRates], Swift.Error>) -> Void)) {
+ if monthIndex == months.count {
+ return callback(.success(state))
+ }
+
+ var newState = state
+
+ let month = months[monthIndex]
+ let url = self.exchangeRatesURL(forMonthContaining: month)
+ logger.debug("requesting exchange rate from \(url)")
+ let dataTask = URLSession.shared.dataTask(with: url) { data, _, error in
+ do {
+ guard let data = data else {
+ throw error!
+ }
+ let exchangeRates = try self.parse(data: data, currencyCodes: Set(currencies))
+ newState[month] = exchangeRates
+ logger.debug("Finished downloading month: \(month)")
+ if let period = exchangeRates.period {
+ logger.debug("Got data covering period: \(period)")
+ }
+ } catch {
+ return callback(.failure(error))
+ }
+ self.download(logger: logger,
+ months: months,
+ monthIndex: monthIndex.advanced(by: 1),
+ currencies: currencies,
+ state: newState,
+ callback: callback)
+ }
+ dataTask.resume()
+ }
+
+ private func parse(data: Data, currencyCodes: Set) throws -> ExchangeRates {
+ let document = try XMLDocument(data: data)
+ let dateFormatter = DateFormatter()
+ dateFormatter.timeZone = TimeZone(identifier: "Etc/UTC")!
+ dateFormatter.dateFormat = "dd/MMM/yy"
+ let interval: DateInterval?
+ if let period = try document.nodes(forXPath: "/exchangeRateMonthList/@Period").first?.stringValue,
+ period.count == 26 {
+ // "01/Sep/2018 to 30/Sep/2018"
+ let startString = period[period.startIndex ..< period.index(period.startIndex, offsetBy: 11)]
+ let to = period[startString.endIndex ..< period.index(startString.endIndex, offsetBy: 4)]
+ let endString = period[to.endIndex ..< period.index(to.endIndex, offsetBy: 11)]
+ if let startDate = dateFormatter.date(from: String(startString)),
+ let startDay = calendar.dateInterval(of: .day, for: startDate),
+ to == " to ",
+ let endDate = dateFormatter.date(from: String(endString)),
+ let endDay = calendar.dateInterval(of: .day, for: endDate) {
+ interval = DateInterval(start: startDay.start, end: endDay.end)
+ } else {
+ interval = nil
+ }
+ } else {
+ interval = nil
+ }
+
+ let ratesByCurrencyCode: [String: Decimal?] = Dictionary(uniqueKeysWithValues: try currencyCodes.map {
+ let xpathCurrency = $0.replacingOccurrences(of: "'", with: "'")
+ if let rateString = try document.nodes(forXPath: "/exchangeRateMonthList/exchangeRate/currencyCode[text()='\(xpathCurrency)']/../rateNew/text()").first?.stringValue,
+ // We must parse the decimal data using the UK locale, not the system one.
+ let rate = Decimal(string: rateString, locale: self.locale) {
+ return ($0, rate)
+ } else {
+ return ($0, nil)
+ }
+ })
+
+ return (period: interval, ratesByCurrencyCode: ratesByCurrencyCode)
+ }
+
+ private func makeUTCDateFormatter(dateFormat: String) -> DateFormatter {
+ let utcTimeZone = TimeZone(identifier: "UTC")!
+ let result = DateFormatter()
+ result.locale = Locale(identifier: "en_US_POSIX")
+ result.timeZone = utcTimeZone
+ result.dateFormat = dateFormat
+ return result
+ }
+
+ private func exchangeRatesURL(forMonthContaining date: Date) -> URL {
+ let exchangeRatesBaseURL = URL(string: "https://www.hmrc.gov.uk/softwaredevelopers/rates")!
+ let dateFormatter = self.makeUTCDateFormatter(dateFormat: "MMyy")
+ return exchangeRatesBaseURL.appendingPathComponent("exrates-monthly-\(dateFormatter.string(from: date)).xml")
+ }
+
+ private typealias ExchangeRates = (period: DateInterval?, ratesByCurrencyCode: [String: Decimal?])
+
+ private struct Error: Swift.Error, CustomStringConvertible {
+ let description: String
+ }
+}
+
+// MARK: - Request and Response
+
+struct Request: Decodable {}
+
+struct Exchange: Encodable {
+ @DateCoding
+ var date: Date
+ let from: Currency
+ let to: Currency
+ let rate: Decimal
+ let change: Change
+
+ struct Currency: Encodable {
+ let symbol: String
+ let emoji: String
+ }
+
+ enum Change: String, Encodable {
+ case up
+ case down
+ case none
+ case unknown
+ }
+
+ @propertyWrapper
+ public struct DateCoding: Encodable {
+ public let wrappedValue: Date
+
+ public init(wrappedValue: Date) {
+ self.wrappedValue = wrappedValue
+ }
+
+ func encode(to encoder: Encoder) throws {
+ let string = Self.dateFormatter.string(from: self.wrappedValue)
+ var container = encoder.singleValueContainer()
+ try container.encode(string)
+ }
+
+ private static var dateFormatter: ISO8601DateFormatter {
+ let dateFormatter = ISO8601DateFormatter()
+ dateFormatter.timeZone = TimeZone(identifier: "UTC")!
+ dateFormatter.formatOptions = [.withYear, .withMonth, .withDashSeparatorInDate]
+ return dateFormatter
+ }
+ }
+}
diff --git a/Examples/LambdaFunctions/Sources/ErrorHandling/main.swift b/Examples/LambdaFunctions/Sources/ErrorHandling/main.swift
new file mode 100644
index 00000000..ab8e04be
--- /dev/null
+++ b/Examples/LambdaFunctions/Sources/ErrorHandling/main.swift
@@ -0,0 +1,101 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import AWSLambdaRuntime
+
+// MARK: - Run Lambda
+
+// switch over the error type "requested" by thr request, and trigger sucg error accordingly
+Lambda.run { (context: Lambda.Context, request: Request, callback: (Result) -> Void) in
+ switch request.error {
+ // no error here!
+ case .none:
+ callback(.success(Response(awsRequestId: context.requestId, requestId: request.requestId, status: .ok)))
+ // trigger a "managed" error - domain specific business logic failure
+ case .managed:
+ callback(.success(Response(awsRequestId: context.requestId, requestId: request.requestId, status: .error)))
+ // trigger an "unmanaged" error - an unexpected Swift Error triggered while processing the request
+ case .unmanaged(let error):
+ callback(.failure(UnmanagedError(description: error)))
+ // trigger a "fatal" error - a panic type error which will crash the process
+ case .fatal:
+ fatalError("crash!")
+ }
+}
+
+// MARK: - Request and Response
+
+struct Request: Codable {
+ let requestId: String
+ let error: Error
+
+ public init(requestId: String, error: Error? = nil) {
+ self.requestId = requestId
+ self.error = error ?? .none
+ }
+
+ public enum Error: Codable, RawRepresentable {
+ case none
+ case managed
+ case unmanaged(String)
+ case fatal
+
+ public init?(rawValue: String) {
+ switch rawValue {
+ case "none":
+ self = .none
+ case "managed":
+ self = .managed
+ case "fatal":
+ self = .fatal
+ default:
+ self = .unmanaged(rawValue)
+ }
+ }
+
+ public var rawValue: String {
+ switch self {
+ case .none:
+ return "none"
+ case .managed:
+ return "managed"
+ case .fatal:
+ return "fatal"
+ case .unmanaged(let error):
+ return error
+ }
+ }
+ }
+}
+
+struct Response: Codable {
+ let awsRequestId: String
+ let requestId: String
+ let status: Status
+
+ public init(awsRequestId: String, requestId: String, status: Status) {
+ self.awsRequestId = awsRequestId
+ self.requestId = requestId
+ self.status = status
+ }
+
+ public enum Status: Int, Codable {
+ case ok
+ case error
+ }
+}
+
+struct UnmanagedError: Error {
+ let description: String
+}
diff --git a/Examples/LambdaFunctions/Sources/HelloWorld/main.swift b/Examples/LambdaFunctions/Sources/HelloWorld/main.swift
new file mode 100644
index 00000000..7535da97
--- /dev/null
+++ b/Examples/LambdaFunctions/Sources/HelloWorld/main.swift
@@ -0,0 +1,20 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+import AWSLambdaRuntime
+
+// introductory example, the obligatory "hello, world!"
+Lambda.run { (_: Lambda.Context, _: String, callback: (Result) -> Void) in
+ callback(.success("hello, world!"))
+}
diff --git a/Examples/LambdaFunctions/Tests/LinuxMain.swift b/Examples/LambdaFunctions/Tests/LinuxMain.swift
new file mode 100644
index 00000000..c46de763
--- /dev/null
+++ b/Examples/LambdaFunctions/Tests/LinuxMain.swift
@@ -0,0 +1,15 @@
+//===----------------------------------------------------------------------===//
+//
+// This source file is part of the SwiftAWSLambdaRuntime open source project
+//
+// Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+// Licensed under Apache License v2.0
+//
+// See LICENSE.txt for license information
+// See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+//
+// SPDX-License-Identifier: Apache-2.0
+//
+//===----------------------------------------------------------------------===//
+
+preconditionFailure("use `swift test --enable-test-discovery`")
diff --git a/Examples/LambdaFunctions/scripts/build-and-package.sh b/Examples/LambdaFunctions/scripts/build-and-package.sh
new file mode 100755
index 00000000..6634539c
--- /dev/null
+++ b/Examples/LambdaFunctions/scripts/build-and-package.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+##===----------------------------------------------------------------------===##
+##
+## This source file is part of the SwiftAWSLambdaRuntime open source project
+##
+## Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+## Licensed under Apache License v2.0
+##
+## See LICENSE.txt for license information
+## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+##
+## SPDX-License-Identifier: Apache-2.0
+##
+##===----------------------------------------------------------------------===##
+
+set -eu
+
+executable=$1
+
+echo "-------------------------------------------------------------------------"
+echo "building \"$executable\" lambda"
+echo "-------------------------------------------------------------------------"
+docker run --rm -v `pwd`:/workspace -w /workspace builder bash -cl "swift build --product $executable -c release -Xswiftc -g"
+echo "done"
+
+echo "-------------------------------------------------------------------------"
+echo "packaging \"$executable\" lambda"
+echo "-------------------------------------------------------------------------"
+docker run --rm -v `pwd`:/workspace -w /workspace builder bash -cl "./scripts/package.sh $executable"
diff --git a/Examples/LambdaFunctions/scripts/deploy.sh b/Examples/LambdaFunctions/scripts/deploy.sh
new file mode 100755
index 00000000..b234ee6a
--- /dev/null
+++ b/Examples/LambdaFunctions/scripts/deploy.sh
@@ -0,0 +1,65 @@
+#!/bin/bash
+##===----------------------------------------------------------------------===##
+##
+## This source file is part of the SwiftAWSLambdaRuntime open source project
+##
+## Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+## Licensed under Apache License v2.0
+##
+## See LICENSE.txt for license information
+## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+##
+## SPDX-License-Identifier: Apache-2.0
+##
+##===----------------------------------------------------------------------===##
+
+set -eu
+
+lambda_name=SwiftSample
+s3_bucket=swift-lambda-test
+executables=( $(swift package dump-package | sed -e 's|: null|: ""|g' | jq '.products[] | (select(.type.executable)) | .name' | sed -e 's|"||g') )
+
+if [[ ${#executables[@]} = 0 ]]; then
+ echo "no executables found"
+ exit 1
+elif [[ ${#executables[@]} = 1 ]]; then
+ executable=${executables[0]}
+elif [[ ${#executables[@]} > 1 ]]; then
+ echo "multiple executables found:"
+ for executable in ${executables[@]}; do
+ echo " * $executable"
+ done
+ echo ""
+ read -p "select which executables to deploy: " executable
+fi
+
+echo -e "\ndeploying $executable"
+
+echo "-------------------------------------------------------------------------"
+echo "preparing docker build image"
+echo "-------------------------------------------------------------------------"
+docker build . -t builder
+
+echo "-------------------------------------------------------------------------"
+echo "building \"$executable\" lambda"
+echo "-------------------------------------------------------------------------"
+docker run --rm -v `pwd`/../..:/workspace -w /workspace builder \
+ bash -cl "cd Examples/LambdaFunctions && \
+ swift build --product $executable -c release -Xswiftc -g"
+echo "done"
+
+echo "-------------------------------------------------------------------------"
+echo "packaging \"$executable\" lambda"
+echo "-------------------------------------------------------------------------"
+docker run --rm -v `pwd`:/workspace -w /workspace builder bash -cl "./scripts/package.sh $executable"
+
+echo "-------------------------------------------------------------------------"
+echo "uploading \"$executable\" lambda to s3"
+echo "-------------------------------------------------------------------------"
+
+aws s3 cp .build/lambda/$executable/lambda.zip s3://$s3_bucket/
+
+echo "-------------------------------------------------------------------------"
+echo "updating \"$lambda_name\" to latest \"$executable\""
+echo "-------------------------------------------------------------------------"
+aws lambda update-function-code --function $lambda_name --s3-bucket $s3_bucket --s3-key lambda.zip
diff --git a/Examples/LambdaFunctions/scripts/package.sh b/Examples/LambdaFunctions/scripts/package.sh
new file mode 100755
index 00000000..190be3f8
--- /dev/null
+++ b/Examples/LambdaFunctions/scripts/package.sh
@@ -0,0 +1,27 @@
+#!/bin/bash
+##===----------------------------------------------------------------------===##
+##
+## This source file is part of the SwiftAWSLambdaRuntime open source project
+##
+## Copyright (c) 2020 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+## Licensed under Apache License v2.0
+##
+## See LICENSE.txt for license information
+## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+##
+## SPDX-License-Identifier: Apache-2.0
+##
+##===----------------------------------------------------------------------===##
+
+set -eu
+
+executable=$1
+
+target=.build/lambda/$executable
+rm -rf "$target"
+mkdir -p "$target"
+cp ".build/release/$executable" "$target/"
+cp -Pv /usr/lib/swift/linux/lib*so* "$target"
+cd "$target"
+ln -s "$executable" "bootstrap"
+zip --symlinks lambda.zip *
diff --git a/Examples/LambdaFunctions/scripts/sam-deploy.sh b/Examples/LambdaFunctions/scripts/sam-deploy.sh
new file mode 100755
index 00000000..3a937828
--- /dev/null
+++ b/Examples/LambdaFunctions/scripts/sam-deploy.sh
@@ -0,0 +1,47 @@
+#!/bin/bash
+##===----------------------------------------------------------------------===##
+##
+## This source file is part of the SwiftAWSLambdaRuntime open source project
+##
+## Copyright (c) 2017-2018 Apple Inc. and the SwiftAWSLambdaRuntime project authors
+## Licensed under Apache License v2.0
+##
+## See LICENSE.txt for license information
+## See CONTRIBUTORS.txt for the list of SwiftAWSLambdaRuntime project authors
+##
+## SPDX-License-Identifier: Apache-2.0
+##
+##===----------------------------------------------------------------------===##
+
+DIR="$(cd "$(dirname "$0")" && pwd)"
+
+executables=( $(swift package dump-package | sed -e 's|: null|: ""|g' | jq '.products[] | (select(.type.executable)) | .name' | sed -e 's|"||g') )
+
+if [[ ${#executables[@]} = 0 ]]; then
+ echo "no executables found"
+ exit 1
+elif [[ ${#executables[@]} = 1 ]]; then
+ executable=${executables[0]}
+elif [[ ${#executables[@]} > 1 ]]; then
+ echo "multiple executables found:"
+ for executable in ${executables[@]}; do
+ echo " * $executable"
+ done
+ echo ""
+ read -p "select which executables to deploy: " executable
+fi
+
+echo -e "\ndeploying $executable"
+
+echo "-------------------------------------------------------------------------"
+echo "preparing docker build image"
+echo "-------------------------------------------------------------------------"
+docker build . -q -t builder
+
+$DIR/build-and-package.sh ${executable}
+
+echo "-------------------------------------------------------------------------"
+echo "deploying using SAM"
+echo "-------------------------------------------------------------------------"
+
+sam deploy --template "${executable}-template.yml" $@
diff --git a/scripts/sanity.sh b/scripts/sanity.sh
index d5f9d4f6..d0a66939 100755
--- a/scripts/sanity.sh
+++ b/scripts/sanity.sh
@@ -112,7 +112,7 @@ EOF
(
cd "$here/.."
find . \
- \( \! -path './.build/*' -a \
+ \( \! -path '*/.build/*' -a \
\( "${matching_files[@]}" \) -a \
\( \! \( "${exceptions[@]}" \) \) \) | while read line; do
if [[ "$(cat "$line" | replace_acceptable_years | head -n $expected_lines | shasum)" != "$expected_sha" ]]; then