This is an automated email from the ASF dual-hosted git repository.

lostluck pushed a commit to branch swift-sdk
in repository https://gitbox.apache.org/repos/asf/beam.git

commit cf29f48a462fb26962e5cb282c821718fdef5ae4
Author: Byron Ellis <byronel...@google.com>
AuthorDate: Wed Aug 9 17:52:28 2023 -0700

    Moving experimental Swift SDK code into Beam repo structure. Cleaning up 
and adding tests and Apache License labels where needed.
---
 sdks/swift/Documentation/INTERNALS.md              |   4 +
 sdks/swift/Package.resolved                        | 113 ++++++++++++
 sdks/swift/Package.swift                           |  62 +++++++
 sdks/swift/Scripts/generate_protobuf.sh            |  97 +++++++++++
 sdks/swift/Sources/ApacheBeam/ApacheBeam.swift     |  20 +++
 .../ApacheBeam/Client/ApiServiceDescriptor.swift   |  63 +++++++
 .../Sources/ApacheBeam/Coders/BeamValue.swift      |  70 ++++++++
 .../Sources/ApacheBeam/Coders/Coder+Decoding.swift |  79 +++++++++
 .../Sources/ApacheBeam/Coders/Coder+Encoding.swift | 109 ++++++++++++
 sdks/swift/Sources/ApacheBeam/Coders/Coder.swift   | 190 +++++++++++++++++++++
 .../Sources/ApacheBeam/Core/ArtifactInfo.swift     |  31 ++++
 .../Sources/ApacheBeam/Core/Environment.swift      |  44 +++++
 sdks/swift/Sources/ApacheBeam/Core/KeyValue.swift  |  62 +++++++
 sdks/swift/Sources/ApacheBeam/Core/Windowing.swift |  55 ++++++
 .../ApacheBeam/Internal/Data+Decoding.swift        |  72 ++++++++
 .../ApacheBeam/Internal/Data+Encoding.swift        |  35 ++++
 .../ApacheBeam/Internal/Date+Timestamp.swift       |  27 +++
 .../ApacheBeam/Internal/ProtoConversion.swift      |  25 +++
 .../Sources/ApacheBeam/Internal/String+Urns.swift  |  38 +++++
 .../Tests/ApacheBeamTests/ApacheBeamTests.swift    |  12 ++
 .../Tests/ApacheBeamTests/Coders/CoderTests.swift  |  62 +++++++
 21 files changed, 1270 insertions(+)

