THRIFT-4547: Swift crosstests (#2670)

Co-authored-by: Jiayu Liu <Jimexist@users.noreply.github.com>
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 99c92aa..2187776 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -183,6 +183,66 @@
             lib/kotlin/cross-test-server/build/install/TestServer/
           retention-days: 3
 
+  lib-swift:
+    needs: compiler
+    runs-on: ubuntu-20.04
+    steps:
+      - uses: actions/checkout@v3
+      
+      - name: Run bootstrap
+        run: ./bootstrap.sh
+
+      - name: Run configure
+        run: |
+          ./configure \
+            --disable-debug \
+            --disable-tests \
+            --disable-dependency-tracking \
+            --without-cpp \
+            --without-c_glib \
+            --without-java \
+            --without-kotlin \
+            --without-python \
+            --without-py3 \
+            --without-ruby \
+            --without-haxe \
+            --without-netstd \
+            --without-perl \
+            --without-php \
+            --without-php_extension \
+            --without-dart \
+            --without-erlang \
+            --without-go \
+            --without-d \
+            --without-nodejs \
+            --without-nodets \
+            --without-lua \
+            --without-rs \
+            --with-swift
+            
+      - uses: actions/download-artifact@v3
+        with:
+          name: thrift-compiler
+          path: compiler/cpp
+
+      - name: Run thrift-compiler
+        run: |
+          chmod a+x compiler/cpp/thrift
+          compiler/cpp/thrift -version
+          
+      - name: Run make precross for swift
+        run: make -C test/swift precross
+
+      - name: Upload swift precross artifacts
+        uses: actions/upload-artifact@v3
+        with:
+          name: swift-precross
+          if-no-files-found: error
+          path: |
+            test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug/TestServer
+            test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug/TestClient
+          retention-days: 3
+
   lib-rust:
     needs: compiler
     runs-on: ubuntu-20.04
@@ -275,6 +335,7 @@
   cross-test:
     needs:
       - lib-java-kotlin
+      - lib-swift
       - lib-rust
     runs-on: ubuntu-20.04
     steps:
@@ -301,6 +362,12 @@
           name: kotlin-precross
           path: lib/kotlin
 
+      - name: Download swift precross artifacts
+        uses: actions/download-artifact@v3
+        with:
+          name: swift-precross
+          path: test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug
+
       - name: Download rust precross artifacts
         uses: actions/download-artifact@v3
         with:
@@ -313,12 +380,13 @@
             lib/java/build/run* \
             lib/kotlin/cross-test-client/build/install/TestClient/bin/* \
             lib/kotlin/cross-test-server/build/install/TestServer/bin/* \
+            test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug/* \
             test/rs/bin/*
 
       - name: Run cross test
         env:
           THRIFT_CROSSTEST_CONCURRENCY: 4
-          PRECROSS_LANGS: java,kotlin,rs
+          PRECROSS_LANGS: java,kotlin,rs,swift
         run: |
           python test/test.py \
             --retry-count 5 \
diff --git a/.gitignore b/.gitignore
index a469311..d4c5dd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@
 erl_crash.dump
 project.lock.json
 
+.Dockerfile.sha512
 .sonar
 .DS_Store
 .svn
@@ -388,6 +389,7 @@
 /test/rs/target/
 /test/rs/*.iml
 /test/rs/**/*.iml
+/test/swift/CrossTests/.build
 /lib/cl/backport-update.zip
 /lib/cl/lib
 /tutorial/cl/quicklisp.lisp
diff --git a/Makefile.am b/Makefile.am
index 95c610e..505951b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -78,7 +78,7 @@
 space := $(empty) $(empty)
 comma := ,
 
-CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_NETSTD@ @MAYBE_NODETS@ @MAYBE_KOTLIN@
+CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_NETSTD@ @MAYBE_NODETS@ @MAYBE_KOTLIN@ @MAYBE_SWIFT@
 CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS))
 
 if WITH_PY3
diff --git a/compiler/cpp/src/thrift/generate/t_swift_generator.cc b/compiler/cpp/src/thrift/generate/t_swift_generator.cc
index 834e31d..78fb1d8 100644
--- a/compiler/cpp/src/thrift/generate/t_swift_generator.cc
+++ b/compiler/cpp/src/thrift/generate/t_swift_generator.cc
@@ -1067,6 +1067,7 @@
       switch (tbase) {
         case t_base_type::TYPE_STRING:
         case t_base_type::TYPE_DOUBLE:
+        case t_base_type::TYPE_UUID:
           padding = "           ";
           break;
 
@@ -1172,6 +1173,7 @@
         switch (tbase) {
           case t_base_type::TYPE_STRING:
           case t_base_type::TYPE_DOUBLE:
+          case t_base_type::TYPE_UUID:
             padding = "           ";
           break;
 
@@ -2591,6 +2593,8 @@
     return "Int64";
   case t_base_type::TYPE_DOUBLE:
     return "Double";
+   case t_base_type::TYPE_UUID:
+    return "UUID";
   default:
     throw "compiler error: no Swift name for base type " + t_base_type::t_base_name(tbase);
   }
@@ -2629,6 +2633,9 @@
       }
       out << ")";
       break;
+    case t_base_type::TYPE_UUID:
+      out << "UUID(uuidString: \"" << get_escaped_string(value) << "\")";
+      break;
     default:
       throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase);
     }
@@ -3136,6 +3143,8 @@
           return result + "i64";
         case t_base_type::TYPE_DOUBLE:
           return result + "double";
+        case t_base_type::TYPE_UUID:
+          return result + "uuid";
         default:
           throw "compiler error: unhandled type";
       }
@@ -3170,6 +3179,8 @@
           return result + "I64";
         case t_base_type::TYPE_DOUBLE:
           return result + "DOUBLE";
+        case t_base_type::TYPE_UUID:
+          return result + "UUID";
         default:
           throw "compiler error: unhandled type";
       }
diff --git a/configure.ac b/configure.ac
index 9a71fc7..12c20de 100644
--- a/configure.ac
+++ b/configure.ac
@@ -821,6 +821,8 @@
   test/py.tornado/Makefile
   test/rb/Makefile
   test/rs/Makefile
+  test/swift/Makefile
+  test/swift/CrossTests/Makefile
   tutorial/Makefile
   tutorial/c_glib/Makefile
   tutorial/cl/Makefile
diff --git a/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java b/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java
index 8d0c7aa..6657df6 100644
--- a/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java
+++ b/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java
@@ -246,7 +246,7 @@
       first_map.put(Numberz.TWO, argument);
       first_map.put(Numberz.THREE, argument);
 
-      Insanity looney = new Insanity();
+      Insanity looney = new Insanity(new HashMap<>(), Arrays.asList());
       second_map.put(Numberz.SIX, looney);
 
       Map<Long, Map<Numberz, Insanity>> insane = new HashMap<>();
diff --git a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt
index ab415b0..b7f38d7 100644
--- a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt
+++ b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt
@@ -183,7 +183,11 @@
         val secondMap = mutableMapOf<Numberz, Insanity>()
         firstMap[Numberz.TWO] = argument
         firstMap[Numberz.THREE] = argument
-        val looney = Insanity()
+        val looney =
+            Insanity().apply {
+                userMap = HashMap()
+                xtructs = listOf()
+            }
         secondMap[Numberz.SIX] = looney
         val insane: MutableMap<Long, Map<Numberz, Insanity>> = HashMap()
         insane[1L] = firstMap
