diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000000..185f74e5a06
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+.DS_Store
+/dist
+/ideaSDK
+/android-studio/sdk
+out/
+/tmp
+workspace.xml
+*.versionsBackup
+.gradle/
+build/
+*.iml
+.idea/
+local.properties
diff --git a/build.gradle.kts b/build.gradle.kts
new file mode 100644
index 00000000000..34efc5e4d84
--- /dev/null
+++ b/build.gradle.kts
@@ -0,0 +1,55 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+plugins {
+    kotlin("jvm") version "1.3.72" apply false
+}
+
+allprojects {
+    repositories {
+        mavenLocal()
+        mavenCentral()
+        jcenter()
+    }
+}
+
+val ktlint by configurations.creating
+val ktlintVersion: String by project
+dependencies {
+    ktlint("com.pinterest:ktlint:$ktlintVersion")
+}
+
+val lintPaths = listOf(
+    "codegen/**/*.kt"
+)
+
+tasks.register<JavaExec>("ktlint") {
+    description = "Check Kotlin code style."
+    group = "Verification"
+    classpath = configurations.getByName("ktlint")
+    main = "com.pinterest.ktlint.Main"
+    args = lintPaths
+}
+
+//tasks.named("check") {
+//    dependsOn(":ktlint")
+//}
+
+tasks.register<JavaExec>("ktlintFormat") {
+    description = "Auto fix Kotlin code style violations"
+    group = "formatting"
+    classpath = configurations.getByName("ktlint")
+    main = "com.pinterest.ktlint.Main"
+    args = listOf("-F") + lintPaths
+}
diff --git a/codegen/protocol-test-codegen/build.gradle.kts b/codegen/protocol-test-codegen/build.gradle.kts
new file mode 100644
index 00000000000..3d5c02ffe81
--- /dev/null
+++ b/codegen/protocol-test-codegen/build.gradle.kts
@@ -0,0 +1,40 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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 software.amazon.smithy.gradle.tasks.SmithyBuild
+
+plugins {
+    id("software.amazon.smithy") version "0.5.0"
+}
+
+dependencies {
+    implementation("software.amazon.smithy:smithy-aws-protocol-tests:1.0.5")
+    compile(project(":smithy-aws-swift-codegen"))
+}
+
+// This project doesn't produce a JAR.
+tasks["jar"].enabled = false
+
+// Run the SmithyBuild task manually since this project needs the built JAR
+// from smithy-aws-swift-codegen.
+tasks["smithyBuildJar"].enabled = false
+
+tasks.create<SmithyBuild>("buildSdk") {
+    addRuntimeClasspath = true
+}
+
+// Run the `buildSdk` automatically.
+tasks["build"].finalizedBy(tasks["buildSdk"])
+
+// TODO:: ensure built artifacts are put into the SDK's folders
\ No newline at end of file
diff --git a/codegen/protocol-test-codegen/smithy-build.json b/codegen/protocol-test-codegen/smithy-build.json
new file mode 100644
index 00000000000..c8b6cd7c843
--- /dev/null
+++ b/codegen/protocol-test-codegen/smithy-build.json
@@ -0,0 +1,26 @@
+{
+  "version": "1.0",
+  "projections": {
+    "aws-restjson": {
+      "transforms": [
+        {
+          "name": "includeServices",
+          "args": {
+            "services": ["aws.protocoltests.restjson#RestJson"]
+          }
+        }
+      ],
+      "plugins": {
+        "swift-codegen": {
+          "service": "aws.protocoltests.restjson#RestJson",
+          "module": "AWSRestJsonTestSDK",
+          "moduleVersion": "1.0",
+          "gitRepo": "https://github.com/aws-amplify/amplify-codegen.git",
+          "author": "Amazon Web Services",
+          "homepage": "https://docs.amplify.aws/",
+          "swiftVersion": "5.1.0"
+        }
+      }
+    }
+  }
+}
diff --git a/codegen/smithy-aws-swift-codegen/build.gradle.kts b/codegen/smithy-aws-swift-codegen/build.gradle.kts
new file mode 100644
index 00000000000..d3031d8d608
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/build.gradle.kts
@@ -0,0 +1,72 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+plugins {
+    kotlin("jvm")
+    jacoco
+}
+
+//description = "Generates Swift code from Smithy models"
+//extra["displayName"] = "AWS:: Smithy :: Swift :: Codegen"
+//extra["moduleName"] = "software.amazon.smithy.aws.swift.codegen"
+
+group = "software.amazon.smithy"
+version = "0.1.0"
+
+val smithyVersion: String by project
+val kotestVersion: String by project
+
+dependencies {
+    implementation(kotlin("stdlib-jdk8"))
+    api("software.amazon.smithy:smithy-swift-codegen:0.1.0")
+    api("software.amazon.smithy:smithy-aws-traits:$smithyVersion")
+    implementation("software.amazon.smithy:smithy-protocol-test-traits:$smithyVersion")
+    testImplementation("org.junit.jupiter:junit-jupiter:5.6.1")
+    testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion")
+}
+
+// Reusable license copySpec
+val licenseSpec = copySpec {
+    from("${project.rootDir}/LICENSE")
+    from("${project.rootDir}/NOTICE")
+}
+
+// Configure jars to include license related info
+tasks.jar {
+    metaInf.with(licenseSpec)
+    inputs.property("moduleName", project.name)
+    manifest {
+        attributes["Automatic-Module-Name"] = project.name
+    }
+}
+
+tasks.test {
+    useJUnitPlatform()
+    testLogging {
+        events("passed", "skipped", "failed")
+        showStandardStreams = true
+    }
+}
+
+// Configure jacoco (code coverage) to generate an HTML report
+tasks.jacocoTestReport {
+    reports {
+        xml.isEnabled = false
+        csv.isEnabled = false
+        html.destination = file("$buildDir/reports/jacoco")
+    }
+}
+
+// Always run the jacoco test report after testing.
+tasks["test"].finalizedBy(tasks["jacocoTestReport"])
diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSRestJson1ProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSRestJson1ProtocolGenerator.kt
new file mode 100644
index 00000000000..e868e5d7fb3
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AWSRestJson1ProtocolGenerator.kt
@@ -0,0 +1,30 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+package software.amazon.smithy.aws.swift.codegen
+
+import software.amazon.smithy.aws.traits.protocols.RestJson1Trait
+import software.amazon.smithy.model.shapes.ShapeId
+
+/**
+ * Handles generating the aws.rest-json protocol for services.
+ *
+ * @inheritDoc
+ * @see RestJsonProtocolGenerator
+ */
+
+class AWSRestJson1ProtocolGenerator : RestJsonProtocolGenerator() {
+    override val defaultContentType: String = "application/json"
+    override val protocol: ShapeId = RestJson1Trait.ID
+}
diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AddProtocols.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AddProtocols.kt
new file mode 100644
index 00000000000..27698a18573
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/AddProtocols.kt
@@ -0,0 +1,34 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+package software.amazon.smithy.aws.swift.codegen
+
+
+import software.amazon.smithy.swift.codegen.integration.SwiftIntegration
+import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
+
+/**
+ * Integration that registers protocol generators this package provides
+ */
+class AddProtocols : SwiftIntegration {
+    /**
+     * Gets the sort order of the customization from -128 to 127, with lowest
+     * executed first.
+     *
+     * @return Returns the sort order, defaults to -10.
+     */
+    override val order: Byte = -10
+
+    override val protocolGenerators: List<ProtocolGenerator> = listOf(AWSRestJson1ProtocolGenerator())
+}
\ No newline at end of file
diff --git a/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGenerator.kt b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGenerator.kt
new file mode 100644
index 00000000000..2d2556733e9
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/main/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGenerator.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+package software.amazon.smithy.aws.swift.codegen
+
+import software.amazon.smithy.model.knowledge.HttpBinding
+import software.amazon.smithy.model.knowledge.HttpBindingIndex
+import software.amazon.smithy.model.knowledge.OperationIndex
+import software.amazon.smithy.model.shapes.MemberShape
+import software.amazon.smithy.model.shapes.OperationShape
+import software.amazon.smithy.model.shapes.ShapeId
+import software.amazon.smithy.swift.codegen.ServiceGenerator
+import software.amazon.smithy.swift.codegen.integration.HttpBindingProtocolGenerator
+import software.amazon.smithy.swift.codegen.integration.HttpFeature
+import software.amazon.smithy.swift.codegen.integration.HttpRequestEncoder
+import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
+
+
+/**
+ * Shared base protocol generator for all AWS JSON protocol variants
+ */
+abstract class RestJsonProtocolGenerator : HttpBindingProtocolGenerator() {
+
+    override fun generateProtocolUnitTests(ctx: ProtocolGenerator.GenerationContext) {}
+
+    override fun generateSerializers(ctx: ProtocolGenerator.GenerationContext) {
+        // Generate extension on input requests to implement Codable protocol
+        val inputShapesWithHttpBindings:MutableSet<ShapeId> = mutableSetOf()
+        for (operation in getHttpBindingOperations(ctx)) {
+            if (operation.input.isPresent) {
+                val inputShapeId = operation.input.get()
+                if (inputShapesWithHttpBindings.contains(inputShapeId)) {
+                    // The input shape is referenced by more than one operation
+                    continue
+                }
+                renderInputRequestConformanceToCodable(ctx, operation)
+                inputShapesWithHttpBindings.add(inputShapeId)
+            }
+        }
+        super.generateSerializers(ctx)
+    }
+
+    override fun getHttpFeatures(ctx: ProtocolGenerator.GenerationContext): List<HttpFeature> {
+        val features = super.getHttpFeatures(ctx).toMutableList()
+        val jsonFeatures = listOf(JSONRequestEncoder())
+        features.addAll(jsonFeatures)
+        return features
+    }
+
+    private fun renderInputRequestConformanceToCodable(ctx: ProtocolGenerator.GenerationContext, op: OperationShape) {
+        if (op.input.isEmpty()) {
+            return
+        }
+        val inputShape = ctx.model.expectShape(op.input.get())
+        val opIndex = ctx.model.getKnowledge(OperationIndex::class.java)
+        val inputShapeName = ServiceGenerator.getOperationInputShapeName(ctx.symbolProvider, opIndex, op)
+        val requestPayloadMembers = getRequestPayloadMembers(ctx, op)
+
+        if (requestPayloadMembers.isNotEmpty()) {
+            ctx.delegator.useShapeWriter(inputShape) { writer ->
+                writer.openBlock("extension ${inputShapeName!!.get()}: Codable {", "}") {
+                    writer.openBlock("private enum CodingKeys: String, CodingKey {", "}") {
+                        // TODO:: handle the case when encoding name is different from member name
+                        writer.write("case ${requestPayloadMembers.joinToString(separator = ", ")}")
+                    }
+                }
+                writer.write("")
+            }
+        }
+    }
+
+    private fun getRequestPayloadMembers(ctx: ProtocolGenerator.GenerationContext,
+                                         op: OperationShape): MutableSet<String> {
+        val requestPayloadMembers = mutableSetOf<String>()
+        val bindingIndex = ctx.model.getKnowledge(HttpBindingIndex::class.java)
+        val requestBindings = bindingIndex.getRequestBindings(op)
+        val httpPayloadBinding = requestBindings.values.firstOrNull { it.location == HttpBinding.Location.PAYLOAD }
+
+        /* Unbound document members that should be serialized into the document format for the protocol.
+         */
+        val documentMemberBindings = requestBindings.values
+                .filter { it.location == HttpBinding.Location.DOCUMENT }
+                .sortedBy { it.memberName }
+        if (httpPayloadBinding != null) {
+            val memberName = httpPayloadBinding.member.memberName
+            requestPayloadMembers.add(memberName)
+        } else if (documentMemberBindings.isNotEmpty()) {
+            val documentMemberShapes = documentMemberBindings.map { it.member }
+            val documentMemberShapesSortedByName: List<MemberShape> = documentMemberShapes.sortedBy { ctx.symbolProvider.toMemberName(it) }
+            if (documentMemberShapesSortedByName.isNotEmpty()) {
+                for (member in documentMemberShapesSortedByName) {
+                    val memberName = ctx.symbolProvider.toMemberName(member)
+                    requestPayloadMembers.add(memberName)
+                }
+            }
+        }
+        return requestPayloadMembers
+    }
+}
+
+
+class JSONRequestEncoder : HttpRequestEncoder("JsonEncoder") {}
diff --git a/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration b/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration
new file mode 100644
index 00000000000..e7059266b7a
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/main/resources/META-INF/services/software.amazon.smithy.swift.codegen.integration.SwiftIntegration
@@ -0,0 +1 @@
+software.amazon.smithy.aws.swift.codegen.AddProtocols
\ No newline at end of file
diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt
new file mode 100644
index 00000000000..456888f96a8
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/RestJsonProtocolGeneratorTests.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+
+package software.amazon.smithy.aws.swift.codegen
+
+import io.kotest.matchers.string.shouldContainOnlyOnce
+import org.junit.jupiter.api.Test
+import software.amazon.smithy.aws.traits.protocols.RestJson1Trait
+import software.amazon.smithy.build.MockManifest
+import software.amazon.smithy.codegen.core.SymbolProvider
+import software.amazon.smithy.model.shapes.ShapeId
+import software.amazon.smithy.swift.codegen.SwiftCodegenPlugin
+import software.amazon.smithy.swift.codegen.SwiftDelegator
+import software.amazon.smithy.swift.codegen.SwiftSettings
+import software.amazon.smithy.swift.codegen.integration.ProtocolGenerator
+
+class MockRestJsonProtocolGenerator : RestJsonProtocolGenerator() {
+    override val defaultContentType: String = "application/json"
+    override val protocol: ShapeId = RestJson1Trait.ID
+}
+
+// NOTE: protocol conformance is mostly handled by the protocol tests suite
+class RestJsonProtocolGeneratorTests: TestsBase() {
+    val model = createModelFromSmithy("http-binding-protocol-generator-test.smithy")
+
+    data class TestContext(val ctx: ProtocolGenerator.GenerationContext,
+                           val manifest: MockManifest,
+                           val generator: MockRestJsonProtocolGenerator)
+
+    private fun newTestContext(): TestContext {
+        val manifest = MockManifest()
+        val provider: SymbolProvider = SwiftCodegenPlugin.createSymbolProvider(model, "Example")
+        val serviceShapeIdWithNamespace = "com.test#Example"
+        val service = model.getShape(ShapeId.from(serviceShapeIdWithNamespace)).get().asServiceShape().get()
+        val settings = SwiftSettings.from(model, buildDefaultSwiftSettingsObjectNode(serviceShapeIdWithNamespace))
+        val delegator = SwiftDelegator(settings, model, manifest, provider)
+        val generator = MockRestJsonProtocolGenerator()
+        val ctx = ProtocolGenerator.GenerationContext(settings, model, service, provider, listOf(), "mockHttp", delegator)
+        return TestContext(ctx, manifest, generator)
+    }
+
+    val newTestContext = newTestContext()
+
+    init {
+        newTestContext.generator.generateSerializers(newTestContext.ctx)
+        newTestContext.ctx.delegator.flushWriters()
+    }
+
+    @Test
+    fun `Define coding keys for unbound document payload members`() {
+        val contents = getModelFileContents("Example","SmokeTestRequest.swift", newTestContext.manifest)
+        contents.shouldSyntacticSanityCheck()
+        val expectedContents =
+                "extension SmokeTestRequest: Codable {\n" +
+                "    private enum CodingKeys: String, CodingKey {\n" +
+                "        case payload1, payload2, payload3\n" +
+                "    }\n" +
+                "}"
+        contents.shouldContainOnlyOnce(expectedContents)
+    }
+
+    @Test
+    fun `Define coding keys for payload member`() {
+        val contents = getModelFileContents("Example","ExplicitBlobRequest.swift", newTestContext.manifest)
+        contents.shouldSyntacticSanityCheck()
+        val expectedContents =
+                "extension ExplicitBlobRequest: Codable {\n" +
+                "    private enum CodingKeys: String, CodingKey {\n" +
+                "        case payload1\n" +
+                "    }\n" +
+                "}"
+        contents.shouldContainOnlyOnce(expectedContents)
+    }
+
+    @Test
+    fun `Defines coding keys for List Input requests`() {
+        val contents = getModelFileContents("Example","ListInputRequest.swift", newTestContext.manifest)
+        contents.shouldSyntacticSanityCheck()
+        val expectedContents =
+                "extension ListInputRequest: Codable {\n" +
+                "    private enum CodingKeys: String, CodingKey {\n" +
+                "        case intList, nestedIntList, structList\n" +
+                "    }\n" +
+                "}"
+        contents.shouldContainOnlyOnce(expectedContents)
+    }
+}
diff --git a/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/TestsBase.kt b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/TestsBase.kt
new file mode 100644
index 00000000000..1a7e129f0ec
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/test/kotlin/software/amazon/smithy/aws/swift/codegen/TestsBase.kt
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ *  http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file 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.
+ */
+package software.amazon.smithy.aws.swift.codegen
+
+import org.junit.jupiter.api.Assertions
+import software.amazon.smithy.build.MockManifest
+import software.amazon.smithy.model.Model
+import software.amazon.smithy.model.node.Node
+import software.amazon.smithy.model.node.ObjectNode
+import java.net.URL
+
+open class TestsBase {
+
+    protected fun createModelFromSmithy(smithyTestResourceName: String): Model {
+        return Model.assembler()
+                .addImport(getSmithyResource(smithyTestResourceName))
+                .discoverModels()
+                .assemble()
+                .unwrap()
+    }
+
+    protected fun getSmithyResource(smithyTestResourceName: String): URL? {
+        return TestsBase::class.java.classLoader.getResource("software.amazon.smithy.aws.swift.codegen/$smithyTestResourceName")
+    }
+
+    protected fun buildDefaultSwiftSettingsObjectNode(serviceShapeId: String,
+                                                      moduleName: String = "example",
+                                                      moduleVersion: String = "1.0.0"): ObjectNode {
+        return Node.objectNodeBuilder()
+                .withMember("service", Node.from(serviceShapeId))
+                .withMember("module", Node.from(moduleName))
+                .withMember("moduleVersion", Node.from(moduleVersion))
+                .withMember("homepage", Node.from("https://docs.amplify.aws/"))
+                .withMember("author", Node.from("Amazon Web Services"))
+                .withMember("gitRepo", Node.from("https://github.com/aws-amplify/amplify-codegen.git"))
+                .withMember("swiftVersion", Node.from("5.1.0"))
+                .build()
+    }
+
+    protected fun getModelFileContents(namespace: String, filename: String, manifest: MockManifest): String {
+        return manifest.expectFileString("$namespace/models/$filename")
+    }
+
+    fun String.shouldSyntacticSanityCheck() {
+        // sanity check the generated code for matching paranthesis
+        var openBraces = 0
+        var closedBraces = 0
+        var openParens = 0
+        var closedParens = 0
+        this.forEach {
+            when (it) {
+                '{' -> openBraces++
+                '}' -> closedBraces++
+                '(' -> openParens++
+                ')' -> closedParens++
+            }
+        }
+        Assertions.assertEquals(openBraces, closedBraces, "unmatched open/closed braces:\n$this")
+        Assertions.assertEquals(openParens, closedParens, "unmatched open/close parens:\n$this")
+    }
+}
\ No newline at end of file
diff --git a/codegen/smithy-aws-swift-codegen/src/test/resources/software.amazon.smithy.aws.swift.codegen/http-binding-protocol-generator-test.smithy b/codegen/smithy-aws-swift-codegen/src/test/resources/software.amazon.smithy.aws.swift.codegen/http-binding-protocol-generator-test.smithy
new file mode 100644
index 00000000000..821b5b1d650
--- /dev/null
+++ b/codegen/smithy-aws-swift-codegen/src/test/resources/software.amazon.smithy.aws.swift.codegen/http-binding-protocol-generator-test.smithy
@@ -0,0 +1,198 @@
+$version: "1.0"
+namespace com.test
+
+use aws.protocols#restJson1
+
+@restJson1
+service Example {
+    version: "1.0.0",
+    operations: [
+        SmokeTest,
+        DuplicateInputTest,
+        ExplicitString,
+        ExplicitBlob,
+        ExplicitBlobStream,
+        ExplicitStruct,
+        ListInput,
+        MapInput,
+        EnumInput
+    ]
+}
+
+@http(method: "POST", uri: "/smoketest/{label1}/foo")
+operation SmokeTest {
+    input: SmokeTestRequest,
+    output: SmokeTestResponse,
+    errors: [SmokeTestError]
+}
+
+@http(method: "POST", uri: "/smoketest-duplicate/{label1}/foo")
+operation DuplicateInputTest {
+    // uses the same input type as another operation. Ensure that we only generate one instance of the serializer
+    input: SmokeTestRequest
+}
+
+structure SmokeTestRequest {
+    @httpHeader("X-Header1")
+    header1: String,
+
+    @httpHeader("X-Header2")
+    header2: String,
+
+    @httpQuery("Query1")
+    query1: String,
+
+    @required
+    @httpLabel
+    label1: String,
+
+    payload1: String,
+    payload2: Integer,
+    payload3: Nested
+}
+
+structure Nested {
+    member1: String,
+    member2: String
+}
+
+structure SmokeTestResponse {
+
+}
+
+@error("client")
+structure SmokeTestError {}
+
+
+@http(method: "POST", uri: "/explicit/string")
+operation ExplicitString {
+    input: ExplicitStringRequest
+}
+
+structure ExplicitStringRequest {
+    @httpPayload
+    payload1: String
+}
+
+@http(method: "POST", uri: "/explicit/blob")
+operation ExplicitBlob {
+    input: ExplicitBlobRequest
+}
+
+structure ExplicitBlobRequest {
+    @httpPayload
+    payload1: Blob
+}
+
+@streaming
+blob BodyStream
+
+@http(method: "POST", uri: "/explicit/blobstream")
+operation ExplicitBlobStream {
+    input: ExplicitBlobStreamRequest
+}
+
+structure ExplicitBlobStreamRequest {
+    @httpPayload
+    payload1: BodyStream
+}
+
+@http(method: "POST", uri: "/explicit/struct")
+operation ExplicitStruct {
+    input: ExplicitStructRequest
+}
+
+structure Nested4 {
+    member1: Integer,
+    // sanity check, member serialization for non top-level (bound to the operation input) aggregate shapes
+    intList: IntList,
+    intMap: IntMap
+}
+
+structure Nested3 {
+    member1: String,
+    member2: String,
+    member3: Nested4
+}
+
+structure Nested2 {
+    moreNesting: Nested3
+}
+
+structure ExplicitStructRequest {
+    @httpPayload
+    payload1: Nested2
+}
+
+list IntList {
+    member: Integer
+}
+
+list StructList {
+    member: Nested
+}
+
+// A list of lists of integers
+list NestedIntList {
+    member: IntList
+}
+
+@http(method: "POST", uri: "/input/list")
+operation ListInput {
+    input: ListInputRequest
+}
+
+structure ListInputRequest {
+    intList: IntList,
+    structList: StructList,
+    nestedIntList: NestedIntList
+}
+
+map IntMap {
+    key: String,
+    value: Integer
+}
+
+map StructMap {
+    key: String,
+    value: Nested
+}
+
+@http(method: "POST", uri: "/input/map")
+operation MapInput {
+    input: MapInputRequest
+}
+
+structure MapInputRequest {
+    intMap: IntMap,
+    structMap: StructMap
+}
+
+
+@http(method: "POST", uri: "/input/enum")
+operation EnumInput {
+    input: EnumInputRequest
+}
+
+@enum([
+    {
+        value: "rawValue1",
+        name: "Variant1"
+    },
+    {
+        value: "rawValue2",
+        name: "Variant2"
+    }
+])
+string MyEnum
+
+structure NestedEnum {
+    myEnum: MyEnum
+}
+
+structure EnumInputRequest {
+    nestedWithEnum: NestedEnum,
+
+    @httpHeader("X-EnumHeader")
+    enumHeader: MyEnum
+}
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 00000000000..1279f82704d
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,15 @@
+kotlin.code.style=official
+
+# codegen
+smithyVersion=1.0.5
+
+# kotlin
+kotlinVersion=1.3.72
+kotlin.native.ignoreDisabledTargets=true
+
+
+# testing/utility
+# FIXME - junit5 not working
+ktlintVersion=0.36.0
+kotestVersion=4.0.5
+
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 00000000000..f3d88b1c2fa
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 00000000000..1b16c34a71c
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,5 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-bin.zip
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 00000000000..2fe81a7d95e
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,183 @@
+#!/usr/bin/env sh
+
+#
+# Copyright 2015 the original author or authors.
+#
+# 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
+#
+#      https://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.
+#
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+    echo "$*"
+}
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=`expr $i + 1`
+    done
+    case $i in
+        0) set -- ;;
+        1) set -- "$args0" ;;
+        2) set -- "$args0" "$args1" ;;
+        3) set -- "$args0" "$args1" "$args2" ;;
+        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Escape application args
+save () {
+    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+    echo " "
+}
+APP_ARGS=`save "$@"`
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 00000000000..24467a141f7
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,100 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windows variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100644
index 00000000000..8ac64e57688
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,23 @@
+pluginManagement {
+    repositories {
+        maven { url = uri("https://dl.bintray.com/kotlin/kotlin-eap") }
+        maven { url = uri("https://kotlin.bintray.com/kotlinx") }
+
+        gradlePluginPortal()
+    }
+}
+
+rootProject.name = "aws-sdk-swift"
+enableFeaturePreview("GRADLE_METADATA")
+
+
+fun module(path: String) {
+    val name = path.replace('\\', '/').substringAfterLast('/')
+    include(name)
+    project(":$name").projectDir = file(path)
+}
+
+
+module("codegen")
+module("codegen/smithy-aws-swift-codegen")
+module("codegen/protocol-test-codegen")