diff --git a/sdks/swift/Documentation/INTERNALS.md 
b/sdks/swift/Documentation/INTERNALS.md
new file mode 100644
index 00000000000..757e1b44602
--- /dev/null
+++ b/sdks/swift/Documentation/INTERNALS.md
@@ -0,0 +1,4 @@
+#  Internals
+
+
+
diff --git a/sdks/swift/Package.resolved b/sdks/swift/Package.resolved
new file mode 100644
index 00000000000..cc4a106ff4a
--- /dev/null
+++ b/sdks/swift/Package.resolved
@@ -0,0 +1,113 @@
+{
+  "pins" : [
+    {
+      "identity" : "grpc-swift",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/grpc/grpc-swift.git";,
+      "state" : {
+        "revision" : "735d88f8196467f24189896f29c4625af7cbd072",
+        "version" : "1.19.0"
+      }
+    },
+    {
+      "identity" : "swift-atomics",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-atomics.git";,
+      "state" : {
+        "revision" : "6c89474e62719ddcc1e9614989fff2f68208fe10",
+        "version" : "1.1.0"
+      }
+    },
+    {
+      "identity" : "swift-collections",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-collections.git";,
+      "state" : {
+        "revision" : "937e904258d22af6e447a0b72c0bc67583ef64a2",
+        "version" : "1.0.4"
+      }
+    },
+    {
+      "identity" : "swift-docc-plugin",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-docc-plugin";,
+      "state" : {
+        "revision" : "26ac5758409154cc448d7ab82389c520fa8a8247",
+        "version" : "1.3.0"
+      }
+    },
+    {
+      "identity" : "swift-docc-symbolkit",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-docc-symbolkit";,
+      "state" : {
+        "revision" : "b45d1f2ed151d057b54504d653e0da5552844e34",
+        "version" : "1.0.0"
+      }
+    },
+    {
+      "identity" : "swift-log",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-log.git";,
+      "state" : {
+        "revision" : "32e8d724467f8fe623624570367e3d50c5638e46",
+        "version" : "1.5.2"
+      }
+    },
+    {
+      "identity" : "swift-nio",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-nio.git";,
+      "state" : {
+        "revision" : "cf281631ff10ec6111f2761052aa81896a83a007",
+        "version" : "2.58.0"
+      }
+    },
+    {
+      "identity" : "swift-nio-extras",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-nio-extras.git";,
+      "state" : {
+        "revision" : "0e0d0aab665ff1a0659ce75ac003081f2b1c8997",
+        "version" : "1.19.0"
+      }
+    },
+    {
+      "identity" : "swift-nio-http2",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-nio-http2.git";,
+      "state" : {
+        "revision" : "a8ccf13fa62775277a5d56844878c828bbb3be1a",
+        "version" : "1.27.0"
+      }
+    },
+    {
+      "identity" : "swift-nio-ssl",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-nio-ssl.git";,
+      "state" : {
+        "revision" : "e866a626e105042a6a72a870c88b4c531ba05f83",
+        "version" : "2.24.0"
+      }
+    },
+    {
+      "identity" : "swift-nio-transport-services",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-nio-transport-services.git";,
+      "state" : {
+        "revision" : "5fd1458c245d5741b3c8ebe55489f590c6ca8f15",
+        "version" : "1.18.0"
+      }
+    },
+    {
+      "identity" : "swift-protobuf",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/apple/swift-protobuf.git";,
+      "state" : {
+        "revision" : "ce20dc083ee485524b802669890291c0d8090170",
+        "version" : "1.22.1"
+      }
+    }
+  ],
+  "version" : 2
+}
diff --git a/sdks/swift/Package.swift b/sdks/swift/Package.swift
new file mode 100644
index 00000000000..e6b67948631
--- /dev/null
+++ b/sdks/swift/Package.swift
@@ -0,0 +1,62 @@
+// swift-tools-version: 5.9
+// The swift-tools-version declares the minimum version of Swift required to 
build this package.
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import PackageDescription
+
+let dependencies: [Package.Dependency] = [
+    // Core Dependencies
+    .package(url: "https://github.com/grpc/grpc-swift.git";, from: "1.19.0"),
+    .package(url: "https://github.com/apple/swift-log.git";, from: "1.0.0"),
+    
+    // Additional Transform Dependencies
+    
+    
+    // Swift Package Manager Plugins
+    .package(url: "https://github.com/apple/swift-docc-plugin";, from: "1.0.0")
+]
+
+let package = Package(
+    name: "ApacheBeam",
+    platforms: [
+        .macOS("13.0")
+    ],
+    products: [
+        // Products define the executables and libraries a package produces, 
making them visible to other packages.
+        .library(
+            name: "ApacheBeam",
+            targets: ["ApacheBeam"]),
+    ],
+    dependencies: dependencies,
+    targets: [
+        // Targets are the basic building blocks of a package, defining a 
module or a test suite.
+        // Targets can depend on other targets in this package and products 
from dependencies.
+        .target(
+            name: "ApacheBeam",
+            dependencies: [
+                .product(name:"GRPC",package:"grpc-swift"),
+                .product(name: "Logging",package:"swift-log")
+            ]
+        ),
+        .testTarget(
+            name: "ApacheBeamTests",
+            dependencies: ["ApacheBeam"]),
+    ]
+)
diff --git a/sdks/swift/Scripts/generate_protobuf.sh 
b/sdks/swift/Scripts/generate_protobuf.sh
new file mode 100755
index 00000000000..8210868a804
--- /dev/null
+++ b/sdks/swift/Scripts/generate_protobuf.sh
@@ -0,0 +1,97 @@
+#!/bin/bash
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+set -e
+
+read -r -d '\0' LICENSE << EOM
+//
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+\0
+EOM
+
+if [[ -z "$(which protoc)" ]]; then
+  echo "protoc not found on path"
+  exit 1
+fi
+
+SCRIPT_DIR="$( realpath "$( dirname "${BASH_SOURCE[0]}" )" )"
+if [[ -z $SCRIPT_DIR ]]; then
+  echo "unable to resolve path to script"
+  exit 1
+fi
+
+SDK_PATH="$( realpath "$(dirname $SCRIPT_DIR)/.." )"
+if [[ -z "$SDK_PATH" ]]; then
+  echo "unable to resolve path to project root"
+  exit 1
+fi
+
+PROJECT_ROOT="$(realpath "$(dirname $SCRIPT_DIR)/../..")"
+if [[ -z "$PROJECT_ROOT" ]]; then
+  echo "unable to resolve path to project root"
+  exit 1
+fi
+
+# You can manually specify this otherwise we assume we are in the repo
+if [[ -z "$BEAM_PATH" ]]; then
+    BEAM_PATH=${PROJECT_ROOT}
+fi
+
+function gen_beam_model_protos() {
+  cd "$PROJECT_ROOT"
+  echo "Generating Beam Model Protos in $PROJECT_ROOT from $BEAM_PATH"
+
+  declare -a TO_GEN=(
+    
'/model/job-management/src/main/proto/org/apache/beam/model/job_management/v1/*.proto'
+    '/model/pipeline/src/main/proto/org/apache/beam/model/pipeline/v1/*.proto'
+    
'/model/fn-execution/src/main/proto/org/apache/beam/model/fn_execution/v1/*.proto'
+  )
+
+  declare -a INCLUDES=()
+  for package in "${TO_GEN[@]}"
+  do
+    INCLUDES+=("-I${BEAM_PATH}/${package%/org*}")
+  done
+
+  mkdir -p $PROJECT_ROOT/sdks/swift/Sources/ApacheBeam/Generated/{Model,GRPC}
+  for package in "${TO_GEN[@]}"
+  do
+    echo "${INCLUDES[@]}"
+    protoc \
+      "${INCLUDES[@]}" \
+      
--swift_out="$PROJECT_ROOT/sdks/swift/Sources/ApacheBeam/Generated/Model" \
+      
--grpc-swift_out="$PROJECT_ROOT/sdks/swift/Sources/ApacheBeam/Generated/GRPC" \
+      ${BEAM_PATH}$package
+  done
+}
+
+gen_beam_model_protos
diff --git a/sdks/swift/Sources/ApacheBeam/ApacheBeam.swift 
b/sdks/swift/Sources/ApacheBeam/ApacheBeam.swift
new file mode 100644
index 00000000000..ab09c722187
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/ApacheBeam.swift
@@ -0,0 +1,20 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+enum ApacheBeamError : Error {
+    case runtimeError(String)
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Client/ApiServiceDescriptor.swift 
b/sdks/swift/Sources/ApacheBeam/Client/ApiServiceDescriptor.swift
new file mode 100644
index 00000000000..40e1b662d8f
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Client/ApiServiceDescriptor.swift
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/// Representation of the API Service Descriptors used to communicate with 
runners (and vice versa)
+public struct ApiServiceDescriptor {
+    
+    public enum EncodedAs {
+        case json,textproto
+    }
+    
+    let url: String
+
+    public init(host:String,port:Int) {
+        self.url = "\(host):\(port)"
+    }
+    public init(unixAddress:String) {
+        self.url = "unix://\(unixAddress)"
+    }
+}
+
+extension ApiServiceDescriptor {
+    init(proto: Org_Apache_Beam_Model_Pipeline_V1_ApiServiceDescriptor) {
+        self.url = proto.url
+    }
+}
+
+extension ApiServiceDescriptor : ProtoConversion {
+    
+    typealias Proto = Org_Apache_Beam_Model_Pipeline_V1_ApiServiceDescriptor
+    var proto: Org_Apache_Beam_Model_Pipeline_V1_ApiServiceDescriptor {
+        .with { $0.url = self.url }
+    }
+
+    
+}
+
+
+
+public extension ApiServiceDescriptor {
+    static func from(env:String,format:EncodedAs = .textproto) throws -> 
ApiServiceDescriptor {
+        switch format {
+        case .textproto:
+            ApiServiceDescriptor(proto: try .init(textFormatString: env))
+        case .json:
+            ApiServiceDescriptor(proto: try .init(jsonString: env))
+        }
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Coders/BeamValue.swift 
b/sdks/swift/Sources/ApacheBeam/Coders/BeamValue.swift
new file mode 100644
index 00000000000..054356ed6f1
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Coders/BeamValue.swift
@@ -0,0 +1,70 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+/// An enum representing values coming over the FnApi Data Plane.
+public indirect enum BeamValue {
+    /// A value not representable in the Swift SDK
+    case invalid(String)
+    
+    // Scalar values
+    
+    /// Bytes coded
+    case bytes(Data?)
+    /// UTF8 Strings
+    case string(String?)
+    /// Integers (Signed 64-bit)
+    case integer(Int?)
+    /// Doubles
+    case double(Double?)
+    /// Booleans
+    case boolean(Bool?)
+    /// A window
+    case window(Window)
+    
+    // Composite Values
+    
+    /// An iterable
+    case array([BeamValue])
+    /// A key-value pair
+    case kv(BeamValue,BeamValue)
+    /// A windowed value
+    case windowed(BeamValue,Date,UInt8,BeamValue)
+    
+    /// Convenience method for extacting the base value from one
+    /// of the scalar representations.
+    var baseValue : Any? {
+        switch self {
+        case let .bytes(d):
+            d
+        case let .string(s):
+            s
+        case let .integer(i):
+            i
+        case let .double(d):
+            d
+        case let .boolean(b):
+            b
+        case let .window(w):
+            w
+        default:
+            nil as Any?
+        }
+    }
+ 
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Coders/Coder+Decoding.swift 
b/sdks/swift/Sources/ApacheBeam/Coders/Coder+Decoding.swift
new file mode 100644
index 00000000000..48c1cb9db43
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Coders/Coder+Decoding.swift
@@ -0,0 +1,79 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+public extension Coder {
+    
+    /// Decodes a raw data block into a BeamValue for further processing
+    func decode(_ data:inout Data) throws -> BeamValue {
+        switch self {
+        // Scalar values check for size 0 input data and return null if that's 
a problem
+            
+        // TODO: Endian and other encoding checks
+            
+        case .bytes:
+            return .bytes(data.count == 0 ? nil : try data.subdata())
+        case .string:
+            return .string(data.count == 0 ? nil : String(data: try 
data.subdata(), encoding: .utf8))
+        case .varint:
+            return .integer(data.count == 0 ? nil : try data.varint())
+        case .fixedint:
+            return .integer(data.count == 0 ? nil : try data.next(Int.self))
+        case .byte:
+            return .integer(data.count == 0 ? nil : Int(try 
data.next(UInt8.self)))
+        case .boolean:
+            return .boolean(data.count == 0 ? nil : try data.next(UInt8.self) 
!= 0)
+        case .double:
+            return .double(data.count == 0 ? nil : try data.next(Double.self))
+        case .globalwindow:
+            return .window(.global)
+        case .lengthprefix(let coder): // Length prefix basically serves to 
make values nullable
+            var subdata = try data.subdata()
+            return try coder.decode(&subdata)
+        case let .keyvalue(keyCoder, valueCoder):
+            return .kv(try keyCoder.decode(&data), try 
valueCoder.decode(&data))
+        case let .iterable(coder):
+            let length = try data.next(Int32.self)
+            return .array(try (0..<length).map({ _ in try coder.decode(&data) 
}))
+        case let .windowedvalue(valueCoder, windowCoder):
+            let timestamp = try data.next(Int64.self).byteSwapped &+ 
Int64(-9223372036854775808)
+            let windowCount = try data.next(Int32.self).byteSwapped
+            if windowCount > 1 {
+                throw ApacheBeamError.runtimeError("Windowed values with > 1 
window not yet supported")
+            }
+            let window = try windowCoder.decode(&data)
+            
+            //TODO: Actually handle pane info
+            let pane = try data.next(UInt8.self)
+            switch ((pane >> 4) & 0x0f) {
+            case 0x0:
+                break
+            case 0x1:
+                _ = try data.varint()
+            case 0x2:
+                _ = try data.varint()
+                _ = try data.varint()
+            default:
+                throw ApacheBeamError.runtimeError("Invalid pane encoding 
\(String(pane,radix:2))")
+            }
+            return .windowed(try valueCoder.decode(&data), 
Date(millisecondsSince1970: timestamp), pane, window)
+        default:
+            throw ApacheBeamError.runtimeError("Decoding of \(self.urn) coders 
not supported.")
+        }
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Coders/Coder+Encoding.swift 
b/sdks/swift/Sources/ApacheBeam/Coders/Coder+Encoding.swift
new file mode 100644
index 00000000000..fffeb351a53
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Coders/Coder+Encoding.swift
@@ -0,0 +1,109 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+public extension Coder {
+
+    func encode(_ value: Any?) throws -> Data {
+        var data = Data()
+        try self.encode(value,data: &data)
+        return data
+    }
+    func encode(_ value: Any?,data: inout Data) throws {
+        switch self {
+            // Scalar values check for size 0 input data and return null if 
that's a problem
+                
+            // TODO: Endian and other encoding checks
+                
+        case .bytes:
+            if let v = value as? Data {
+                data.varint(v.count)
+                data.append(v)
+            }
+        case .string:
+            if let v = value as? String {
+                let d = Data(v.utf8)
+                data.varint(d.count)
+                data.append(d)
+            }
+        case .varint:
+            if let v = value as? Int {
+                data.varint(v)
+            }
+        case .fixedint:
+            if let v = value as? Int {
+                data.next(v)
+            }
+        case .byte:
+            if let v = value as? UInt8 {
+                data.next(v)
+            }
+        case .boolean:
+            if let v = value as? Bool {
+                let byte: UInt8 = v ? 1 : 0
+                data.next(byte)
+            }
+        case .double:
+            if let v = value as? Double {
+                data.next(v)
+            }
+        case .globalwindow:
+            break
+        case .lengthprefix(let coder):
+            let subData = try coder.encode(value)
+            data.varint(subData.count)
+            data.append(subData)
+        case let .keyvalue(keyCoder, valueCoder):
+            if let v = value as? AnyKeyValue {
+                try keyCoder.encode(v.anyKey, data: &data)
+                // We do a special case check here to account for the fact that
+                // keyvalue is used both for group by as well as a pair type
+                switch valueCoder {
+                case .iterable(_):
+                    try valueCoder.encode(v.anyValues,data:&data)
+                default:
+                    try valueCoder.encode(v.anyValue,data:&data)
+                }
+            }
+        case let .iterable(coder):
+            if let v = value as? [Any] {
+                data.next(Int32(truncatingIfNeeded: v.count))
+                for item in v {
+                    try coder.encode(item, data: &data)
+                }
+            }
+        case let .windowedvalue(valueCoder, windowCoder):
+            if let (v,ts,w) = value as? (Any,Date,Window) {
+                //Timestamp
+                data.next( (ts.millisecondsSince1970 &- 
Int64(-9223372036854775808)).bigEndian )
+                switch w {
+                case .global:
+                    data.next(Int32(0))
+                default:
+                    data.next(Int32(1))
+                    try windowCoder.encode(w,data:&data)
+                }
+                // TODO: Real Panes
+                data.append(UInt8(1 >> 5 | 1 >> 6 | 1 >> 7))
+                try valueCoder.encode(v, data: &data)
+            }
+            default:
+                throw ApacheBeamError.runtimeError("Encoding of \(self.urn) 
coders not supported.")
+        }
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Coders/Coder.swift 
b/sdks/swift/Sources/ApacheBeam/Coders/Coder.swift
new file mode 100644
index 00000000000..e8c23b9d4cf
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Coders/Coder.swift
@@ -0,0 +1,190 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+public indirect enum Coder {
+    // Special
+    case unknown(String)
+    case custom(Data)
+    
+    // Scalar standard coders
+    case double,varint,fixedint,byte,bytes,string,boolean,globalwindow
+    
+    // Composite standard coders
+    
+    case keyvalue(Coder,Coder)
+    case iterable(Coder)
+    case lengthprefix(Coder)
+    case windowedvalue(Coder,Coder)
+    
+    // TODO: Row Coder
+}
+
+
+public extension Coder {
+    
+    var urn: String {
+        switch self {
+        case let .unknown(name):
+            return .coderUrn(name)
+        case .custom(_):
+            return .coderUrn("custom")
+        case .double:
+            return .coderUrn("double")
+        case .varint:
+            return .coderUrn("varint")
+        case .fixedint:
+            return .coderUrn("integer")
+        case .bytes:
+            return .coderUrn("bytes")
+        case .byte:
+            return .coderUrn("byte")
+        case .string:
+            return .coderUrn("string_utf8")
+        case .boolean:
+            return .coderUrn("bool")
+        case .globalwindow:
+            return .coderUrn("global_window")
+        case .keyvalue(_, _):
+            return .coderUrn("kv")
+        case .iterable(_):
+            return .coderUrn("iterable")
+        case .lengthprefix(_):
+            return .coderUrn("length_prefix")
+        case .windowedvalue(_, _):
+            return .coderUrn("windowed_value")
+        }
+    }
+    
+    static let capabilities:[String] = 
["byte","bytes","bool","varint","double","integer","string_utf8","length_prefix","kv","iterable","windowed_value","global_window"]
+        .map({ .coderUrn($0) })
+}
+
+extension Coder : Hashable {
+    public func hash(into hasher: inout Hasher) {
+        hasher.combine(self.urn)
+        switch self {
+        case let .keyvalue(k,v):
+            k.hash(into:&hasher)
+            v.hash(into:&hasher)
+        case let .iterable(c):
+            c.hash(into:&hasher)
+        case let .lengthprefix(c):
+            c.hash(into:&hasher)
+        case let .windowedvalue(v, w):
+            v.hash(into:&hasher)
+            w.hash(into:&hasher)
+        default:
+            break
+        }
+    }
+}
+
+extension Coder : Equatable {
+    public static func ==(_ lhs: Coder,_ rhs: Coder) -> Bool {
+        switch (lhs,rhs) {
+        case let (.unknown(a),.unknown(b)):
+            return a == b
+        case let (.custom(a),.custom(b)):
+            return a == b
+        case (.double,.double):
+            return true
+        case (.varint,.varint):
+            return true
+        case (.fixedint,.fixedint):
+            return true
+        case (.bytes,.bytes):
+            return true
+        case (.byte,.byte):
+            return true
+        case (.string,.string):
+            return true
+        case (.boolean,.boolean):
+            return true
+        case (.globalwindow,.globalwindow):
+            return true
+        case let (.keyvalue(lk,lv),.keyvalue(rk, rv)):
+            return lk == rk && lv == rv
+        case let (.iterable(a),.iterable(b)):
+            return a == b
+        case let (.lengthprefix(a),.lengthprefix(b)):
+            return a == b
+        case let (.windowedvalue(lv, lw),.windowedvalue(rv,rw)):
+            return lv == rv && lw == rw
+        default:
+            return false
+        }
+    }
+}
+
+
+
+protocol CoderContainer {
+    subscript(name:String) -> CoderProto? { get }
+}
+
+struct PipelineCoderContainer : CoderContainer {
+    let pipeline: PipelineProto
+    subscript(name: String) -> CoderProto? {
+        pipeline.components.coders[name]
+    }
+}
+
+struct BundleCoderContainer : CoderContainer {
+    let bundle : ProcessBundleDescriptorProto
+    subscript(name: String) -> CoderProto? {
+        bundle.coders[name]
+    }
+}
+
+extension Coder {
+    static func of(name:String,in container:CoderContainer) -> Coder? {
+        if let baseCoder = container[name] {
+            switch baseCoder.spec.urn {
+            case "beam:coder:bytes:v1":
+                return .bytes
+            case "beam:coder:varint:v1":
+                return .varint
+            case "beam:coder:string_utf8:v1":
+                return .string
+            case "beam:coder:double:v1":
+                return .double
+            case "beam:coder:iterable:v1":
+                return .iterable(.of(name: baseCoder.componentCoderIds[0], in: 
container)!)
+            case "beam:coder:kv:v1":
+                return .keyvalue(
+                    .of(name: baseCoder.componentCoderIds[0],in:container)!,
+                    .of(name: baseCoder.componentCoderIds[1], in:container)!)
+            case "beam:coder:global_window:v1":
+                return .globalwindow
+            case "beam:coder:windowed_value:v1":
+                return .windowedvalue(
+                    .of(name: baseCoder.componentCoderIds[0],in:container)!,
+                    .of(name: baseCoder.componentCoderIds[1],in:container)!)
+            case "beam:coder:length_prefix:v1":
+                return .lengthprefix(.of(name: 
baseCoder.componentCoderIds[0],in:container)!)
+            default:
+                return .unknown(baseCoder.spec.urn)
+            }
+        } else {
+            return nil
+        }
+    }
+}
+
+
diff --git a/sdks/swift/Sources/ApacheBeam/Core/ArtifactInfo.swift 
b/sdks/swift/Sources/ApacheBeam/Core/ArtifactInfo.swift
new file mode 100644
index 00000000000..73c6112d680
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Core/ArtifactInfo.swift
@@ -0,0 +1,31 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF licenses this file
+* to you under the Apache License, Version 2.0 (the
+* "License"); you may not use this file except in compliance
+* with the License.  You may obtain a copy of the License at
+*
+*     http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+public struct ArtifactInfo {
+    let role: String
+    let type: String
+}
+
+extension ArtifactInfo : ProtoConversion {
+    var proto: Org_Apache_Beam_Model_Pipeline_V1_ArtifactInformation {
+        .with {
+            $0.roleUrn = .beamUrn(role,type:"artifact:type")
+            $0.typeUrn = .beamUrn(type,type:"artifact:role")
+        }
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Core/Environment.swift 
b/sdks/swift/Sources/ApacheBeam/Core/Environment.swift
new file mode 100644
index 00000000000..9e45c4575c4
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Core/Environment.swift
@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/// A struct that encodes the different types of available in Beam
+public struct Environment {
+    
+    public enum Category {
+        case system
+        case process(String,String,String)
+        case docker(String)
+        case external(ApiServiceDescriptor)
+    }
+    
+    let category: Category
+    public init(_ category: Category = .system,capabilities:[String] = 
[],dependencies:[ArtifactInfo]) {
+        self.category = category
+    }
+}
+
+
+extension Environment : ProtoConversion {
+    typealias Proto = EnvironmentProto
+    
+    var proto: EnvironmentProto {
+        .with { p in
+        }
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Core/KeyValue.swift 
b/sdks/swift/Sources/ApacheBeam/Core/KeyValue.swift
new file mode 100644
index 00000000000..f8cd8f735c0
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Core/KeyValue.swift
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public protocol AnyKeyValue {
+    var anyKey: Any { get }
+    var anyValues: [Any] { get }
+    var anyValue: Any? { get }
+}
+
+
+/// A structure representing an array of values grouped by key
+public struct KV<Key,Value> : AnyKeyValue {
+    
+    public let key: Key
+    public let values: [Value]
+    public var value: Value? { get { values.first } }
+    
+    public init(_ key: Key,_ value: Value) {
+        self.key = key
+        self.values = [value]
+    }
+    
+    public init(_ key: Key,_ values: [Value]) {
+        self.key = key
+        self.values = values
+    }
+    
+    public init(beam value: BeamValue) throws {
+        switch value {
+        case .windowed(let v, _, _, _):
+            self = try KV<Key,Value>(beam:v)
+            break
+        case let .kv(k, v):
+            self.key = k.baseValue! as! Key
+            self.values = v.baseValue as! [Value]
+        default:
+            throw ApacheBeamError.runtimeError("KV can only accept kv or 
windowed kv types")
+        }
+    }
+    
+    
+    public var anyKey: Any { key }
+    public var anyValues: [Any] { values.map({$0 as Any}) }
+    public var anyValue: Any? { value }
+}
+
+
diff --git a/sdks/swift/Sources/ApacheBeam/Core/Windowing.swift 
b/sdks/swift/Sources/ApacheBeam/Core/Windowing.swift
new file mode 100644
index 00000000000..5727b6c3741
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Core/Windowing.swift
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import Foundation
+
+
+public enum Window {
+    case global
+    case bounded(Date)
+    case interval(Date,Date)
+
+    var maxTimestamp : Date {
+        get {
+            switch(self) {
+                case .global: return Date.distantFuture
+                case .bounded(let end): return end
+                case .interval(_, let end): return end
+            }
+        }
+    }
+}
+
+public enum WindowingStrategy {
+    
+}
+
+public enum Timing : String {
+    case early = "EARLY"
+    case onTime = "ON_TIME"
+    case late = "LATE"
+    case unknown = "UNKNOWN"
+}
+
+public protocol PaneInfo {
+    var timing : Timing { get }
+    var index : UInt64 { get }
+    var onTimeIndex: UInt64 { get }
+    var first : Bool { get }
+    var last : Bool { get }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Internal/Data+Decoding.swift 
b/sdks/swift/Sources/ApacheBeam/Internal/Data+Decoding.swift
new file mode 100644
index 00000000000..706c2036cbf
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Internal/Data+Decoding.swift
@@ -0,0 +1,72 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+extension Data {
+    /// Read a variable length integer from the current data
+    mutating func varint() throws -> Int {
+        var data = self
+        let result = try data.withUnsafeBytes {
+            try $0.baseAddress!.withMemoryRebound(to: UInt8.self, capacity: 4) 
{
+                var p = $0
+                if(p.pointee & 0x80 == 0) {
+                    data = data.advanced(by: 1)
+                    return Int(UInt64(p.pointee))
+                }
+                var value = UInt64(p.pointee & 0x7f)
+                var shift = UInt64(7)
+                var count = 1
+                p = p.successor()
+                while true {
+                    if shift > 63 {
+                        throw ApacheBeamError.runtimeError("Malformed Varint. 
Too large.")
+                    }
+                    count += 1
+                    value |= UInt64(p.pointee & 0x7f) << shift
+                    if(p.pointee & 0x80 == 0) {
+                        data = data.advanced(by: count)
+                        return Int(value)
+                    }
+                    p = p.successor()
+                    shift += 7
+                }
+            }
+        }
+        self = data
+        return result
+    }
+    
+    /// Read a length prefixed chunk of data
+    mutating func subdata() throws -> Data {
+        let length = try self.varint()
+        let result = self.subdata(in: 0..<length)
+        self = self.advanced(by: length)
+        return result
+    }
+    
+    /// Extract a fixed length value from the data
+    mutating func next<T>(_ type: T.Type) throws -> T {
+        let size = MemoryLayout<T>.size
+        let result = self.withUnsafeBytes {
+            $0.baseAddress!.withMemoryRebound(to: T.self, capacity: 1) { 
$0.pointee }
+        }
+        self = self.advanced(by: size)
+        return result
+    }
+    
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Internal/Data+Encoding.swift 
b/sdks/swift/Sources/ApacheBeam/Internal/Data+Encoding.swift
new file mode 100644
index 00000000000..86007b21104
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Internal/Data+Encoding.swift
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+extension Data {
+    mutating func varint(_ value: Int) {
+        var current = UInt64(value)
+        while current >= 0x80 {
+            self.append(UInt8(truncatingIfNeeded: current)|0x80)
+            current >>= 7
+        }
+        self.append(UInt8(current))
+    }
+    
+    mutating func next<T>(_ value: T) {
+        Swift.withUnsafeBytes(of:value) {
+            self.append(contentsOf: $0)
+        }
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Internal/Date+Timestamp.swift 
b/sdks/swift/Sources/ApacheBeam/Internal/Date+Timestamp.swift
new file mode 100644
index 00000000000..16eb658d050
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Internal/Date+Timestamp.swift
@@ -0,0 +1,27 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+extension Date {
+    var millisecondsSince1970 : Int64 {
+        Int64((self.timeIntervalSince1970 * 1000.0).rounded())
+    }
+    init(millisecondsSince1970: Int64) {
+        self = Date(timeIntervalSince1970: TimeInterval(millisecondsSince1970) 
/ 1000)
+    }
+}
diff --git a/sdks/swift/Sources/ApacheBeam/Internal/ProtoConversion.swift 
b/sdks/swift/Sources/ApacheBeam/Internal/ProtoConversion.swift
new file mode 100644
index 00000000000..2a50e950386
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Internal/ProtoConversion.swift
@@ -0,0 +1,25 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+protocol ProtoConversion {
+    associatedtype Proto
+    
+    var proto: Proto { get }
+}
+
+
diff --git a/sdks/swift/Sources/ApacheBeam/Internal/String+Urns.swift 
b/sdks/swift/Sources/ApacheBeam/Internal/String+Urns.swift
new file mode 100644
index 00000000000..48fa8508f5d
--- /dev/null
+++ b/sdks/swift/Sources/ApacheBeam/Internal/String+Urns.swift
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+public extension String {
+    static func beamUrn(_ name:String,type:String="beam",version:String="v1") 
-> String {
+        return "beam:\(type):\(name):\(version)"
+    }
+
+    static func coderUrn(_ name:String,version:String="v1") -> String {
+        return .beamUrn(name,type:"coder",version:version)
+    }
+
+    static func runnerUrn(_ name:String,version:String="v1") -> String {
+        return .beamUrn(name,type:"runner",version:version)
+    }
+
+    static func doFnUrn(_ 
name:String,sdk:String="swiftsdk",version:String="v1") -> String {
+        return .beamUrn(name,type:"dofn:\(sdk)",version:version)
+    }
+
+    static func transformUrn(_ name:String,version:String="v1") -> String {
+        return .beamUrn(name,type:"transform",version:version)
+    }
+}
diff --git a/sdks/swift/Tests/ApacheBeamTests/ApacheBeamTests.swift 
b/sdks/swift/Tests/ApacheBeamTests/ApacheBeamTests.swift
new file mode 100644
index 00000000000..23f85941311
--- /dev/null
+++ b/sdks/swift/Tests/ApacheBeamTests/ApacheBeamTests.swift
@@ -0,0 +1,12 @@
+import XCTest
+@testable import ApacheBeam
+
+final class ApacheBeamTests: XCTestCase {
+    func testExample() throws {
+        // XCTest Documentation
+        // https://developer.apple.com/documentation/xctest
+
+        // Defining Test Cases and Test Methods
+        // 
https://developer.apple.com/documentation/xctest/defining_test_cases_and_test_methods
+    }
+}
diff --git a/sdks/swift/Tests/ApacheBeamTests/Coders/CoderTests.swift 
b/sdks/swift/Tests/ApacheBeamTests/Coders/CoderTests.swift
new file mode 100644
index 00000000000..74031c8b545
--- /dev/null
+++ b/sdks/swift/Tests/ApacheBeamTests/Coders/CoderTests.swift
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import Foundation
+
+
+import XCTest
+@testable import ApacheBeam
+
+final class CoderTests: XCTestCase {
+    func testDefaultImpulseDecode() throws {
+        var impulse = 
Data([0x7f,0xdf,0x3b,0x64,0x5a,0x1c,0xac,0x09,0x00,0x00,0x00,0x01,0x0f,0x00])
+        let impulseCoder = Coder.windowedvalue(.bytes, .globalwindow)
+
+        let value = try impulseCoder.decode(&impulse)
+        switch value {
+        case let .windowed(value, _, _, window):
+            let data = value.baseValue as! Data
+            XCTAssertTrue(data.count == 0)
+            
+            let w = window.baseValue as! Window
+            switch w {
+            case .global:
+                break
+            default:
+                throw ApacheBeamError.runtimeError("Expected window to be 
global not \(w)")
+            }
+            
+        default:
+            throw ApacheBeamError.runtimeError("Expecting a windowed value, 
got \(value)")
+        }
+    }
+    
+    func testWindowedValue() throws {
+        let coder = Coder.windowedvalue(.bytes, .globalwindow)
+        let timestamp = Date.now
+        var data = try coder.encode((Data(),timestamp,Window.global))
+        XCTAssertEqual(data.count, 14)
+        let value = try coder.decode(&data)
+        switch value {
+        case let .windowed(_,ts,_,_):
+            XCTAssertTrue("\(timestamp)" == "\(ts)")
+        default:
+            throw ApacheBeamError.runtimeError("Expected a windowed value, got 
\(value)")
+        }
+    }
+}
+

Reply via email to