@@ -241,11 +245,20 @@
         } else if (arg0 == "Xception2") {
             val x = Xception2()
             x.errorCode = 2002
-            x.struct_thing = Xtruct().apply { string_thing = "This is an Xception2" }
+            x.struct_thing =
+                Xtruct().apply {
+                    string_thing = "This is an Xception2"
+                    byte_thing = 0
+                    i32_thing = 0
+                    i64_thing = 0
+                }
             throw x
         }
         val result = Xtruct()
         result.string_thing = arg1
+        result.byte_thing = 0
+        result.i32_thing = 0
+        result.i64_thing = 0
         return result
     }
 
diff --git a/lib/swift/Makefile.am b/lib/swift/Makefile.am
index 6b88b06..6a6644a 100644
--- a/lib/swift/Makefile.am
+++ b/lib/swift/Makefile.am
@@ -30,7 +30,7 @@
 	rm -rf .build
 
 precross:
-	swift
+	swift build
 
 check-local:
 	swift test
diff --git a/lib/swift/Sources/LinuxHelper.swift b/lib/swift/Sources/LinuxHelper.swift
index 66d92bb..83603f8 100644
--- a/lib/swift/Sources/LinuxHelper.swift
+++ b/lib/swift/Sources/LinuxHelper.swift
@@ -24,10 +24,6 @@
 #if os(Linux)
 /// Extensions for Linux for incomplete Foundation API's.
 /// swift-corelibs-foundation is not yet 1:1 with OSX/iOS Foundation
-
-extension CFSocketError {
-  public static let success = kCFSocketSuccess
-}
   
 extension UInt {
   public static func &(lhs: UInt, rhs: Int) -> UInt {
diff --git a/lib/swift/Sources/TBinaryProtocol.swift b/lib/swift/Sources/TBinaryProtocol.swift
index 85acce0..766027e 100644
--- a/lib/swift/Sources/TBinaryProtocol.swift
+++ b/lib/swift/Sources/TBinaryProtocol.swift
@@ -191,6 +191,16 @@
     return buff[0]
   }
   
+  public func read() throws -> Int8 {
+    var buff = Data()
+    try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+      buff = try self.transport.readAll(size: 1)
+    }
+    return buff.withUnsafeBytes { pntr in
+      return pntr.load(as: Int8.self)
+    }
+  }
+  
   public func read() throws -> Int16 {
     var buff = Data()
     try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
@@ -246,6 +256,20 @@
     return data
   }
   
+  public func read() throws -> UUID {
+    let data = try self.transport.readAll(size: 16)
+    let lsb = data[0..<8]
+    let msb = data[8..<16]
+    
+    var id = UUID().uuid
+    withUnsafeMutableBytes(of: &id) { pntr in
+      var copyData = msb
+      copyData.append(lsb)
+      copyData.copyBytes(to: pntr)
+    }
+    return UUID(uuid: id)
+  }
+  
   // Write methods
   
   public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws {
@@ -332,6 +356,14 @@
     }
   }
   
+  public func write(_ value: Int8) throws {
+    var value = value
+    let buff = Data(bytes: &value, count: MemoryLayout<Int8>.size(ofValue: value))
+    try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+      try self.transport.write(data: buff)
+    }
+  }
+  
   public func write(_ value: Int16) throws {
     var buff = Data()
     buff.append(Data([UInt8(0xff & (value >> 8))]))
@@ -381,4 +413,18 @@
       try self.transport.write(data: data)
     }
   }
+  
+  public func write(_ value: UUID) throws {
+    let data = withUnsafePointer(to: value.uuid) {
+      Data(bytes: $0, count: MemoryLayout.size(ofValue: value.uuid))
+    }
+    let msb = data[0..<8]
+    let lsb = data[8..<16]
+    
+    var buff = Data()
+    buff.append(lsb)
+    buff.append(msb)
+    
+    try self.transport.write(data: buff)
+  }
 }
diff --git a/lib/swift/Sources/TCompactProtocol.swift b/lib/swift/Sources/TCompactProtocol.swift
index 81a51f5..482178e 100644
--- a/lib/swift/Sources/TCompactProtocol.swift
+++ b/lib/swift/Sources/TCompactProtocol.swift
@@ -34,6 +34,7 @@
   case set           = 0x0A
   case map           = 0x0B
   case `struct`      = 0x0C
+  case uuid          = 0x0D
   
   public static let typeMask: UInt8 = 0xE0 // 1110 0000
   public static let typeBits: UInt8 = 0x07 // 0000 0111
@@ -188,6 +189,7 @@
     case .set: return .set;
     case .map: return .map;
     case .struct: return .struct;
+    case .uuid: return .uuid;
     }
   }
   
@@ -207,7 +209,8 @@
     case .set:    return .set
     case .list:   return .list
     case .utf8:   return .binary
-    case .utf16:  return .binary
+      //case .utf16:  return .binary
+    case .uuid:   return .uuid
     }
   }
   
@@ -261,7 +264,8 @@
     guard let mtype = TMessageType(rawValue: Int32(type)) else {
       throw TProtocolError(message: "Unknown TMessageType value: \(type)")
     }
-    let sequenceId = try readVarint32()
+    let varint = zigZagToi32(try readVarint32())
+    let sequenceId = Int32(varint)
     let name: String = try read()
     
     return (name, mtype, Int32(sequenceId))
@@ -351,6 +355,16 @@
     return buff
   }
   
+  public func read() throws -> Int8 {
+    var buff = Data()
+    try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) {
+      buff = try self.transport.readAll(size: 1)
+    }
+    return buff.withUnsafeBytes { pntr in
+      return pntr.load(as: Int8.self)
+    }
+  }
+  
   public func read() throws -> Int16 {
     let v = try readVarint32()
     return Int16(zigZagToi32(v))
@@ -381,6 +395,20 @@
     return try readBinary(Int(length))
   }
   
+  public func read() throws -> UUID {
+    let data = try self.transport.readAll(size: 16)
+    let lsb = data[0..<data.count/2]
+    let msb = data[(data.count/2)..<data.count]
+    
+    var id = UUID().uuid
+    withUnsafeMutableBytes(of: &id) { pntr in
+      var copyData = msb
+      copyData.append(lsb)
+      copyData.copyBytes(to: pntr)
+    }
+    return UUID(uuid: id)
+  }
+  
   public func readMapBegin() throws -> (TType, TType, Int32) {
     var keyAndValueType: UInt8 = 8
     let size = try readVarint32()
@@ -423,7 +451,7 @@
                           (UInt8((UInt32(messageType.rawValue) << UInt32(TCType.typeShiftAmount))) &
                           TCType.typeMask)
     try writebyteDirect(nextByte)
-    try writeVarint32(UInt32(sequenceID))
+    try writeVarint32(i32ToZigZag(sequenceID))
     try write(name)
     
     currentMessageName = name
@@ -536,7 +564,15 @@
   public func write(_ value: UInt8) throws {
     try writebyteDirect(value)
   }
-
+  
+  public func write(_ value: Int8) throws {
+    var value = value
+    let buff = Data(bytes: &value, count: MemoryLayout<Int8>.size(ofValue: value))
+    try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) {
+      try self.transport.write(data: buff)
+    }
+  }
+  
   public func write(_ value: Int16) throws {
     try writeVarint32(i32ToZigZag(Int32(value)))
   }
@@ -565,4 +601,18 @@
       try self.transport.write(data: data)
     }
   }
+  
+  public func write(_ value: UUID) throws {
+    let data = withUnsafePointer(to: value.uuid) {
+      Data(bytes: $0, count: MemoryLayout.size(ofValue: value.uuid))
+    }
+    let msb = data[0..<data.count/2]
+    let lsb = data[(data.count/2)..<data.count]
+    
+    var buff = Data()
+    buff.append(lsb)
+    buff.append(msb)
+    
+    try self.transport.write(data: buff)
+  }
 }
diff --git a/lib/swift/Sources/TMap.swift b/lib/swift/Sources/TMap.swift
index 8f52067..7a93630 100644
--- a/lib/swift/Sources/TMap.swift
+++ b/lib/swift/Sources/TMap.swift
@@ -180,5 +180,5 @@
   if lhs.count != rhs.count {
     return false
   }
-  return lhs.storage.elementsEqual(rhs.storage) { $0.key == $1.key && $0.value == $1.value }
+  return lhs.storage == rhs.storage
 }
diff --git a/lib/swift/Sources/TProtocol.swift b/lib/swift/Sources/TProtocol.swift
index b4e5dbe..87e3bc5 100644
--- a/lib/swift/Sources/TProtocol.swift
+++ b/lib/swift/Sources/TProtocol.swift
@@ -41,7 +41,8 @@
   case set      = 14
   case list     = 15
   case utf8     = 16
-  case utf16    = 17
+  //case utf16    = 17
+  case uuid     = 17
 }
 
 public protocol TProtocol {
@@ -65,12 +66,14 @@
   func read() throws -> String
   func read() throws -> Bool
   func read() throws -> UInt8
+  func read() throws -> Int8
   func read() throws -> Int16
   func read() throws -> Int32
   func read() throws -> Int64
   func read() throws -> Double
   func read() throws -> Data
-  
+  func read() throws -> UUID
+    
   // Writing methods
   
   func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws
@@ -90,11 +93,13 @@
   func write(_ value: String) throws
   func write(_ value: Bool) throws
   func write(_ value: UInt8) throws
+  func write(_ value: Int8) throws
   func write(_ value: Int16) throws
   func write(_ value: Int32) throws
   func write(_ value: Int64) throws
   func write(_ value: Double) throws
   func write(_ value: Data) throws
+  func write(_ value: UUID) throws
 }
 
 public extension TProtocol {
@@ -132,12 +137,13 @@
   func skip(type: TType) throws {
     switch type {
     case .bool:   _ = try read() as Bool
-    case .i8:   _ = try read() as UInt8
+    case .i8:   _ = try read() as Int8
     case .i16:    _ = try read() as Int16
     case .i32:    _ = try read() as Int32
     case .i64:    _ = try read() as Int64
     case .double: _ = try read() as Double
     case .string: _ = try read() as String
+    case .uuid: _ = try read() as UUID
       
     case .struct:
       _ = try readStructBegin()
diff --git a/lib/swift/Sources/TProtocolDecorator.swift b/lib/swift/Sources/TProtocolDecorator.swift
index b1b1480..e831f27 100644
--- a/lib/swift/Sources/TProtocolDecorator.swift
+++ b/lib/swift/Sources/TProtocolDecorator.swift
@@ -92,6 +92,10 @@
   func read() throws -> UInt8 {
     return try proto.read()
   }
+    
+  func read() throws -> Int8 {
+    return try proto.read()
+  }
 
   func read() throws -> Int16 {
     return try proto.read()
@@ -112,6 +116,10 @@
   func read() throws -> Data {
     return try proto.read()
   }
+    
+  func read() throws -> UUID {
+    return try proto.read()
+  }
 
   func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws {
     try proto.writeMessageBegin(name: name, type: messageType, sequenceID: sequenceID)
@@ -176,6 +184,10 @@
   func write(_ value: UInt8) throws {
     try proto.write(value)
   }
+    
+  func write(_ value: Int8) throws {
+    try proto.write(value)
+  }
 
   func write(_ value: Int16) throws {
     try proto.write(value)
@@ -196,4 +208,8 @@
   func write(_ value: Data) throws {
     try proto.write(value)
   }
+    
+  func write(_ value: UUID) throws {
+    try proto.write(value)
+  }
 }
diff --git a/lib/swift/Sources/TSerializable.swift b/lib/swift/Sources/TSerializable.swift
index 1374700..ff976eb 100644
--- a/lib/swift/Sources/TSerializable.swift
+++ b/lib/swift/Sources/TSerializable.swift
@@ -61,11 +61,11 @@
   public static var thriftType: TType { return .i8 }
 
   public static func read(from proto: TProtocol) throws -> Int8 {
-    return Int8(try proto.read() as UInt8)
+    return try proto.read() as Int8
   }
 
   public func write(to proto: TProtocol) throws {
-    try proto.write(UInt8(self))
+    try proto.write(Int8(self))
   }
 }
 
@@ -129,3 +129,15 @@
     try proto.write(self)
   }
 }
+
+extension UUID : TSerializable {
+  public static var thriftType: TType { .uuid }
+  
+  public static func read(from proto: TProtocol) throws -> UUID {
+    return try proto.read()
+  }
+  
+  public func write(to proto: TProtocol) throws {
+    try proto.write(self)
+  }
+}
diff --git a/lib/swift/Sources/TSocketServer.swift b/lib/swift/Sources/TSocketServer.swift
index 7debe9f..6cd9adf 100644
--- a/lib/swift/Sources/TSocketServer.swift
+++ b/lib/swift/Sources/TSocketServer.swift
@@ -96,26 +96,21 @@
 
     // throw away our socket
     CFSocketInvalidate(sock)
-
-    // register for notifications of accepted incoming connections
-    _ = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted,
-                                               object: nil, queue: nil) {
-                                                [weak self] notification in
-                                                guard let strongSelf = self else { return }
-                                                guard let clientSocket = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return }
-                                                strongSelf.connectionAccepted(clientSocket)
-    }
-
-    // tell socket to listen
-    socketFileHandle.acceptConnectionInBackgroundAndNotify()
-
+    
     print("TSocketServer: Listening on TCP port \(port)")
+    
+    // tell socket to listen
+    acceptConnectionInBackgroundAndNotify(handle: socketFileHandle)
   }
-
-  deinit {
-    NotificationCenter.default.removeObserver(self)
+  
+  private func acceptConnectionInBackgroundAndNotify(handle: FileHandle) {
+    DispatchQueue(label: "TSocketServer.connectionAccept").async {
+      let acceptedFD = accept(handle.fileDescriptor, nil, nil)
+      DispatchQueue.main.async {
+        self.connectionAccepted(FileHandle(fileDescriptor: acceptedFD))
+      }
+    }
   }
-
   func connectionAccepted(_ clientSocket: FileHandle) {
     // Now that we have a client connected, handle the request on queue
     processingQueue.async {
@@ -123,7 +118,7 @@
     }
 
     // continue accepting connections
-    socketFileHandle.acceptConnectionInBackgroundAndNotify()
+    acceptConnectionInBackgroundAndNotify(handle: socketFileHandle)
   }
 
   open func createTransport(fileHandle: FileHandle) -> TTransport {
@@ -145,7 +140,7 @@
     DispatchQueue.main.async {
       NotificationCenter.default
         .post(name: Notification.Name(rawValue: TSocketServerClientConnectionFinished),
-              object: self,
+              object: nil,
               userInfo: [TSocketServerProcessorKey: self.processor,
                          TSocketServerTransportKey: transport])
     }
diff --git a/lib/swift/Sources/TWrappedProtocol.swift b/lib/swift/Sources/TWrappedProtocol.swift
index 8e8577b..8e47bd5 100644
--- a/lib/swift/Sources/TWrappedProtocol.swift
+++ b/lib/swift/Sources/TWrappedProtocol.swift
@@ -101,6 +101,10 @@
     return try concreteProtocol.read()
   }
   
+  public func read() throws -> Int8 {
+    return try concreteProtocol.read()
+  }
+    
   public func read() throws -> Int16 {
     return try concreteProtocol.read()
   }
@@ -120,6 +124,10 @@
   public func read() throws -> Data {
     return try concreteProtocol.read()
   }
+    
+  public func read() throws -> UUID {
+    return try concreteProtocol.read()
+  }
   
   // Write methods
   
@@ -185,6 +193,10 @@
   public func write(_ value: UInt8) throws {
     try concreteProtocol.write(value)
   }
+  
+  public func write(_ value: Int8) throws {
+    try concreteProtocol.write(value)
+  }
 
   public func write(_ value: Int16) throws {
     try concreteProtocol.write(value)
@@ -205,4 +217,8 @@
   public func write(_ data: Data) throws {
     try concreteProtocol.write(data)
   }
+    
+  public func write(_ value: UUID) throws {
+    try concreteProtocol.write(value)
+  }
 }
diff --git a/test/Makefile.am b/test/Makefile.am
index 478fb2c..d5f1434 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -92,6 +92,11 @@
 PRECROSS_TARGET += precross-rs
 endif
 
+if WITH_SWIFT
+SUBDIRS += swift
+PRECROSS_TARGET += precross-swift
+endif
+
 #
 # generate html for ThriftTest.thrift AND validate it!
 #
@@ -133,6 +138,7 @@
 	py.twisted \
 	rb \
 	rs \
+	swift \
 	threads \
 	partial \
 	AnnotationTest.thrift \
diff --git a/test/swift/CrossTests/Makefile.am b/test/swift/CrossTests/Makefile.am
new file mode 100644
index 0000000..b7d8fbb
--- /dev/null
+++ b/test/swift/CrossTests/Makefile.am
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+TESTTHRIFT=../../ThriftTest.thrift
+
+stubs: $(THRIFT) $(TESTTHRIFT)
+	$(THRIFT) -o Sources/Common --gen swift $(TESTTHRIFT)
+
+precross: stubs
+	swift build
+
+check: stubs
+
+clean-local:
+	$(RM) -r Sources/Common/gen-swift/
+
+dist-hook:
+	$(RM) -r $(distdir)/gen-swift/
diff --git a/test/swift/CrossTests/Package.swift b/test/swift/CrossTests/Package.swift
new file mode 100644
index 0000000..4b3f6cb
--- /dev/null
+++ b/test/swift/CrossTests/Package.swift
@@ -0,0 +1,44 @@
+// swift-tools-version:5.1
+// 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 package = Package(
+    name: "CrossTests",
+    products: [
+        .executable(name: "TestServer", targets: ["TestServer"]),
+        .executable(name: "TestClient", targets: ["TestClient"]),
+    ],
+    dependencies: [
+        // Dependencies declare other packages that this package depends on.
+       .package(path: "../../../lib/swift")
+    ],
+    targets: [
+        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+        // Targets can depend on other targets in this package, and on products in packages which this package depends on.
+        .target(
+            name: "Common",
+            dependencies: ["Thrift"]),
+        .target(
+            name: "TestServer",
+            dependencies: ["Thrift", "Common"]),
+        .target(
+            name: "TestClient",
+            dependencies: ["Thrift", "Common"])
+    ]
+)
diff --git a/test/swift/CrossTests/Sources/Common/Parameters.swift b/test/swift/CrossTests/Sources/Common/Parameters.swift
new file mode 100644
index 0000000..4ef1dcd
--- /dev/null
+++ b/test/swift/CrossTests/Sources/Common/Parameters.swift
@@ -0,0 +1,256 @@
+// 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 Thrift
+
+public enum Protocol: String {
+  case binary
+  case compact
+  case header
+  case json
+}
+
+public enum Transport: String {
+  case buffered
+  case framed
+  case http
+  case anonpipe
+  case zlib
+}
+
+public enum ServerType: String {
+  case simple
+  case threadPool = "thread-pool"
+  case threaded
+  case nonblocking
+}
+
+public enum ParserError: Error {
+  case unknownArgument(argument: String)
+  case missingParameter(argument: String)
+  case invalidParameter(argument: String, parameter: String)
+
+  case unsupportedOption
+}
+
+public class ParametersBase {
+  public var showHelp = false
+  public var port: Int?
+  public var domainSocket: String?
+  public var namedPipe: String?
+  public var proto: Protocol?
+  public var transport: Transport?
+  public var multiplex = false
+  public var abstractNamespace = false
+  public var ssl = false
+  public var zlib = false
+
+  public init (arguments: [String]) throws {
+    if arguments.count > 1 { 
+      for argument in arguments[1...] {
+        let equalSignPos = argument.firstIndex(of: "=") ?? argument.endIndex
+        let name = String(argument[..<equalSignPos])
+        let value: String? = (equalSignPos < argument.endIndex) ? String(argument[argument.index(equalSignPos, offsetBy: 1)..<argument.endIndex]) : nil
+
+        try processArgument(name: name, value: value)
+      }
+    }
+
+    fillDefaults()
+    try checkSupported()
+  }
+
+  open func processArgument(name: String, value: String?) throws {
+    switch name {
+      case "-h", "--help":
+        showHelp = true
+      case "--port":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        port = Int(value!)
+        guard port != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+      case "--domain-socket":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        domainSocket = value!
+      case "--named-pipe":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        namedPipe = value!
+      case "--transport":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        transport = Transport(rawValue: value!)
+        guard transport != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+      case "--protocol":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        proto = Protocol(rawValue: value!)
+        guard proto != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+      case "--multiplex":
+        multiplex = true
+      case "--abstract-namespace":
+        abstractNamespace = true
+      case "--ssl":
+        ssl = true
+      case "--zlib":
+        zlib = true
+      default:
+        throw ParserError.unknownArgument(argument: name)
+    }
+  }
+
+  open func fillDefaults() {
+    if port == nil && domainSocket == nil && namedPipe == nil {
+      port = 9090
+    }
+
+    if transport == nil {
+      transport = .buffered
+    }
+
+    if proto == nil {
+      proto = .binary
+    }
+  }
+
+  open func checkSupported() throws {
+    guard transport == .buffered || transport == .framed else { throw ParserError.unsupportedOption }
+    guard proto == .binary || proto == .compact else { throw ParserError.unsupportedOption }
+  }
+}
+
+public class TestClientParameters: ParametersBase {
+  public var host: String?
+  public var testLoops: Int?
+  public var threads: Int?
+
+  public func printHelp() {
+    print("""
+Allowed options:
+  -h | --help                  produce help message
+  --host=arg (localhost)       Host to connect
+  --port=arg (9090)            Port number to connect
+  --domain-socket=arg          Domain Socket (e.g. /tmp/ThriftTest.thrift),
+                               instead of host and port
+  --named-pipe=arg             Windows Named Pipe (e.g. MyThriftPipe)
+  --anon-pipes hRead hWrite    Windows Anonymous Pipes pair (handles)
+  --abstract-namespace         Create the domain socket in the Abstract Namespace
+                               (no connection with filesystem pathnames)
+  --transport=arg (buffered)   Transport: buffered, framed, http, evhttp, zlib
+  --protocol=arg (binary)      Protocol: binary, compact, header, json
+  --multiplex                  Add TMultiplexedProtocol service name "ThriftTest"
+  --ssl                        Encrypted Transport using SSL
+  --zlib                       Wrap Transport with Zlib
+  -n=arg | --testloops=arg (1) Number of Tests
+  -t=arg | --threads=arg (1)   Number of Test threads
+""")
+  }
+
+  open override func processArgument(name: String, value: String?) throws {
+    switch name {
+      case "--host":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        host = value!
+      case "-n", "--testloops":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        testLoops = Int(value!) 
+        guard testLoops != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+      case "-t", "--threads":
+        guard value != nil else { throw ParserError.missingParameter(argument: name) }
+        threads = Int(value!) 
+        guard threads != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+      default:
+        try super.processArgument(name: name, value: value)
+    }
+  }
+
+  open override func fillDefaults() {
+    super.fillDefaults()
+
+    if host == nil {
+      host = "localhost"
+    }
+
+    if testLoops == nil {
+      testLoops = 1
+    }
+
+    if threads == nil {
+      threads = 4
+    }
+  }
+}
+
+public class TestServerParameters: ParametersBase {
+  public var serverType: ServerType?
+  public var processorEvents = false
+  public var workers: Int?
+
+  public func printHelp() {
+    print("""
+Allowed options:
+  -h | --help                 produce help message
+  --port=arg (=9090)          Port number to listen
+  --domain-socket=arg         Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)
+  --named-pipe=arg            Windows Named Pipe (e.g. MyThriftPipe)
+  --server-type=arg (=simple) type of server, "simple", "thread-pool",
+                              "threaded", or "nonblocking"
+  --transport=arg (=buffered) transport: buffered, framed, http, anonpipe, zlib
+  --protocol=arg (=binary)    protocol: binary, compact, header, json
+  --multiplex                 Add TMultiplexedProtocol service name "ThriftTest"
+  --abstract-namespace        Create the domain socket in the Abstract Namespace 
+                              (no connection with filesystem pathnames)
+  --ssl                       Encrypted Transport using SSL
+  --zlib                      Wrapped Transport using Zlib
+  --processor-events          processor-events
+  -n=arg | --workers=arg (=4) Number of thread pools workers. Only valid for
+                              thread-pool server type
+""")
+  }
+
+  open override func processArgument(name: String, value: String?) throws {
+    switch name {
+    case "--server-type":
+      guard value != nil else { throw ParserError.missingParameter(argument: name) }
+      serverType = ServerType(rawValue: value!)
+      guard serverType != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+    case "--processor-events":
+      processorEvents = true
+    case "-n", "--workers":
+      guard value != nil else { throw ParserError.missingParameter(argument: name) }
+      workers = Int(value!) 
+      guard workers != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+    default:
+      try super.processArgument(name: name, value: value)
+    }
+  }
+  
+  open override func fillDefaults() {
+    super.fillDefaults()
+
+    if serverType == nil {
+      serverType = .simple
+    }
+
+    if workers == nil {
+      workers = 4
+    }
+  }
+
+  open override func checkSupported() throws {
+    try super.checkSupported()
+    guard serverType == .simple else { throw ParserError.unsupportedOption }
+  }
+}
+
diff --git a/test/swift/CrossTests/Sources/TestClient/main.swift b/test/swift/CrossTests/Sources/TestClient/main.swift
new file mode 100644
index 0000000..6817981
--- /dev/null
+++ b/test/swift/CrossTests/Sources/TestClient/main.swift
@@ -0,0 +1,400 @@
+// 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 Thrift
+import Common
+import XCTest
+
+enum Error: Int32 {
+  case baseTypes = 1
+  case structs = 2
+  case containers = 4
+  case exceptions = 8
+  case unknown = 64
+  case timeout = 128
+}
+
+class TestClient {
+  var client: ThriftTestClient
+  var resultCode: Int32 = 0
+
+  public init(parameters: TestClientParameters) throws {
+    let transport = try TestClient.getTransport(parameters: parameters)  
+    let proto = try TestClient.getProtocol(parameters: parameters, transport: transport)
+    client = ThriftTestClient(inoutProtocol: proto)
+  }
+
+  static func getTransport(parameters: TestClientParameters) throws -> TTransport {
+    let socketTransport = try TSocketTransport(hostname: parameters.host!, port: parameters.port!)
+    if parameters.transport == .framed {
+      return TFramedTransport(transport: socketTransport)
+    } 
+
+    if parameters.transport == .buffered {
+      return socketTransport
+    }
+
+    throw ParserError.unsupportedOption
+  }
+
+  static func getProtocol(parameters: TestClientParameters, transport: TTransport) throws -> TProtocol {
+    if parameters.proto == .binary {
+      return TBinaryProtocol(on: transport)
+    } 
+
+    if parameters.proto == .compact {
+      return TCompactProtocol(on: transport)
+    }
+
+    throw ParserError.unsupportedOption
+  }
+
+  func run() throws {
+    do {
+      try testVoid()
+      try testString()
+      try testBool()
+      try testByte()
+      try testI32()
+      try testI64()
+      try testDouble()
+      try testBinary()
+      try testStruct()
+      try testNest()
+      try testMap()
+      try testSet()
+      try testList()
+      try testEnum()
+      try testTypedef()
+      try testMapMap()
+      try testInsanity()
+      try testMulti()
+      try testException()
+      try testMultiException()
+      // Swift generator doesn't yet support one way functions (THRIFT-5468)
+      /*try testOneway()
+       try testOnewayThenNormal()*/
+      try testUuid()
+      
+    } catch let error {
+      print("\(error)")
+      resultCode |= Error.unknown.rawValue
+    }
+    exit(resultCode)
+  }
+
+  func testVoid() throws {
+    print("testVoid")
+    try client.testVoid()
+  }
+
+
+  func testString1(_ s1: String) throws {
+    print("testString(\(s1))")
+    let r1 = try client.testString(thing: s1)
+    print(r1)
+    if s1 != r1 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+  }
+
+  func testString() throws {
+    try testString1(String(repeating: "Python", count: 20))
+    try testString1("")
+    try testString1("\t\n/\\\\\r{}:パイソン")
+    try testString1("""
+Afrikaans, Alemannisch, Aragonés, العربية, مصرى,
+Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška,
+Беларуская, Беларуская (тарашкевіца), Български, Bamanankan,
+বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн,
+Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg,
+Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English,
+Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt,
+Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego,
+Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski,
+Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia,
+Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa,
+ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар,
+Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino,
+Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa
+Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa
+Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪
+Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad,
+Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو,
+Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română,
+Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple
+English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk,
+Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog,
+Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük,
+Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文,
+Bân-lâm-gú, 粵語
+""")
+  }
+  
+  func testBool1(_ s1: Bool) throws {
+    print("testBool(\(s1))")
+    let r1 = try client.testBool(thing: s1)
+    print(r1)
+    if s1 != r1 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+  }
+  
+  func testBool() throws {
+    try testBool1(true)
+    try testBool1(false)
+  }
+  
+  func testByte() throws {
+    print("testByte")
+    if try client.testByte(thing: 63) != 63 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+    if try client.testByte(thing: -127) != -127 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+  }
+  
+  func testI32() throws {
+    print("testI32")
+    if try client.testI32(thing: -1) != -1 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+    if try client.testI32(thing: 0) != 0 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+  }
+  
+  func testI64() throws {
+    print("testI64")
+    if try client.testI64(thing: 1) != 1 ||
+        client.testI64(thing: -34359738368) != -34359738368 {
+      resultCode |= Error.baseTypes.rawValue
+    }
+  }
+  
+  func testDouble() throws {
+    print("testDouble")
+    for testValue in [-5.235098235, 0, -1, -0.000341012439638598279] {
+      if try client.testDouble(thing: testValue) != testValue {
+        resultCode |= Error.baseTypes.rawValue
+      }
+    }
+  }
+  
+  func testBinary() throws {
+    print("testBinary")
+    let val = Data(Array(0...255))
+    if try client.testBinary(thing: val) != val {
+      resultCode |= Error.baseTypes.rawValue
+    }
+  }
+  
+  func testStruct() throws {
+    print("testStruct")
+    let x = Xtruct(string_thing: "Zero", byte_thing: 1, i32_thing: -3, i64_thing: -5)
+    if try client.testStruct(thing: x) != x {
+      resultCode |= Error.structs.rawValue
+    }
+  }
+  
+  func testNest() throws {
+    print("testNest")
+    let inner = Xtruct(string_thing: "Zero", byte_thing: 1, i32_thing: -3, i64_thing: -5)
+    let x = Xtruct2(byte_thing: 0, struct_thing: inner, i32_thing: 0)
+    if try client.testNest(thing: x) != x {
+      resultCode |= Error.structs.rawValue
+    }
+  }
+  
+  func testMap() throws {
+    print("testMap")
+    let x = TMap<Int32, Int32>([0: 1, 1: 2, 2: 3, 3: 4, -1: -2])
+    if try client.testMap(thing: x) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testSet() throws {
+    print("testSet")
+    let x = TSet<Int32>([8, 1, 42])
+    if try client.testSet(thing: x) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testList() throws {
+    print("testList")
+    let x = TList<Int32>([1, 4, 9, -42])
+    if try client.testList(thing: x) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testEnum() throws {
+    print("testEnum")
+    let x = Numberz.five
+    if try client.testEnum(thing: x) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testTypedef() throws {
+    print("testTypedef")
+    let x = UserId(bitPattern: 0xffffffffffffff)
+    if try client.testTypedef(thing: x) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testMapMap() throws {
+    print("testMapMap")
+    let x = TMap<Int32, TMap<Int32, Int32>>([
+      -4: [-4: -4, -3: -3, -2: -2, -1: -1],
+       4: [4: 4, 3: 3, 2: 2, 1: 1]
+    ])
+    if try client.testMapMap(hello: 42) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testInsanity() throws {
+    print("testInsanity()")
+    let argument = Insanity(userMap: [.eight: 8], xtructs: [])
+    let expected = TMap<UserId, TMap<Numberz, Insanity>>([
+      1: [
+        .two: argument,
+        .three: argument
+      ],
+      2: [
+        .six: Insanity(userMap: [:], xtructs: [])
+      ]
+    ])
+    if try client.testInsanity(argument: argument) != expected {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testMulti() throws {
+    print("testMulti")
+    let x = Xtruct(string_thing: "Hello2", byte_thing: 74, i32_thing: 0xff00ff, i64_thing: 0xffffffffd0d0)
+    if try client.testMulti(arg0: x.byte_thing, arg1: x.i32_thing, arg2: x.i64_thing, arg3: .init([0: "abc"]), arg4: Numberz.five, arg5: 0xf0f0f0) != x {
+      resultCode |= Error.containers.rawValue
+    }
+  }
+  
+  func testException() throws {
+    print("testException")
+    try client.testException(arg: "Safe")
+    do {
+      try client.testException(arg: "Xception")
+      resultCode |= Error.exceptions.rawValue
+    } catch let error as Xception {
+      guard error.errorCode == 1001, error.message == "Xception" else {
+        resultCode |= Error.exceptions.rawValue
+        return
+      }
+    } catch {
+      resultCode |= Error.exceptions.rawValue
+    }
+    
+    do {
+      try client.testException(arg: "TException")
+      resultCode |= Error.exceptions.rawValue
+    } catch is TError {
+      
+    } catch {
+      resultCode |= Error.exceptions.rawValue
+    }
+    
+    try client.testException(arg: "success")
+  }
+  
+  func testMultiException() throws {
+    print("testMultiException")
+    do {
+      _ = try client.testMultiException(arg0: "Xception", arg1: "ignore")
+    } catch let error as Xception {
+      guard error.errorCode == 1001, error.message == "This is an Xception" else {
+        resultCode |= Error.exceptions.rawValue
+        return
+      }
+    } catch {
+      resultCode |= Error.exceptions.rawValue
+    }
+    
+    do {
+      _ = try client.testMultiException(arg0: "Xception2", arg1: "ignore")
+    } catch let error as Xception2 {
+      guard error.errorCode == 2002, error.struct_thing.string_thing == "This is an Xception2" else {
+        resultCode |= Error.exceptions.rawValue
+        return
+      }
+    }
+    
+    let y = try client.testMultiException(arg0: "success", arg1: "foobar")
+    if y.string_thing != "foobar" {
+      resultCode |= Error.exceptions.rawValue
+    }
+  }
+  
+  // Swift generator doesn't yet support one way functions (THRIFT-5468)
+  /*func testOneway() throws {
+   print("testOneway")
+   let start = CACurrentMediaTime()
+   try client.testOneway(secondsToSleep: 1)
+   let end = CACurrentMediaTime()
+   let duration = end - start
+   let delta = abs(1 - duration)
+   print("oneway sleep took \(end - start) sec")
+   
+   guard delta < 0.5 else {
+   print("oneway sleep took \(end - start) sec")
+   resultCode |= Error.unknown.rawValue
+   return
+   }
+   }
+   
+   func testOnewayThenNormal() throws {
+   print("testOnewayThenNormal")
+   try client.testOneway(secondsToSleep: 1)
+   if try client.testString(thing: "Swift") != "Swift" {
+   resultCode |= Error.baseTypes.rawValue
+   }
+   }*/
+  
+  func testUuid() throws {
+    let uuid = UUID()
+    guard try client.testUuid(thing: uuid) == uuid else {
+      resultCode |= Error.baseTypes.rawValue
+      return
+    }
+  }
+}
+
+
+let parameters = try TestClientParameters(arguments: CommandLine.arguments)
+    
+if parameters.showHelp {
+  parameters.printHelp()
+  exit(0)
+}
+
+Thread.sleep(forTimeInterval: 1)
+
+try TestClient(parameters: parameters).run()
diff --git a/test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift b/test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift
new file mode 100644
index 0000000..5548e90
--- /dev/null
+++ b/test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift
@@ -0,0 +1,389 @@
+// 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 Thrift
+import Common
+
+class ThriftTestImpl : ThriftTest {
+
+  /// Prints "testVoid()" and returns nothing.
+  ///
+  /// - Throws: 
+  func testVoid() throws {
+    print("testVoid()")
+  }
+
+  /// Prints 'testString("%s")' with thing as '%s'
+  /// @param string thing - the string to print
+  /// @return string - returns the string 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: String
+  /// - Throws: 
+  func testString(thing: String) throws -> String {
+    print("testString(\"\(thing)\")")
+    return thing
+  }
+
+  /// Prints 'testBool("%s")' where '%s' with thing as 'true' or 'false'
+  /// @param bool  thing - the bool data to print
+  /// @return bool  - returns the bool 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Bool
+  /// - Throws: 
+  func testBool(thing: Bool) throws -> Bool {
+    print("testBool\"(\(thing ? "true" : "false")\")")
+    return thing
+  }
+
+  /// Prints 'testByte("%d")' with thing as '%d'
+  /// The types i8 and byte are synonyms, use of i8 is encouraged, byte still exists for the sake of compatibility.
+  /// @param byte thing - the i8/byte to print
+  /// @return i8 - returns the i8/byte 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Int8
+  /// - Throws: 
+  func testByte(thing: Int8) throws -> Int8 {
+    print("testByte(\"\(thing)\")")
+    return thing
+  }
+
+
+  /// Prints 'testI32("%d")' with thing as '%d'
+  /// @param i32 thing - the i32 to print
+  /// @return i32 - returns the i32 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Int32
+  /// - Throws: 
+  func testI32(thing: Int32) throws -> Int32 {
+    print("testI32(\"\(thing)\")")
+    return thing
+  }
+
+
+  /// Prints 'testI64("%d")' with thing as '%d'
+  /// @param i64 thing - the i64 to print
+  /// @return i64 - returns the i64 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Int64
+  /// - Throws: 
+  func testI64(thing: Int64) throws -> Int64 {
+    print("testI64(\"\(thing)\")")
+    return thing
+  }
+
+
+  /// Prints 'testDouble("%f")' with thing as '%f'
+  /// @param double thing - the double to print
+  /// @return double - returns the double 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Double
+  /// - Throws: 
+  func testDouble(thing: Double) throws -> Double {
+    print("testDouble(\"\(thing)\")")
+    return thing
+  }
+
+
+  /// Prints 'testBinary("%s")' where '%s' is a hex-formatted string of thing's data
+  /// @param binary  thing - the binary data to print
+  /// @return binary  - returns the binary 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Data
+  /// - Throws: 
+  func testBinary(thing: Data) throws -> Data {
+    print("testBinary(\"\(thing)\")")
+    return thing
+  }
+
+
+  /// Prints 'testStruct("{%s}")' where thing has been formatted into a string of comma separated values
+  /// @param Xtruct thing - the Xtruct to print
+  /// @return Xtruct - returns the Xtruct 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Xtruct
+  /// - Throws: 
+  func testStruct(thing: Xtruct) throws -> Xtruct {
+    print("testStruct({\([thing.string_thing, "\(thing.byte_thing)", "\(thing.i32_thing)", "\(thing.i64_thing)"].joined(separator: ", "))})")
+    return thing
+  }
+
+
+  /// Prints 'testNest("{%s}")' where thing has been formatted into a string of the nested struct
+  /// @param Xtruct2 thing - the Xtruct2 to print
+  /// @return Xtruct2 - returns the Xtruct2 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Xtruct2
+  /// - Throws: 
+  func testNest(thing: Xtruct2) throws -> Xtruct2 {
+    print("testNest(\(thing)")
+    return thing
+  }
+
+
+  /// Prints 'testMap("{%s")' where thing has been formatted into a string of 'key => value' pairs
+  ///  separated by commas and new lines
+  /// @param map<i32,i32> thing - the map<i32,i32> to print
+  /// @return map<i32,i32> - returns the map<i32,i32> 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: TMap<Int32, Int32>
+  /// - Throws: 
+  func testMap(thing: TMap<Int32, Int32>) throws -> TMap<Int32, Int32> {
+    print("testMap(\(thing)")
+    return thing
+  }
+
+
+  /// Prints 'testStringMap("{%s}")' where thing has been formatted into a string of 'key => value' pairs
+  ///  separated by commas and new lines
+  /// @param map<string,string> thing - the map<string,string> to print
+  /// @return map<string,string> - returns the map<string,string> 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: TMap<String, String>
+  /// - Throws: 
+  func testStringMap(thing: TMap<String, String>) throws -> TMap<String, String> {
+    print("testStringMap(\(thing)")
+    return thing
+  }
+
+
+  /// Prints 'testSet("{%s}")' where thing has been formatted into a string of values
+  ///  separated by commas and new lines
+  /// @param set<i32> thing - the set<i32> to print
+  /// @return set<i32> - returns the set<i32> 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: TSet<Int32>
+  /// - Throws: 
+  func testSet(thing: TSet<Int32>) throws -> TSet<Int32> {
+    print("testSet\(thing)")
+    return thing
+  }
+
+
+  /// Prints 'testList("{%s}")' where thing has been formatted into a string of values
+  ///  separated by commas and new lines
+  /// @param list<i32> thing - the list<i32> to print
+  /// @return list<i32> - returns the list<i32> 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: TList<Int32>
+  /// - Throws: 
+  func testList(thing: TList<Int32>) throws -> TList<Int32> {
+    print("testList\(thing)")
+    return thing
+  }
+
+
+  /// Prints 'testEnum("%d")' where thing has been formatted into its numeric value
+  /// @param Numberz thing - the Numberz to print
+  /// @return Numberz - returns the Numberz 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: Numberz
+  /// - Throws: 
+  func testEnum(thing: Numberz) throws -> Numberz {
+    print("testEnum\(thing.rawValue)")
+    return thing
+  }
+
+
+  /// Prints 'testTypedef("%d")' with thing as '%d'
+  /// @param UserId thing - the UserId to print
+  /// @return UserId - returns the UserId 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: UserId
+  /// - Throws: 
+  func testTypedef(thing: UserId) throws -> UserId {
+    print("testTypedef(\(thing)")
+    return thing
+  }
+
+
+  /// Prints 'testMapMap("%d")' with hello as '%d'
+  /// @param i32 hello - the i32 to print
+  /// @return map<i32,map<i32,i32>> - returns a dictionary with these values:
+  ///   {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, }
+  ///
+  /// - Parameters:
+  ///   - hello: 
+  /// - Returns: TMap<Int32, TMap<Int32, Int32>>
+  /// - Throws: 
+  func testMapMap(hello: Int32) throws -> TMap<Int32, TMap<Int32, Int32>> {
+    print("testMapMap(\(hello)")
+    return TMap<Int32, TMap<Int32, Int32>>([
+      -4: [-4: -4, -3: -3, -2: -2, -1: -1],
+       4: [4: 4, 3: 3, 2: 2, 1: 1]
+    ])
+  }
+
+
+  /// So you think you've got this all worked out, eh?
+  /// Creates a map with these values and prints it out:
+  ///   { 1 => { 2 => argument,
+  ///            3 => argument,
+  ///          },
+  ///     2 => { 6 => <empty Insanity struct>, },
+  ///   }
+  /// @return map<UserId, map<Numberz,Insanity>> - a map with the above values
+  ///
+  /// - Parameters:
+  ///   - argument: 
+  /// - Returns: TMap<UserId, TMap<Numberz, Insanity>>
+  /// - Throws: 
+  func testInsanity(argument: Insanity) throws -> TMap<UserId, TMap<Numberz, Insanity>> {
+    return TMap<UserId, TMap<Numberz, Insanity>>([
+      1: [
+        .two: argument,
+        .three: argument
+      ],
+      2: [
+        .six: Insanity(userMap: [:], xtructs: [])
+      ]
+    ])
+  }
+
+
+  /// Prints 'testMulti()'
+  /// @param i8 arg0 -
+  /// @param i32 arg1 -
+  /// @param i64 arg2 -
+  /// @param map<i16, string> arg3 -
+  /// @param Numberz arg4 -
+  /// @param UserId arg5 -
+  /// @return Xtruct - returns an Xtruct with string_thing = "Hello2, byte_thing = arg0, i32_thing = arg1
+  ///    and i64_thing = arg2
+  ///
+  /// - Parameters:
+  ///   - arg0: 
+  ///   - arg1: 
+  ///   - arg2: 
+  ///   - arg3: 
+  ///   - arg4: 
+  ///   - arg5: 
+  /// - Returns: Xtruct
+  /// - Throws: 
+  func testMulti(arg0: Int8, arg1: Int32, arg2: Int64, arg3: TMap<Int16, String>, arg4: Numberz, arg5: UserId) throws -> Xtruct {
+    print("testMulti()")
+    return Xtruct(string_thing: "Hello2", byte_thing: arg0, i32_thing: arg1, i64_thing: arg2)
+  }
+
+
+  /// Print 'testException(%s)' with arg as '%s'
+  /// @param string arg - a string indication what type of exception to throw
+  /// if arg == "Xception" throw Xception with errorCode = 1001 and message = arg
+  /// else if arg == "TException" throw TException
+  /// else do not throw anything
+  ///
+  /// - Parameters:
+  ///   - arg: 
+  /// - Throws: Xception
+  func testException(arg: String) throws {
+    print("testException(\(arg)")
+    if arg == "Xception" {
+      throw Xception(errorCode: 1001, message: arg)
+    } else if arg == "TException" {
+      throw TApplicationError() // is type TError (TException Swift equiv)
+    }
+  }
+
+
+  /// Print 'testMultiException(%s, %s)' with arg0 as '%s' and arg1 as '%s'
+  /// @param string arg - a string indicating what type of exception to throw
+  /// if arg0 == "Xception" throw Xception with errorCode = 1001 and message = "This is an Xception"
+  /// else if arg0 == "Xception2" throw Xception2 with errorCode = 2002 and struct_thing.string_thing = "This is an Xception2"
+  /// else do not throw anything
+  /// @return Xtruct - an Xtruct with string_thing = arg1
+  ///
+  /// - Parameters:
+  ///   - arg0: 
+  ///   - arg1: 
+  /// - Returns: Xtruct
+  /// - Throws: Xception, Xception2
+  func testMultiException(arg0: String, arg1: String) throws -> Xtruct {
+    print("testMultiException(\(arg0), \(arg1)")
+    if arg0 == "Xception" {
+      throw Xception(errorCode: 1001, message: "This is an Xception")
+    } else if arg0 == "Xception2" {
+      throw Xception2(errorCode: 2002, struct_thing: Xtruct(string_thing: "This is an Xception2", byte_thing: 0, i32_thing: 0, i64_thing: 0))
+    }
+    return Xtruct(string_thing: arg1, byte_thing: 0, i32_thing: 0, i64_thing: 0)
+  }
+
+
+  /// Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d'
+  /// sleep 'secondsToSleep'
+  /// Print 'testOneway(%d): done sleeping!' with secondsToSleep as '%d'
+  /// @param i32 secondsToSleep - the number of seconds to sleep
+  ///
+  /// - Parameters:
+  ///   - secondsToSleep: 
+  /// - Throws: 
+  func testOneway(secondsToSleep: Int32) throws {
+    print("testOneway(\(secondsToSleep): Sleeping...")
+    Thread.sleep(forTimeInterval: TimeInterval(secondsToSleep))
+  }
+  
+  func testUuid(thing: UUID) throws -> UUID {
+    print("testUuid(\(thing))")
+    return thing
+  }
+}
+
+class SecondServiceImpl : SecondService {
+
+  /// Prints 'testString("%s")' with thing as '%s'
+  /// @param string thing - the string to print
+  /// @return string - returns the string 'thing'
+  ///
+  /// - Parameters:
+  ///   - thing: 
+  /// - Returns: String
+  /// - Throws: 
+  func secondtestString(thing: String) throws -> String {
+    print("testString(\"\(thing)\")")
+    return thing
+  }
+}
+
diff --git a/test/swift/CrossTests/Sources/TestServer/main.swift b/test/swift/CrossTests/Sources/TestServer/main.swift
new file mode 100644
index 0000000..15564d3
--- /dev/null
+++ b/test/swift/CrossTests/Sources/TestServer/main.swift
@@ -0,0 +1,59 @@
+// 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 Thrift
+import Common
+
+class TestServer {
+  var server: Any?
+  
+  func run() throws {
+    let parameters = try TestServerParameters(arguments: CommandLine.arguments)
+    
+    if parameters.showHelp {
+      parameters.printHelp()
+      return
+    }
+    
+    let service = ThriftTestImpl()
+    let processor = ThriftTestProcessor(service: service)
+    
+    
+    switch (parameters.proto, parameters.transport) {
+    case (.binary, .buffered):
+      let proto = TBinaryProtocol.self
+      server = try TSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+    case (.binary, .framed):
+      let proto = TBinaryProtocol.self
+      server = try TFramedSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+    case (.compact, .buffered):
+      let proto = TCompactProtocol.self
+      server = try TSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+    case (.compact, .framed):
+      let proto = TCompactProtocol.self
+      server = try TFramedSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+    default:
+      throw ParserError.unsupportedOption
+    }
+  }
+}
+
+let server = TestServer()
+try server.run()
+
+RunLoop.main.run()
diff --git a/test/swift/Makefile.am b/test/swift/Makefile.am
new file mode 100644
index 0000000..20c2fe5
--- /dev/null
+++ b/test/swift/Makefile.am
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+SUBDIRS = CrossTests
+
+precross:
+	$(MAKE) -C CrossTests precross
+
diff --git a/test/tests.json b/test/tests.json
index cce774a..7eb9f65 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -757,5 +757,22 @@
       "buffered"
     ],
     "workdir": "../lib/nodets/test"
+  },
+  {
+    "name": "swift",
+    "server": {
+      "command": ["TestServer"],
+      "workdir": "swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug",
+      "protocols": ["binary", "compact"],
+      "transports": ["buffered", "framed"],
+      "sockets": ["ip"]
+    },
+    "client": {
+      "command": ["TestClient"],
+      "workdir": "swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug",
+      "protocols": ["binary", "compact"],
+      "transports": ["buffered", "framed"],
+      "sockets": ["ip"]
+    }
   }
 ]