[pigeon] Adds Swift support for Pigeon (#976)

diff --git a/packages/pigeon/CHANGELOG.md b/packages/pigeon/CHANGELOG.md
index 9a73a6c..d960395 100644
--- a/packages/pigeon/CHANGELOG.md
+++ b/packages/pigeon/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 3.2.0
+
+* Adds experimental support for Swift generation.
+
 ## 3.1.7
 
 * [java] Adds option to add javax.annotation.Generated annotation.
@@ -37,7 +41,7 @@
 ## 3.0.4
 
 * [objc] Simplified some code output, including avoiding Xcode warnings about
-  using `NSNumber*` directly as boolean value.
+  using `NSNumber*` directly as boolean value.  
 * [tests] Moved test script to enable CI.
 
 ## 3.0.3
diff --git a/packages/pigeon/e2e_tests/test_objc/android/app/src/main/java/io/flutter/plugins/Pigeon.java b/packages/pigeon/e2e_tests/test_objc/android/app/src/main/java/io/flutter/plugins/Pigeon.java
index 0f2aa16..b8bda15b 100644
--- a/packages/pigeon/e2e_tests/test_objc/android/app/src/main/java/io/flutter/plugins/Pigeon.java
+++ b/packages/pigeon/e2e_tests/test_objc/android/app/src/main/java/io/flutter/plugins/Pigeon.java
@@ -1,144 +1,468 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-
-// Autogenerated from Pigeon (v0.1.2), do not edit directly.
+//
+// Autogenerated from Pigeon (v3.0.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 
 package dev.flutter.aaclarke.pigeon;
 
+import android.util.Log;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 import io.flutter.plugin.common.BasicMessageChannel;
 import io.flutter.plugin.common.BinaryMessenger;
+import io.flutter.plugin.common.MessageCodec;
 import io.flutter.plugin.common.StandardMessageCodec;
+import java.io.ByteArrayOutputStream;
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.HashMap;
+import java.util.Map;
 
 /** Generated class from Pigeon. */
-@SuppressWarnings("unused")
-public class Pigeon {
+@SuppressWarnings({"unused", "unchecked", "CodeBlock2Expr", "RedundantSuppression"})
+public class MessagePigeon {
 
-  /** Generated class from Pigeon that represents data sent in messages. */
-  public static class SearchReply {
-    private String result;
+  public enum MessageRequestState {
+    pending(0),
+    success(1),
+    failure(2);
 
-    public String getResult() {
-      return result;
-    }
+    private int index;
 
-    public void setResult(String setterArg) {
-      this.result = setterArg;
-    }
-
-    private String error;
-
-    public String getError() {
-      return error;
-    }
-
-    public void setError(String setterArg) {
-      this.error = setterArg;
-    }
-
-    HashMap toMap() {
-      HashMap<String, Object> toMapResult = new HashMap<>();
-      toMapResult.put("result", result);
-      toMapResult.put("error", error);
-      return toMapResult;
-    }
-
-    static SearchReply fromMap(HashMap map) {
-      SearchReply fromMapResult = new SearchReply();
-      Object result = map.get("result");
-      fromMapResult.result = (String) result;
-      Object error = map.get("error");
-      fromMapResult.error = (String) error;
-      return fromMapResult;
+    private MessageRequestState(final int index) {
+      this.index = index;
     }
   }
 
   /** Generated class from Pigeon that represents data sent in messages. */
-  public static class SearchRequest {
-    private String query;
+  public static class MessageSearchRequest {
+    private @Nullable String query;
 
-    public String getQuery() {
+    public @Nullable String getQuery() {
       return query;
     }
 
-    public void setQuery(String setterArg) {
+    public void setQuery(@Nullable String setterArg) {
       this.query = setterArg;
     }
 
-    private Long anInt;
+    private @Nullable Long anInt;
 
-    public Long getAnInt() {
+    public @Nullable Long getAnInt() {
       return anInt;
     }
 
-    public void setAnInt(Long setterArg) {
+    public void setAnInt(@Nullable Long setterArg) {
       this.anInt = setterArg;
     }
 
-    private Boolean aBool;
+    private @Nullable Boolean aBool;
 
-    public Boolean getABool() {
+    public @Nullable Boolean getABool() {
       return aBool;
     }
 
-    public void setABool(Boolean setterArg) {
+    public void setABool(@Nullable Boolean setterArg) {
       this.aBool = setterArg;
     }
 
-    HashMap toMap() {
-      HashMap<String, Object> toMapResult = new HashMap<>();
+    public static final class Builder {
+      private @Nullable String query;
+
+      public @NonNull Builder setQuery(@Nullable String setterArg) {
+        this.query = setterArg;
+        return this;
+      }
+
+      private @Nullable Long anInt;
+
+      public @NonNull Builder setAnInt(@Nullable Long setterArg) {
+        this.anInt = setterArg;
+        return this;
+      }
+
+      private @Nullable Boolean aBool;
+
+      public @NonNull Builder setABool(@Nullable Boolean setterArg) {
+        this.aBool = setterArg;
+        return this;
+      }
+
+      public @NonNull MessageSearchRequest build() {
+        MessageSearchRequest pigeonReturn = new MessageSearchRequest();
+        pigeonReturn.setQuery(query);
+        pigeonReturn.setAnInt(anInt);
+        pigeonReturn.setABool(aBool);
+        return pigeonReturn;
+      }
+    }
+
+    @NonNull
+    Map<String, Object> toMap() {
+      Map<String, Object> toMapResult = new HashMap<>();
       toMapResult.put("query", query);
       toMapResult.put("anInt", anInt);
       toMapResult.put("aBool", aBool);
       return toMapResult;
     }
 
-    static SearchRequest fromMap(HashMap map) {
-      SearchRequest fromMapResult = new SearchRequest();
+    static @NonNull MessageSearchRequest fromMap(@NonNull Map<String, Object> map) {
+      MessageSearchRequest pigeonResult = new MessageSearchRequest();
       Object query = map.get("query");
-      fromMapResult.query = (String) query;
+      pigeonResult.setQuery((String) query);
       Object anInt = map.get("anInt");
-      fromMapResult.anInt =
-          (anInt == null) ? null : ((anInt instanceof Integer) ? (Integer) anInt : (Long) anInt);
+      pigeonResult.setAnInt(
+          (anInt == null) ? null : ((anInt instanceof Integer) ? (Integer) anInt : (Long) anInt));
       Object aBool = map.get("aBool");
-      fromMapResult.aBool = (Boolean) aBool;
-      return fromMapResult;
+      pigeonResult.setABool((Boolean) aBool);
+      return pigeonResult;
     }
   }
 
   /** Generated class from Pigeon that represents data sent in messages. */
-  public static class Nested {
-    private SearchRequest request;
+  public static class MessageSearchReply {
+    private @Nullable String result;
 
-    public SearchRequest getRequest() {
-      return request;
+    public @Nullable String getResult() {
+      return result;
     }
 
-    public void setRequest(SearchRequest setterArg) {
-      this.request = setterArg;
+    public void setResult(@Nullable String setterArg) {
+      this.result = setterArg;
     }
 
-    HashMap toMap() {
-      HashMap<String, Object> toMapResult = new HashMap<>();
-      toMapResult.put("request", request);
+    private @Nullable String error;
+
+    public @Nullable String getError() {
+      return error;
+    }
+
+    public void setError(@Nullable String setterArg) {
+      this.error = setterArg;
+    }
+
+    private @Nullable MessageRequestState state;
+
+    public @Nullable MessageRequestState getState() {
+      return state;
+    }
+
+    public void setState(@Nullable MessageRequestState setterArg) {
+      this.state = setterArg;
+    }
+
+    public static final class Builder {
+      private @Nullable String result;
+
+      public @NonNull Builder setResult(@Nullable String setterArg) {
+        this.result = setterArg;
+        return this;
+      }
+
+      private @Nullable String error;
+
+      public @NonNull Builder setError(@Nullable String setterArg) {
+        this.error = setterArg;
+        return this;
+      }
+
+      private @Nullable MessageRequestState state;
+
+      public @NonNull Builder setState(@Nullable MessageRequestState setterArg) {
+        this.state = setterArg;
+        return this;
+      }
+
+      public @NonNull MessageSearchReply build() {
+        MessageSearchReply pigeonReturn = new MessageSearchReply();
+        pigeonReturn.setResult(result);
+        pigeonReturn.setError(error);
+        pigeonReturn.setState(state);
+        return pigeonReturn;
+      }
+    }
+
+    @NonNull
+    Map<String, Object> toMap() {
+      Map<String, Object> toMapResult = new HashMap<>();
+      toMapResult.put("result", result);
+      toMapResult.put("error", error);
+      toMapResult.put("state", state == null ? null : state.index);
       return toMapResult;
     }
 
-    static Nested fromMap(HashMap map) {
-      Nested fromMapResult = new Nested();
+    static @NonNull MessageSearchReply fromMap(@NonNull Map<String, Object> map) {
+      MessageSearchReply pigeonResult = new MessageSearchReply();
+      Object result = map.get("result");
+      pigeonResult.setResult((String) result);
+      Object error = map.get("error");
+      pigeonResult.setError((String) error);
+      Object state = map.get("state");
+      pigeonResult.setState(state == null ? null : MessageRequestState.values()[(int) state]);
+      return pigeonResult;
+    }
+  }
+
+  /** Generated class from Pigeon that represents data sent in messages. */
+  public static class MessageNested {
+    private @Nullable MessageSearchRequest request;
+
+    public @Nullable MessageSearchRequest getRequest() {
+      return request;
+    }
+
+    public void setRequest(@Nullable MessageSearchRequest setterArg) {
+      this.request = setterArg;
+    }
+
+    public static final class Builder {
+      private @Nullable MessageSearchRequest request;
+
+      public @NonNull Builder setRequest(@Nullable MessageSearchRequest setterArg) {
+        this.request = setterArg;
+        return this;
+      }
+
+      public @NonNull MessageNested build() {
+        MessageNested pigeonReturn = new MessageNested();
+        pigeonReturn.setRequest(request);
+        return pigeonReturn;
+      }
+    }
+
+    @NonNull
+    Map<String, Object> toMap() {
+      Map<String, Object> toMapResult = new HashMap<>();
+      toMapResult.put("request", (request == null) ? null : request.toMap());
+      return toMapResult;
+    }
+
+    static @NonNull MessageNested fromMap(@NonNull Map<String, Object> map) {
+      MessageNested pigeonResult = new MessageNested();
       Object request = map.get("request");
-      fromMapResult.request = (SearchRequest) request;
-      return fromMapResult;
+      pigeonResult.setRequest(
+          (request == null) ? null : MessageSearchRequest.fromMap((Map) request));
+      return pigeonResult;
+    }
+  }
+
+  private static class MessageApiCodec extends StandardMessageCodec {
+    public static final MessageApiCodec INSTANCE = new MessageApiCodec();
+
+    private MessageApiCodec() {}
+
+    @Override
+    protected Object readValueOfType(byte type, ByteBuffer buffer) {
+      switch (type) {
+        case (byte) 128:
+          return MessageSearchReply.fromMap((Map<String, Object>) readValue(buffer));
+
+        case (byte) 129:
+          return MessageSearchRequest.fromMap((Map<String, Object>) readValue(buffer));
+
+        default:
+          return super.readValueOfType(type, buffer);
+      }
+    }
+
+    @Override
+    protected void writeValue(ByteArrayOutputStream stream, Object value) {
+      if (value instanceof MessageSearchReply) {
+        stream.write(128);
+        writeValue(stream, ((MessageSearchReply) value).toMap());
+      } else if (value instanceof MessageSearchRequest) {
+        stream.write(129);
+        writeValue(stream, ((MessageSearchRequest) value).toMap());
+      } else {
+        super.writeValue(stream, value);
+      }
+    }
+  }
+
+  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+  public interface MessageApi {
+    void initialize();
+
+    @NonNull
+    MessageSearchReply search(@NonNull MessageSearchRequest request);
+
+    /** The codec used by MessageApi. */
+    static MessageCodec<Object> getCodec() {
+      return MessageApiCodec.INSTANCE;
+    }
+
+    /** Sets up an instance of `MessageApi` to handle messages through the `binaryMessenger`. */
+    static void setup(BinaryMessenger binaryMessenger, MessageApi api) {
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.MessageApi.initialize", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                Map<String, Object> wrapped = new HashMap<>();
+                try {
+                  api.initialize();
+                  wrapped.put("result", null);
+                } catch (Error | RuntimeException exception) {
+                  wrapped.put("error", wrapError(exception));
+                }
+                reply.reply(wrapped);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.MessageApi.search", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                Map<String, Object> wrapped = new HashMap<>();
+                try {
+                  ArrayList<Object> args = (ArrayList<Object>) message;
+                  MessageSearchRequest requestArg = (MessageSearchRequest) args.get(0);
+                  if (requestArg == null) {
+                    throw new NullPointerException("requestArg unexpectedly null.");
+                  }
+                  MessageSearchReply output = api.search(requestArg);
+                  wrapped.put("result", output);
+                } catch (Error | RuntimeException exception) {
+                  wrapped.put("error", wrapError(exception));
+                }
+                reply.reply(wrapped);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+    }
+  }
+
+  private static class MessageNestedApiCodec extends StandardMessageCodec {
+    public static final MessageNestedApiCodec INSTANCE = new MessageNestedApiCodec();
+
+    private MessageNestedApiCodec() {}
+
+    @Override
+    protected Object readValueOfType(byte type, ByteBuffer buffer) {
+      switch (type) {
+        case (byte) 128:
+          return MessageNested.fromMap((Map<String, Object>) readValue(buffer));
+
+        case (byte) 129:
+          return MessageSearchReply.fromMap((Map<String, Object>) readValue(buffer));
+
+        case (byte) 130:
+          return MessageSearchRequest.fromMap((Map<String, Object>) readValue(buffer));
+
+        default:
+          return super.readValueOfType(type, buffer);
+      }
+    }
+
+    @Override
+    protected void writeValue(ByteArrayOutputStream stream, Object value) {
+      if (value instanceof MessageNested) {
+        stream.write(128);
+        writeValue(stream, ((MessageNested) value).toMap());
+      } else if (value instanceof MessageSearchReply) {
+        stream.write(129);
+        writeValue(stream, ((MessageSearchReply) value).toMap());
+      } else if (value instanceof MessageSearchRequest) {
+        stream.write(130);
+        writeValue(stream, ((MessageSearchRequest) value).toMap());
+      } else {
+        super.writeValue(stream, value);
+      }
+    }
+  }
+
+  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
+  public interface MessageNestedApi {
+    @NonNull
+    MessageSearchReply search(@NonNull MessageNested nested);
+
+    /** The codec used by MessageNestedApi. */
+    static MessageCodec<Object> getCodec() {
+      return MessageNestedApiCodec.INSTANCE;
+    }
+
+    /**
+     * Sets up an instance of `MessageNestedApi` to handle messages through the `binaryMessenger`.
+     */
+    static void setup(BinaryMessenger binaryMessenger, MessageNestedApi api) {
+      {
+        BasicMessageChannel<Object> channel =
+            new BasicMessageChannel<>(
+                binaryMessenger, "dev.flutter.pigeon.MessageNestedApi.search", getCodec());
+        if (api != null) {
+          channel.setMessageHandler(
+              (message, reply) -> {
+                Map<String, Object> wrapped = new HashMap<>();
+                try {
+                  ArrayList<Object> args = (ArrayList<Object>) message;
+                  MessageNested nestedArg = (MessageNested) args.get(0);
+                  if (nestedArg == null) {
+                    throw new NullPointerException("nestedArg unexpectedly null.");
+                  }
+                  MessageSearchReply output = api.search(nestedArg);
+                  wrapped.put("result", output);
+                } catch (Error | RuntimeException exception) {
+                  wrapped.put("error", wrapError(exception));
+                }
+                reply.reply(wrapped);
+              });
+        } else {
+          channel.setMessageHandler(null);
+        }
+      }
+    }
+  }
+
+  private static class MessageFlutterSearchApiCodec extends StandardMessageCodec {
+    public static final MessageFlutterSearchApiCodec INSTANCE = new MessageFlutterSearchApiCodec();
+
+    private MessageFlutterSearchApiCodec() {}
+
+    @Override
+    protected Object readValueOfType(byte type, ByteBuffer buffer) {
+      switch (type) {
+        case (byte) 128:
+          return MessageSearchReply.fromMap((Map<String, Object>) readValue(buffer));
+
+        case (byte) 129:
+          return MessageSearchRequest.fromMap((Map<String, Object>) readValue(buffer));
+
+        default:
+          return super.readValueOfType(type, buffer);
+      }
+    }
+
+    @Override
+    protected void writeValue(ByteArrayOutputStream stream, Object value) {
+      if (value instanceof MessageSearchReply) {
+        stream.write(128);
+        writeValue(stream, ((MessageSearchReply) value).toMap());
+      } else if (value instanceof MessageSearchRequest) {
+        stream.write(129);
+        writeValue(stream, ((MessageSearchRequest) value).toMap());
+      } else {
+        super.writeValue(stream, value);
+      }
     }
   }
 
   /** Generated class from Pigeon that represents Flutter messages that can be called from Java. */
-  public static class FlutterSearchApi {
+  public static class MessageFlutterSearchApi {
     private final BinaryMessenger binaryMessenger;
 
-    public FlutterSearchApi(BinaryMessenger argBinaryMessenger) {
+    public MessageFlutterSearchApi(BinaryMessenger argBinaryMessenger) {
       this.binaryMessenger = argBinaryMessenger;
     }
 
@@ -146,91 +470,32 @@
       void reply(T reply);
     }
 
-    public void search(SearchRequest argInput, Reply<SearchReply> callback) {
+    static MessageCodec<Object> getCodec() {
+      return MessageFlutterSearchApiCodec.INSTANCE;
+    }
+
+    public void search(
+        @NonNull MessageSearchRequest requestArg, Reply<MessageSearchReply> callback) {
       BasicMessageChannel<Object> channel =
           new BasicMessageChannel<>(
-              binaryMessenger,
-              "dev.flutter.pigeon.FlutterSearchApi.search",
-              new StandardMessageCodec());
-      HashMap inputMap = argInput.toMap();
+              binaryMessenger, "dev.flutter.pigeon.MessageFlutterSearchApi.search", getCodec());
       channel.send(
-          inputMap,
+          new ArrayList<Object>(Arrays.asList(requestArg)),
           channelReply -> {
-            HashMap outputMap = (HashMap) channelReply;
             @SuppressWarnings("ConstantConditions")
-            SearchReply output = SearchReply.fromMap(outputMap);
+            MessageSearchReply output = (MessageSearchReply) channelReply;
             callback.reply(output);
           });
     }
   }
 
-  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
-  public interface NestedApi {
-    SearchReply search(Nested arg);
-
-    /** Sets up an instance of `NestedApi` to handle messages through the `binaryMessenger` */
-    static void setup(BinaryMessenger binaryMessenger, NestedApi api) {
-      {
-        BasicMessageChannel<Object> channel =
-            new BasicMessageChannel<>(
-                binaryMessenger, "dev.flutter.pigeon.NestedApi.search", new StandardMessageCodec());
-        if (api != null) {
-          channel.setMessageHandler(
-              (message, reply) -> {
-                HashMap<String, HashMap> wrapped = new HashMap<>();
-                try {
-                  @SuppressWarnings("ConstantConditions")
-                  Nested input = Nested.fromMap((HashMap) message);
-                  SearchReply output = api.search(input);
-                  wrapped.put("result", output.toMap());
-                } catch (Exception exception) {
-                  wrapped.put("error", wrapError(exception));
-                }
-                reply.reply(wrapped);
-              });
-        } else {
-          channel.setMessageHandler(null);
-        }
-      }
-    }
-  }
-
-  /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
-  public interface Api {
-    SearchReply search(SearchRequest arg);
-
-    /** Sets up an instance of `Api` to handle messages through the `binaryMessenger` */
-    static void setup(BinaryMessenger binaryMessenger, Api api) {
-      {
-        BasicMessageChannel<Object> channel =
-            new BasicMessageChannel<>(
-                binaryMessenger, "dev.flutter.pigeon.Api.search", new StandardMessageCodec());
-        if (api != null) {
-          channel.setMessageHandler(
-              (message, reply) -> {
-                HashMap<String, HashMap> wrapped = new HashMap<>();
-                try {
-                  @SuppressWarnings("ConstantConditions")
-                  SearchRequest input = SearchRequest.fromMap((HashMap) message);
-                  SearchReply output = api.search(input);
-                  wrapped.put("result", output.toMap());
-                } catch (Exception exception) {
-                  wrapped.put("error", wrapError(exception));
-                }
-                reply.reply(wrapped);
-              });
-        } else {
-          channel.setMessageHandler(null);
-        }
-      }
-    }
-  }
-
-  private static HashMap wrapError(Exception exception) {
-    HashMap<String, Object> errorMap = new HashMap<>();
+  private static Map<String, Object> wrapError(Throwable exception) {
+    Map<String, Object> errorMap = new HashMap<>();
     errorMap.put("message", exception.toString());
-    errorMap.put("code", null);
-    errorMap.put("details", null);
+    errorMap.put("code", exception.getClass().getSimpleName());
+    errorMap.put(
+        "details",
+        "Cause: " + exception.getCause() + ", Stacktrace: " + Log.getStackTraceString(exception));
     return errorMap;
   }
 }
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Flutter/AppFrameworkInfo.plist b/packages/pigeon/e2e_tests/test_objc/ios/Flutter/AppFrameworkInfo.plist
index 6b4c0f7..f2872cf 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Flutter/AppFrameworkInfo.plist
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
   <key>CFBundleVersion</key>
   <string>1.0</string>
   <key>MinimumOSVersion</key>
-  <string>8.0</string>
+  <string>9.0</string>
 </dict>
 </plist>
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.pbxproj
index d8ec407..f0e57f3 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 46;
+	objectVersion = 50;
 	objects = {
 
 /* Begin PBXBuildFile section */
@@ -236,7 +236,7 @@
 		97C146E61CF9000F007C117D /* Project object */ = {
 			isa = PBXProject;
 			attributes = {
-				LastUpgradeCheck = 1020;
+				LastUpgradeCheck = 1300;
 				ORGANIZATIONNAME = "";
 				TargetAttributes = {
 					0DD149D023C926D900ABB3D6 = {
@@ -296,9 +296,12 @@
 			files = (
 			);
 			inputPaths = (
+				"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
+				"${BUILT_PRODUCTS_DIR}/e2e/e2e.framework",
 			);
 			name = "[CP] Embed Pods Frameworks";
 			outputPaths = (
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/e2e.framework",
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 			shellPath = /bin/sh;
@@ -424,7 +427,11 @@
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				INFOPLIST_FILE = RunnerTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.aaclarke.RunnerTests;
@@ -448,7 +455,11 @@
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				INFOPLIST_FILE = RunnerTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.aaclarke.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -471,7 +482,11 @@
 				GCC_C_LANGUAGE_STANDARD = gnu11;
 				INFOPLIST_FILE = RunnerTests/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+					"@loader_path/Frameworks",
+				);
 				MTL_FAST_MATH = YES;
 				PRODUCT_BUNDLE_IDENTIFIER = com.google.aaclarke.RunnerTests;
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -521,7 +536,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
@@ -542,7 +557,10 @@
 					"$(PROJECT_DIR)/Flutter",
 				);
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
@@ -600,7 +618,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				MTL_ENABLE_DEBUG_INFO = YES;
 				ONLY_ACTIVE_ARCH = YES;
 				SDKROOT = iphoneos;
@@ -649,7 +667,7 @@
 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
 				GCC_WARN_UNUSED_FUNCTION = YES;
 				GCC_WARN_UNUSED_VARIABLE = YES;
-				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
 				MTL_ENABLE_DEBUG_INFO = NO;
 				SDKROOT = iphoneos;
 				SUPPORTED_PLATFORMS = iphoneos;
@@ -670,7 +688,10 @@
 					"$(PROJECT_DIR)/Flutter",
 				);
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
@@ -693,7 +714,10 @@
 					"$(PROJECT_DIR)/Flutter",
 				);
 				INFOPLIST_FILE = Runner/Info.plist;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)/Flutter",
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
index 1d526a1..919434a 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -2,6 +2,6 @@
 <Workspace
    version = "1.0">
    <FileRef
-      location = "group:Runner.xcodeproj">
+      location = "self:">
    </FileRef>
 </Workspace>
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 595f1c6..80ff1bd 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <Scheme
-   LastUpgradeVersion = "1020"
+   LastUpgradeVersion = "1300"
    version = "1.3">
    <BuildAction
       parallelizeBuildables = "YES"
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner/Info.plist b/packages/pigeon/e2e_tests/test_objc/ios/Runner/Info.plist
index 58bf245..5b82f09 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner/Info.plist
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner/Info.plist
@@ -41,5 +41,7 @@
 	</array>
 	<key>UIViewControllerBasedStatusBarAppearance</key>
 	<false/>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
+	<true/>
 </dict>
 </plist>
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.h b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.h
index bd05670..cf809b8 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.h
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.h
@@ -8,7 +8,7 @@
 NS_ASSUME_NONNULL_BEGIN
 
 /// Implementation of the Pigeon generated interface Api.
-@interface MyApi : NSObject <ACApi>
+@interface MyApi : NSObject <ACMessageApi>
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.m b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.m
index dab746d..e312232 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.m
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyApi.m
@@ -9,14 +9,15 @@
 - (void)initializeWithError:(FlutterError *_Nullable *_Nonnull)error {
 }
 
-- (ACSearchReply *)searchRequest:(ACSearchRequest *)input error:(FlutterError **)error {
+- (ACMessageSearchReply *)searchRequest:(ACMessageSearchRequest *)input
+                                  error:(FlutterError **)error {
   if ([input.query isEqualToString:@"error"]) {
     *error = [FlutterError errorWithCode:@"somecode" message:@"somemessage" details:nil];
     return nil;
   } else {
-    ACSearchReply *reply = [[ACSearchReply alloc] init];
+    ACMessageSearchReply *reply = [[ACMessageSearchReply alloc] init];
     reply.result = [NSString stringWithFormat:@"Hello %@!", input.query];
-    reply.state = ACRequestStateSuccess;
+    reply.state = ACMessageRequestStateSuccess;
     return reply;
   }
 }
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyFlutterViewController.m b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyFlutterViewController.m
index 7ff9fde..9a29a9a 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyFlutterViewController.m
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyFlutterViewController.m
@@ -14,8 +14,8 @@
 
 - (void)viewDidLoad {
   [super viewDidLoad];
-  ACApiSetup(self.engine.binaryMessenger, [[MyApi alloc] init]);
-  ACNestedApiSetup(self.engine.binaryMessenger, [[MyNestedApi alloc] init]);
+  ACMessageApiSetup(self.engine.binaryMessenger, [[MyApi alloc] init]);
+  ACMessageNestedApiSetup(self.engine.binaryMessenger, [[MyNestedApi alloc] init]);
 }
 
 @end
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.h b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.h
index 83980be..431e44a 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.h
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.h
@@ -8,8 +8,9 @@
 NS_ASSUME_NONNULL_BEGIN
 
 /// Implementation of the Pigeon generated interface NestedApi.
-@interface MyNestedApi : NSObject <ACNestedApi>
-- (ACSearchReply *)search:(ACNested *)input error:(FlutterError *_Nullable *_Nonnull)error;
+@interface MyNestedApi : NSObject <ACMessageNestedApi>
+- (ACMessageSearchReply *)search:(ACMessageNested *)input
+                           error:(FlutterError *_Nullable *_Nonnull)error;
 @end
 
 NS_ASSUME_NONNULL_END
diff --git a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.m b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.m
index 5d1ea1d..77bc34d 100644
--- a/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.m
+++ b/packages/pigeon/e2e_tests/test_objc/ios/Runner/MyNestedApi.m
@@ -5,10 +5,10 @@
 #import "MyNestedApi.h"
 
 @implementation MyNestedApi
-- (ACSearchReply *)searchNested:(ACNested *)input error:(FlutterError **)error {
-  ACSearchReply *reply = [[ACSearchReply alloc] init];
+- (ACMessageSearchReply *)searchNested:(ACMessageNested *)input error:(FlutterError **)error {
+  ACMessageSearchReply *reply = [[ACMessageSearchReply alloc] init];
   reply.result = [NSString stringWithFormat:@"Hello %@!", input.request.query];
-  reply.state = ACRequestStateSuccess;
+  reply.state = ACMessageRequestStateSuccess;
   return reply;
 }
 @end
diff --git a/packages/pigeon/e2e_tests/test_objc/lib/dartle.dart b/packages/pigeon/e2e_tests/test_objc/lib/dartle.dart
index 7c408d4..c62520a 100644
--- a/packages/pigeon/e2e_tests/test_objc/lib/dartle.dart
+++ b/packages/pigeon/e2e_tests/test_objc/lib/dartle.dart
@@ -1,47 +1,30 @@
 // Copyright 2013 The Flutter Authors. All rights reserved.
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
-
-// Autogenerated from Pigeon (v0.2.1), do not edit directly.
+//
+// Autogenerated from Pigeon (v3.0.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
-// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types
+// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
 import 'dart:async';
 import 'dart:typed_data' show Uint8List, Int32List, Int64List, Float64List;
 
+import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
 import 'package:flutter/services.dart';
 
-enum RequestState {
+enum MessageRequestState {
   pending,
   success,
   failure,
 }
 
-class SearchReply {
-  String? result;
-  String? error;
-  RequestState? state;
+class MessageSearchRequest {
+  MessageSearchRequest({
+    this.query,
+    this.anInt,
+    this.aBool,
+  });
 
-  Object encode() {
-    final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
-    pigeonMap['result'] = result;
-    pigeonMap['error'] = error;
-    pigeonMap['state'] = state == null ? null : state!.index;
-    return pigeonMap;
-  }
-
-  static SearchReply decode(Object message) {
-    final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchReply()
-      ..result = pigeonMap['result'] as String?
-      ..error = pigeonMap['error'] as String?
-      ..state = pigeonMap['state'] != null
-          ? RequestState.values[pigeonMap['state']! as int]
-          : null;
-  }
-}
-
-class SearchRequest {
   String? query;
   int? anInt;
   bool? aBool;
@@ -54,101 +37,114 @@
     return pigeonMap;
   }
 
-  static SearchRequest decode(Object message) {
+  static MessageSearchRequest decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequest()
-      ..query = pigeonMap['query'] as String?
-      ..anInt = pigeonMap['anInt'] as int?
-      ..aBool = pigeonMap['aBool'] as bool?;
+    return MessageSearchRequest(
+      query: pigeonMap['query'] as String?,
+      anInt: pigeonMap['anInt'] as int?,
+      aBool: pigeonMap['aBool'] as bool?,
+    );
   }
 }
 
-class Nested {
-  SearchRequest? request;
+class MessageSearchReply {
+  MessageSearchReply({
+    this.result,
+    this.error,
+    this.state,
+  });
+
+  String? result;
+  String? error;
+  MessageRequestState? state;
 
   Object encode() {
     final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
-    pigeonMap['request'] = request == null ? null : request!.encode();
+    pigeonMap['result'] = result;
+    pigeonMap['error'] = error;
+    pigeonMap['state'] = state?.index;
     return pigeonMap;
   }
 
-  static Nested decode(Object message) {
+  static MessageSearchReply decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return Nested()
-      ..request = pigeonMap['request'] != null
-          ? SearchRequest.decode(pigeonMap['request']!)
-          : null;
+    return MessageSearchReply(
+      result: pigeonMap['result'] as String?,
+      error: pigeonMap['error'] as String?,
+      state: pigeonMap['state'] != null
+          ? MessageRequestState.values[pigeonMap['state']! as int]
+          : null,
+    );
   }
 }
 
-abstract class FlutterSearchApi {
-  SearchReply search(SearchRequest arg);
-  static void setup(FlutterSearchApi? api) {
-    {
-      const BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.FlutterSearchApi.search', StandardMessageCodec());
-      if (api == null) {
-        channel.setMessageHandler(null);
-      } else {
-        channel.setMessageHandler((Object? message) async {
-          assert(message != null,
-              'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null. Expected SearchRequest.');
-          final SearchRequest input = SearchRequest.decode(message!);
-          final SearchReply output = api.search(input);
-          return output.encode();
-        });
-      }
+class MessageNested {
+  MessageNested({
+    this.request,
+  });
+
+  MessageSearchRequest? request;
+
+  Object encode() {
+    final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
+    pigeonMap['request'] = request?.encode();
+    return pigeonMap;
+  }
+
+  static MessageNested decode(Object message) {
+    final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
+    return MessageNested(
+      request: pigeonMap['request'] != null
+          ? MessageSearchRequest.decode(pigeonMap['request']!)
+          : null,
+    );
+  }
+}
+
+class _MessageApiCodec extends StandardMessageCodec {
+  const _MessageApiCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is MessageSearchReply) {
+      buffer.putUint8(128);
+      writeValue(buffer, value.encode());
+    } else if (value is MessageSearchRequest) {
+      buffer.putUint8(129);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
+    }
+  }
+
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 128:
+        return MessageSearchReply.decode(readValue(buffer)!);
+
+      case 129:
+        return MessageSearchRequest.decode(readValue(buffer)!);
+
+      default:
+        return super.readValueOfType(type, buffer);
     }
   }
 }
 
-class NestedApi {
-  /// Constructor for [NestedApi].  The [binaryMessenger] named argument is
+class MessageApi {
+  /// Constructor for [MessageApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
-  NestedApi({BinaryMessenger? binaryMessenger})
+  MessageApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
 
   final BinaryMessenger? _binaryMessenger;
 
-  Future<SearchReply> search(Nested arg) async {
-    final Object encoded = arg.encode();
-    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.NestedApi.search', const StandardMessageCodec(),
-        binaryMessenger: _binaryMessenger);
-    final Map<Object?, Object?>? replyMap =
-        await channel.send(encoded) as Map<Object?, Object?>?;
-    if (replyMap == null) {
-      throw PlatformException(
-        code: 'channel-error',
-        message: 'Unable to establish connection on channel.',
-        details: null,
-      );
-    } else if (replyMap['error'] != null) {
-      final Map<Object?, Object?> error =
-          (replyMap['error'] as Map<Object?, Object?>?)!;
-      throw PlatformException(
-        code: (error['code'] as String?)!,
-        message: error['message'] as String?,
-        details: error['details'],
-      );
-    } else {
-      return SearchReply.decode(replyMap['result']!);
-    }
-  }
-}
-
-class Api {
-  /// Constructor for [Api].  The [binaryMessenger] named argument is
-  /// available for dependency injection.  If it is left null, the default
-  /// BinaryMessenger will be used which routes to the host platform.
-  Api({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger;
-
-  final BinaryMessenger? _binaryMessenger;
+  static const MessageCodec<Object?> codec = _MessageApiCodec();
 
   Future<void> initialize() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.Api.initialize', const StandardMessageCodec(),
+        'dev.flutter.pigeon.MessageApi.initialize', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(null) as Map<Object?, Object?>?;
@@ -156,7 +152,6 @@
       throw PlatformException(
         code: 'channel-error',
         message: 'Unable to establish connection on channel.',
-        details: null,
       );
     } else if (replyMap['error'] != null) {
       final Map<Object?, Object?> error =
@@ -167,22 +162,20 @@
         details: error['details'],
       );
     } else {
-      // noop
+      return;
     }
   }
 
-  Future<SearchReply> search(SearchRequest arg) async {
-    final Object encoded = arg.encode();
+  Future<MessageSearchReply> search(MessageSearchRequest arg_request) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.Api.search', const StandardMessageCodec(),
+        'dev.flutter.pigeon.MessageApi.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
-        await channel.send(encoded) as Map<Object?, Object?>?;
+        await channel.send(<Object?>[arg_request]) as Map<Object?, Object?>?;
     if (replyMap == null) {
       throw PlatformException(
         code: 'channel-error',
         message: 'Unable to establish connection on channel.',
-        details: null,
       );
     } else if (replyMap['error'] != null) {
       final Map<Object?, Object?> error =
@@ -192,8 +185,149 @@
         message: error['message'] as String?,
         details: error['details'],
       );
+    } else if (replyMap['result'] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
     } else {
-      return SearchReply.decode(replyMap['result']!);
+      return (replyMap['result'] as MessageSearchReply?)!;
+    }
+  }
+}
+
+class _MessageNestedApiCodec extends StandardMessageCodec {
+  const _MessageNestedApiCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is MessageNested) {
+      buffer.putUint8(128);
+      writeValue(buffer, value.encode());
+    } else if (value is MessageSearchReply) {
+      buffer.putUint8(129);
+      writeValue(buffer, value.encode());
+    } else if (value is MessageSearchRequest) {
+      buffer.putUint8(130);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
+    }
+  }
+
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 128:
+        return MessageNested.decode(readValue(buffer)!);
+
+      case 129:
+        return MessageSearchReply.decode(readValue(buffer)!);
+
+      case 130:
+        return MessageSearchRequest.decode(readValue(buffer)!);
+
+      default:
+        return super.readValueOfType(type, buffer);
+    }
+  }
+}
+
+class MessageNestedApi {
+  /// Constructor for [MessageNestedApi].  The [binaryMessenger] named argument is
+  /// available for dependency injection.  If it is left null, the default
+  /// BinaryMessenger will be used which routes to the host platform.
+  MessageNestedApi({BinaryMessenger? binaryMessenger})
+      : _binaryMessenger = binaryMessenger;
+
+  final BinaryMessenger? _binaryMessenger;
+
+  static const MessageCodec<Object?> codec = _MessageNestedApiCodec();
+
+  Future<MessageSearchReply> search(MessageNested arg_nested) async {
+    final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+        'dev.flutter.pigeon.MessageNestedApi.search', codec,
+        binaryMessenger: _binaryMessenger);
+    final Map<Object?, Object?>? replyMap =
+        await channel.send(<Object?>[arg_nested]) as Map<Object?, Object?>?;
+    if (replyMap == null) {
+      throw PlatformException(
+        code: 'channel-error',
+        message: 'Unable to establish connection on channel.',
+      );
+    } else if (replyMap['error'] != null) {
+      final Map<Object?, Object?> error =
+          (replyMap['error'] as Map<Object?, Object?>?)!;
+      throw PlatformException(
+        code: (error['code'] as String?)!,
+        message: error['message'] as String?,
+        details: error['details'],
+      );
+    } else if (replyMap['result'] == null) {
+      throw PlatformException(
+        code: 'null-error',
+        message: 'Host platform returned null value for non-null return value.',
+      );
+    } else {
+      return (replyMap['result'] as MessageSearchReply?)!;
+    }
+  }
+}
+
+class _MessageFlutterSearchApiCodec extends StandardMessageCodec {
+  const _MessageFlutterSearchApiCodec();
+  @override
+  void writeValue(WriteBuffer buffer, Object? value) {
+    if (value is MessageSearchReply) {
+      buffer.putUint8(128);
+      writeValue(buffer, value.encode());
+    } else if (value is MessageSearchRequest) {
+      buffer.putUint8(129);
+      writeValue(buffer, value.encode());
+    } else {
+      super.writeValue(buffer, value);
+    }
+  }
+
+  @override
+  Object? readValueOfType(int type, ReadBuffer buffer) {
+    switch (type) {
+      case 128:
+        return MessageSearchReply.decode(readValue(buffer)!);
+
+      case 129:
+        return MessageSearchRequest.decode(readValue(buffer)!);
+
+      default:
+        return super.readValueOfType(type, buffer);
+    }
+  }
+}
+
+abstract class MessageFlutterSearchApi {
+  static const MessageCodec<Object?> codec = _MessageFlutterSearchApiCodec();
+
+  MessageSearchReply search(MessageSearchRequest request);
+  static void setup(MessageFlutterSearchApi? api,
+      {BinaryMessenger? binaryMessenger}) {
+    {
+      final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
+          'dev.flutter.pigeon.MessageFlutterSearchApi.search', codec,
+          binaryMessenger: binaryMessenger);
+      if (api == null) {
+        channel.setMessageHandler(null);
+      } else {
+        channel.setMessageHandler((Object? message) async {
+          assert(message != null,
+              'Argument for dev.flutter.pigeon.MessageFlutterSearchApi.search was null.');
+          final List<Object?> args = (message as List<Object?>?)!;
+          final MessageSearchRequest? arg_request =
+              (args[0] as MessageSearchRequest?);
+          assert(arg_request != null,
+              'Argument for dev.flutter.pigeon.MessageFlutterSearchApi.search was null, expected non-null MessageSearchRequest.');
+          final MessageSearchReply output = api.search(arg_request!);
+          return output;
+        });
+      }
     }
   }
 }
diff --git a/packages/pigeon/e2e_tests/test_objc/lib/main.dart b/packages/pigeon/e2e_tests/test_objc/lib/main.dart
index 1157059..fc453f7 100644
--- a/packages/pigeon/e2e_tests/test_objc/lib/main.dart
+++ b/packages/pigeon/e2e_tests/test_objc/lib/main.dart
@@ -5,16 +5,16 @@
 import 'package:flutter/material.dart';
 import 'dartle.dart';
 
-class _MyFlutterSearchApi extends FlutterSearchApi {
+class _MyFlutterSearchApi extends MessageFlutterSearchApi {
   @override
-  SearchReply search(SearchRequest input) {
-    return SearchReply()..result = 'Hello ${input.query}, from Flutter';
+  MessageSearchReply search(MessageSearchRequest input) {
+    return MessageSearchReply()..result = 'Hello ${input.query}, from Flutter';
   }
 }
 
 void main() {
   WidgetsFlutterBinding.ensureInitialized();
-  FlutterSearchApi.setup(_MyFlutterSearchApi());
+  MessageFlutterSearchApi.setup(_MyFlutterSearchApi());
   runApp(const MyApp());
 }
 
@@ -45,12 +45,13 @@
 
 class _MyHomePageState extends State<_MyHomePage> {
   String _message = '';
-  RequestState _state = RequestState.pending;
+  MessageRequestState _state = MessageRequestState.pending;
 
   Future<void> _incrementCounter() async {
-    final SearchRequest request = SearchRequest()..query = 'Aaron';
-    final Api api = Api();
-    final SearchReply reply = await api.search(request);
+    final MessageSearchRequest request = MessageSearchRequest()
+      ..query = 'Aaron';
+    final MessageApi api = MessageApi();
+    final MessageSearchReply reply = await api.search(request);
     setState(() {
       _message = reply.result;
       _state = reply.state;
diff --git a/packages/pigeon/e2e_tests/test_objc/test_driver/e2e_test.dart b/packages/pigeon/e2e_tests/test_objc/test_driver/e2e_test.dart
index e86f760..406e9e1 100644
--- a/packages/pigeon/e2e_tests/test_objc/test_driver/e2e_test.dart
+++ b/packages/pigeon/e2e_tests/test_objc/test_driver/e2e_test.dart
@@ -9,26 +9,29 @@
 void main() {
   E2EWidgetsFlutterBinding.ensureInitialized();
   testWidgets('simple call', (WidgetTester tester) async {
-    final SearchRequest request = SearchRequest()..query = 'Aaron';
-    final Api api = Api();
-    final SearchReply reply = await api.search(request);
+    final MessageSearchRequest request = MessageSearchRequest()
+      ..query = 'Aaron';
+    final MessageApi api = MessageApi();
+    final MessageSearchReply reply = await api.search(request);
     expect(reply.result, equals('Hello Aaron!'));
-    expect(reply.state, equals(RequestState.success));
+    expect(reply.state, equals(MessageRequestState.success));
   });
 
   testWidgets('simple nested', (WidgetTester tester) async {
-    final SearchRequest request = SearchRequest()..query = 'Aaron';
-    final Nested nested = Nested()..request = request;
-    final NestedApi api = NestedApi();
-    final SearchReply reply = await api.search(nested);
+    final MessageSearchRequest request = MessageSearchRequest()
+      ..query = 'Aaron';
+    final MessageNested nested = MessageNested()..request = request;
+    final MessageNestedApi api = MessageNestedApi();
+    final MessageSearchReply reply = await api.search(nested);
     expect(reply.result, equals('Hello Aaron!'));
-    expect(reply.state, equals(RequestState.success));
+    expect(reply.state, equals(MessageRequestState.success));
   });
 
   testWidgets('throws', (WidgetTester tester) async {
-    final SearchRequest request = SearchRequest()..query = 'error';
-    final Api api = Api();
-    SearchReply reply;
+    final MessageSearchRequest request = MessageSearchRequest()
+      ..query = 'error';
+    final MessageApi api = MessageApi();
+    MessageSearchReply reply;
     expect(() async {
       reply = await api.search(request);
     }, throwsException);
diff --git a/packages/pigeon/example/README.md b/packages/pigeon/example/README.md
index 854a750..d8f1cde 100644
--- a/packages/pigeon/example/README.md
+++ b/packages/pigeon/example/README.md
@@ -37,6 +37,7 @@
   --dart_out lib/pigeon.dart \
   --objc_header_out ios/Runner/pigeon.h \
   --objc_source_out ios/Runner/pigeon.m \
+  --experimental_swift_out ios/Runner/Pigeon.swift \
   --java_out ./android/app/src/main/java/dev/flutter/pigeon/Pigeon.java \
   --java_package "dev.flutter.pigeon"
 ```
@@ -74,6 +75,32 @@
 @end
 ```
 
+### AppDelegate.swift
+
+This is the code that will use the generated Swift code to receive calls from Flutter.
+
+```swift
+import Flutter
+
+class MyApi: NSObject, BookApi {
+  func search(keyword: String) -> [Book] {
+    let result = Book(title: "\(keyword)'s Life", author: keyword)
+    return [result]
+  }
+}
+
+class AppDelegate {
+  override func application(
+    _ application: UIApplication,
+    didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
+  ) -> Bool {
+    let api = MyApi()
+    BookApiSetup.setUp(getFlutterEngine().binaryMessenger, api)
+    return true
+  }
+}
+```
+
 ### StartActivity.java
 
 This is the code that will use the generated Java code to receive calls from Flutter.
diff --git a/packages/pigeon/lib/generator_tools.dart b/packages/pigeon/lib/generator_tools.dart
index 0da3112..172e5ff 100644
--- a/packages/pigeon/lib/generator_tools.dart
+++ b/packages/pigeon/lib/generator_tools.dart
@@ -9,7 +9,7 @@
 import 'ast.dart';
 
 /// The current version of pigeon. This must match the version in pubspec.yaml.
-const String pigeonVersion = '3.1.7';
+const String pigeonVersion = '3.2.0';
 
 /// Read all the content from [stdin] to a String.
 String readStdin() {
diff --git a/packages/pigeon/lib/pigeon_lib.dart b/packages/pigeon/lib/pigeon_lib.dart
index f784c1b..a05dffe 100644
--- a/packages/pigeon/lib/pigeon_lib.dart
+++ b/packages/pigeon/lib/pigeon_lib.dart
@@ -22,6 +22,7 @@
 import 'package:pigeon/cpp_generator.dart';
 import 'package:pigeon/generator_tools.dart';
 import 'package:pigeon/java_generator.dart';
+import 'package:pigeon/swift_generator.dart';
 
 import 'ast.dart';
 import 'ast_generator.dart';
@@ -158,6 +159,8 @@
       this.objcOptions,
       this.javaOut,
       this.javaOptions,
+      this.swiftOut,
+      this.swiftOptions,
       this.cppHeaderOut,
       this.cppSourceOut,
       this.cppOptions,
@@ -191,6 +194,12 @@
   /// Options that control how Java will be generated.
   final JavaOptions? javaOptions;
 
+  /// Path to the swift file that will be generated.
+  final String? swiftOut;
+
+  /// Options that control how Swift will be generated.
+  final SwiftOptions? swiftOptions;
+
   /// Path to the ".h" C++ file that will be generated.
   final String? cppHeaderOut;
 
@@ -231,6 +240,10 @@
       javaOptions: map.containsKey('javaOptions')
           ? JavaOptions.fromMap((map['javaOptions'] as Map<String, Object>?)!)
           : null,
+      swiftOut: map['swiftOut'] as String?,
+      swiftOptions: map.containsKey('swiftOptions')
+          ? SwiftOptions.fromMap((map['swiftOptions'] as Map<String, Object>?)!)
+          : null,
       cppHeaderOut: map['experimental_cppHeaderOut'] as String?,
       cppSourceOut: map['experimental_cppSourceOut'] as String?,
       cppOptions: map.containsKey('experimental_cppOptions')
@@ -259,6 +272,8 @@
       if (objcOptions != null) 'objcOptions': objcOptions!.toMap(),
       if (javaOut != null) 'javaOut': javaOut!,
       if (javaOptions != null) 'javaOptions': javaOptions!.toMap(),
+      if (swiftOut != null) 'swiftOut': swiftOut!,
+      if (swiftOptions != null) 'swiftOptions': swiftOptions!.toMap(),
       if (cppHeaderOut != null) 'experimental_cppHeaderOut': cppHeaderOut!,
       if (cppSourceOut != null) 'experimental_cppSourceOut': cppSourceOut!,
       if (cppOptions != null) 'experimental_cppOptions': cppOptions!.toMap(),
@@ -491,6 +506,28 @@
   List<Error> validate(PigeonOptions options, Root root) => <Error>[];
 }
 
+/// A [Generator] that generates Swift source code.
+class SwiftGenerator implements Generator {
+  /// Constructor for [SwiftGenerator].
+  const SwiftGenerator();
+
+  @override
+  void generate(StringSink sink, PigeonOptions options, Root root) {
+    SwiftOptions swiftOptions = options.swiftOptions ?? const SwiftOptions();
+    swiftOptions = swiftOptions.merge(SwiftOptions(
+        copyrightHeader: options.copyrightHeader != null
+            ? _lineReader(options.copyrightHeader!)
+            : null));
+    generateSwift(swiftOptions, root, sink);
+  }
+
+  @override
+  IOSink? shouldGenerate(PigeonOptions options) => _openSink(options.swiftOut);
+
+  @override
+  List<Error> validate(PigeonOptions options, Root root) => <Error>[];
+}
+
 /// A [Generator] that generates C++ header code.
 class CppHeaderGenerator implements Generator {
   /// Constructor for [CppHeaderGenerator].
@@ -1137,6 +1174,8 @@
         help: 'The package that generated Java code will be in.')
     ..addFlag('java_use_generated_annotation',
         help: 'Adds the java.annotation.Generated annotation to the output.')
+    ..addOption('experimental_swift_out',
+        help: 'Path to generated Swift file (.swift).')
     ..addOption('experimental_cpp_header_out',
         help: 'Path to generated C++ header file (.h). (experimental)')
     ..addOption('experimental_cpp_source_out',
@@ -1182,6 +1221,7 @@
         package: results['java_package'],
         useGeneratedAnnotation: results['java_use_generated_annotation'],
       ),
+      swiftOut: results['experimental_swift_out'],
       cppHeaderOut: results['experimental_cpp_header_out'],
       cppSourceOut: results['experimental_cpp_source_out'],
       cppOptions: CppOptions(
@@ -1229,6 +1269,7 @@
         <Generator>[
           const DartGenerator(),
           const JavaGenerator(),
+          const SwiftGenerator(),
           const CppHeaderGenerator(),
           const CppSourceGenerator(),
           const DartTestGenerator(),
diff --git a/packages/pigeon/lib/swift_generator.dart b/packages/pigeon/lib/swift_generator.dart
new file mode 100644
index 0000000..8a7d6ec
--- /dev/null
+++ b/packages/pigeon/lib/swift_generator.dart
@@ -0,0 +1,612 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:pigeon/functional.dart';
+
+import 'ast.dart';
+import 'generator_tools.dart';
+
+/// Options that control how Swift code will be generated.
+class SwiftOptions {
+  /// Creates a [SwiftOptions] object
+  const SwiftOptions({
+    this.copyrightHeader,
+  });
+
+  /// A copyright header that will get prepended to generated code.
+  final Iterable<String>? copyrightHeader;
+
+  /// Creates a [SwiftOptions] from a Map representation where:
+  /// `x = SwiftOptions.fromMap(x.toMap())`.
+  static SwiftOptions fromMap(Map<String, Object> map) {
+    return SwiftOptions(
+      copyrightHeader: map['copyrightHeader'] as Iterable<String>?,
+    );
+  }
+
+  /// Converts a [SwiftOptions] to a Map representation where:
+  /// `x = SwiftOptions.fromMap(x.toMap())`.
+  Map<String, Object> toMap() {
+    final Map<String, Object> result = <String, Object>{
+      if (copyrightHeader != null) 'copyrightHeader': copyrightHeader!,
+    };
+    return result;
+  }
+
+  /// Overrides any non-null parameters from [options] into this to make a new
+  /// [SwiftOptions].
+  SwiftOptions merge(SwiftOptions options) {
+    return SwiftOptions.fromMap(mergeMaps(toMap(), options.toMap()));
+  }
+}
+
+/// Calculates the name of the codec that will be generated for [api].
+String _getCodecName(Api api) => '${api.name}Codec';
+
+/// Writes the codec classwill be used for encoding messages for the [api].
+/// Example:
+/// private class FooHostApiCodecReader: FlutterStandardReader {...}
+/// private class FooHostApiCodecWriter: FlutterStandardWriter {...}
+/// private class FooHostApiCodecReaderWriter: FlutterStandardReaderWriter {...}
+void _writeCodec(Indent indent, Api api, Root root) {
+  final String codecName = _getCodecName(api);
+  final String readerWriterName = '${codecName}ReaderWriter';
+  final String readerName = '${codecName}Reader';
+  final String writerName = '${codecName}Writer';
+
+  // Generate Reader
+  indent.write('private class $readerName: FlutterStandardReader ');
+  indent.scoped('{', '}', () {
+    if (getCodecClasses(api, root).isNotEmpty) {
+      indent.write('override func readValue(ofType type: UInt8) -> Any? ');
+      indent.scoped('{', '}', () {
+        indent.write('switch type ');
+        indent.scoped('{', '}', () {
+          for (final EnumeratedClass customClass
+              in getCodecClasses(api, root)) {
+            indent.write('case ${customClass.enumeration}:');
+            indent.scoped('', '', () {
+              indent.write(
+                  'return ${customClass.name}.fromMap(self.readValue() as! [String: Any])');
+            });
+          }
+          indent.write('default:');
+          indent.scoped('', '', () {
+            indent.writeln('return super.readValue(ofType: type)');
+          });
+        });
+      });
+    }
+  });
+
+  // Generate Writer
+  indent.write('private class $writerName: FlutterStandardWriter ');
+  indent.scoped('{', '}', () {
+    if (getCodecClasses(api, root).isNotEmpty) {
+      indent.write('override func writeValue(_ value: Any) ');
+      indent.scoped('{', '}', () {
+        indent.write('');
+        for (final EnumeratedClass customClass in getCodecClasses(api, root)) {
+          indent.add('if let value = value as? ${customClass.name} ');
+          indent.scoped('{', '} else ', () {
+            indent.writeln('super.writeByte(${customClass.enumeration})');
+            indent.writeln('super.writeValue(value.toMap())');
+          }, addTrailingNewline: false);
+        }
+        indent.scoped('{', '}', () {
+          indent.writeln('super.writeValue(value)');
+        });
+      });
+    }
+  });
+  indent.writeln('');
+
+  // Generate ReaderWriter
+  indent.write('private class $readerWriterName: FlutterStandardReaderWriter ');
+  indent.scoped('{', '}', () {
+    indent.write(
+        'override func reader(with data: Data) -> FlutterStandardReader ');
+    indent.scoped('{', '}', () {
+      indent.writeln('return $readerName(data: data)');
+    });
+    indent.writeln('');
+    indent.write(
+        'override func writer(with data: NSMutableData) -> FlutterStandardWriter ');
+    indent.scoped('{', '}', () {
+      indent.writeln('return $writerName(data: data)');
+    });
+  });
+  indent.writeln('');
+
+  // Generate Codec
+  indent.write('class $codecName: FlutterStandardMessageCodec ');
+  indent.scoped('{', '}', () {
+    indent.writeln(
+        'static let shared = $codecName(readerWriter: $readerWriterName())');
+  });
+}
+
+/// Write the swift code that represents a host [Api], [api].
+/// Example:
+/// protocol Foo {
+///   Int32 add(x: Int32, y: Int32)
+/// }
+void _writeHostApi(Indent indent, Api api, Root root) {
+  assert(api.location == ApiLocation.host);
+
+  final String apiName = api.name;
+
+  indent.writeln(
+      '/// Generated protocol from Pigeon that represents a handler of messages from Flutter.');
+  indent.write('protocol $apiName ');
+  indent.scoped('{', '}', () {
+    for (final Method method in api.methods) {
+      final List<String> argSignature = <String>[];
+      if (method.arguments.isNotEmpty) {
+        final Iterable<String> argTypes = method.arguments
+            .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
+        final Iterable<String> argNames =
+            method.arguments.map((NamedType e) => e.name);
+        argSignature
+            .addAll(map2(argTypes, argNames, (String argType, String argName) {
+          return '$argName: $argType';
+        }));
+      }
+
+      final String returnType = method.returnType.isVoid
+          ? ''
+          : _nullsafeSwiftTypeForDartType(method.returnType);
+      if (method.isAsynchronous) {
+        argSignature.add('completion: @escaping ($returnType) -> Void');
+        indent.writeln('func ${method.name}(${argSignature.join(', ')})');
+      } else if (method.returnType.isVoid) {
+        indent.writeln('func ${method.name}(${argSignature.join(', ')})');
+      } else {
+        indent.writeln(
+            'func ${method.name}(${argSignature.join(', ')}) -> $returnType');
+      }
+    }
+  });
+
+  indent.addln('');
+  indent.writeln(
+      '/// Generated setup class from Pigeon to handle messages through the `binaryMessenger`.');
+  indent.write('class ${apiName}Setup ');
+  indent.scoped('{', '}', () {
+    final String codecName = _getCodecName(api);
+    indent.writeln('/// The codec used by $apiName.');
+    indent.writeln(
+        'static var codec: FlutterStandardMessageCodec { $codecName.shared }');
+    indent.writeln(
+        '/// Sets up an instance of `$apiName` to handle messages through the `binaryMessenger`.');
+    indent.write(
+        'static func setUp(binaryMessenger: FlutterBinaryMessenger, api: $apiName?) ');
+    indent.scoped('{', '}', () {
+      for (final Method method in api.methods) {
+        final String channelName = makeChannelName(api, method);
+        final String varChannelName = '${method.name}Channel';
+
+        indent.writeln(
+            'let $varChannelName = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger, codec: codec)');
+        indent.write('if let api = api ');
+        indent.scoped('{', '}', () {
+          indent.write('$varChannelName.setMessageHandler ');
+          final String messageVarName =
+              method.arguments.isNotEmpty ? 'message' : '_';
+          indent.scoped('{ $messageVarName, reply in', '}', () {
+            final List<String> methodArgument = <String>[];
+            if (method.arguments.isNotEmpty) {
+              indent.writeln('let args = message as! [Any?]');
+              enumerate(method.arguments, (int index, NamedType arg) {
+                final String argName = _getSafeArgumentName(index, arg);
+                final String argIndex = 'args[$index]';
+                indent.writeln(
+                    'let $argName = ${_castForceUnwrap(argIndex, arg.type, root)}');
+                methodArgument.add('${arg.name}: $argName');
+              });
+            }
+            final String call =
+                'api.${method.name}(${methodArgument.join(', ')})';
+            if (method.isAsynchronous) {
+              indent.write('$call ');
+              if (method.returnType.isVoid) {
+                indent.scoped('{', '}', () {
+                  indent.writeln('reply(nil)');
+                });
+              } else {
+                indent.scoped('{ result in', '}', () {
+                  indent.writeln('reply(wrapResult(result))');
+                });
+              }
+            } else {
+              if (method.returnType.isVoid) {
+                indent.writeln(call);
+                indent.writeln('reply(nil)');
+              } else {
+                indent.writeln('let result = $call');
+                indent.writeln('reply(wrapResult(result))');
+              }
+            }
+          });
+        }, addTrailingNewline: false);
+        indent.scoped(' else {', '}', () {
+          indent.writeln('$varChannelName.setMessageHandler(nil)');
+        });
+      }
+    });
+  });
+}
+
+String _getArgumentName(int count, NamedType argument) =>
+    argument.name.isEmpty ? 'arg$count' : argument.name;
+
+/// Returns an argument name that can be used in a context where it is possible to collide.
+String _getSafeArgumentName(int count, NamedType argument) =>
+    _getArgumentName(count, argument) + 'Arg';
+
+String _camelCase(String text) {
+  final String pascal = text.split('_').map((String part) {
+    return part.isEmpty ? '' : part[0].toUpperCase() + part.substring(1);
+  }).join();
+  return pascal[0].toLowerCase() + pascal.substring(1);
+}
+
+/// Writes the code for a flutter [Api], [api].
+/// Example:
+/// class Foo {
+///   private let binaryMessenger: FlutterBinaryMessenger
+///   init(binaryMessenger: FlutterBinaryMessenger) {...}
+///   func add(x: Int32, y: Int32, completion: @escaping (Int32?) -> Void) {...}
+/// }
+void _writeFlutterApi(Indent indent, Api api, Root root) {
+  assert(api.location == ApiLocation.flutter);
+  indent.writeln(
+      '/// Generated class from Pigeon that represents Flutter messages that can be called from Swift.');
+  indent.write('class ${api.name} ');
+  indent.scoped('{', '}', () {
+    indent.writeln('private let binaryMessenger: FlutterBinaryMessenger');
+    indent.write('init(binaryMessenger: FlutterBinaryMessenger)');
+    indent.scoped('{', '}', () {
+      indent.writeln('self.binaryMessenger = binaryMessenger');
+    });
+    final String codecName = _getCodecName(api);
+    indent.write('var codec: FlutterStandardMessageCodec ');
+    indent.scoped('{', '}', () {
+      indent.writeln('return $codecName.shared');
+    });
+    for (final Method func in api.methods) {
+      final String channelName = makeChannelName(api, func);
+      final String returnType = func.returnType.isVoid
+          ? ''
+          : _nullsafeSwiftTypeForDartType(func.returnType);
+      String sendArgument;
+      if (func.arguments.isEmpty) {
+        indent.write(
+            'func ${func.name}(completion: @escaping ($returnType) -> Void) ');
+        sendArgument = 'nil';
+      } else {
+        final Iterable<String> argTypes = func.arguments
+            .map((NamedType e) => _nullsafeSwiftTypeForDartType(e.type));
+        final Iterable<String> argLabels =
+            indexMap(func.arguments, _getArgumentName);
+        final Iterable<String> argNames =
+            indexMap(func.arguments, _getSafeArgumentName);
+        sendArgument = '[${argNames.join(', ')}]';
+        final String argsSignature = map3(
+            argTypes,
+            argLabels,
+            argNames,
+            (String type, String label, String name) =>
+                '$label $name: $type').join(', ');
+        if (func.returnType.isVoid) {
+          indent.write(
+              'func ${func.name}($argsSignature, completion: @escaping () -> Void) ');
+        } else {
+          indent.write(
+              'func ${func.name}($argsSignature, completion: @escaping ($returnType) -> Void) ');
+        }
+      }
+      indent.scoped('{', '}', () {
+        const String channel = 'channel';
+        indent.writeln(
+            'let $channel = FlutterBasicMessageChannel(name: "$channelName", binaryMessenger: binaryMessenger, codec: codec)');
+        indent.write('$channel.sendMessage($sendArgument) ');
+        if (func.returnType.isVoid) {
+          indent.scoped('{ _ in', '}', () {
+            indent.writeln('completion()');
+          });
+        } else {
+          indent.scoped('{ response in', '}', () {
+            indent.writeln(
+                'let result = ${_castForceUnwrap("response", func.returnType, root)}');
+            indent.writeln('completion(result)');
+          });
+        }
+      });
+    }
+  });
+}
+
+String _castForceUnwrap(String value, TypeDeclaration type, Root root) {
+  if (isEnum(root, type)) {
+    final String forceUnwrap = type.isNullable ? '' : '!';
+    final String nullableConditionPrefix =
+        type.isNullable ? '$value == nil ? nil : ' : '';
+    return '$nullableConditionPrefix${_swiftTypeForDartType(type)}(rawValue: $value as! Int)$forceUnwrap';
+  } else {
+    final String castUnwrap = type.isNullable ? '?' : '!';
+    return '$value as$castUnwrap ${_swiftTypeForDartType(type)}';
+  }
+}
+
+/// Converts a [List] of [TypeDeclaration]s to a comma separated [String] to be
+/// used in Swift code.
+String _flattenTypeArguments(List<TypeDeclaration> args) {
+  return args.map((TypeDeclaration e) => _swiftTypeForDartType(e)).join(', ');
+}
+
+String _swiftTypeForBuiltinGenericDartType(TypeDeclaration type) {
+  if (type.typeArguments.isEmpty) {
+    if (type.baseName == 'List') {
+      return '[Any?]';
+    } else if (type.baseName == 'Map') {
+      return '[AnyHashable: Any?]';
+    } else {
+      return 'Any';
+    }
+  } else {
+    if (type.baseName == 'List') {
+      return '[${_nullsafeSwiftTypeForDartType(type.typeArguments.first)}]';
+    } else if (type.baseName == 'Map') {
+      return '[${_nullsafeSwiftTypeForDartType(type.typeArguments.first)}: ${_nullsafeSwiftTypeForDartType(type.typeArguments.last)}]';
+    } else {
+      return '${type.baseName}<${_flattenTypeArguments(type.typeArguments)}>';
+    }
+  }
+}
+
+String? _swiftTypeForBuiltinDartType(TypeDeclaration type) {
+  const Map<String, String> swiftTypeForDartTypeMap = <String, String>{
+    'void': 'Void',
+    'bool': 'Bool',
+    'String': 'String',
+    'int': 'Int32',
+    'double': 'Double',
+    'Uint8List': '[UInt8]',
+    'Int32List': '[Int32]',
+    'Int64List': '[Int64]',
+    'Float32List': '[Float32]',
+    'Float64List': '[Float64]',
+    'Object': 'Any',
+  };
+  if (swiftTypeForDartTypeMap.containsKey(type.baseName)) {
+    return swiftTypeForDartTypeMap[type.baseName];
+  } else if (type.baseName == 'List' || type.baseName == 'Map') {
+    return _swiftTypeForBuiltinGenericDartType(type);
+  } else {
+    return null;
+  }
+}
+
+String _swiftTypeForDartType(TypeDeclaration type) {
+  return _swiftTypeForBuiltinDartType(type) ?? type.baseName;
+}
+
+String _nullsafeSwiftTypeForDartType(TypeDeclaration type) {
+  final String nullSafe = type.isNullable ? '?' : '';
+  return '${_swiftTypeForDartType(type)}$nullSafe';
+}
+
+/// Generates the ".swift" file for the AST represented by [root] to [sink] with the
+/// provided [options].
+void generateSwift(SwiftOptions options, Root root, StringSink sink) {
+  final Set<String> rootClassNameSet =
+      root.classes.map((Class x) => x.name).toSet();
+  final Set<String> rootEnumNameSet =
+      root.enums.map((Enum x) => x.name).toSet();
+  final Indent indent = Indent(sink);
+
+  HostDatatype _getHostDatatype(NamedType field) {
+    return getHostDatatype(field, root.classes, root.enums,
+        (NamedType x) => _swiftTypeForBuiltinDartType(x.type));
+  }
+
+  void writeHeader() {
+    if (options.copyrightHeader != null) {
+      addLines(indent, options.copyrightHeader!, linePrefix: '// ');
+    }
+    indent.writeln('// $generatedCodeWarning');
+    indent.writeln('// $seeAlsoWarning');
+  }
+
+  void writeImports() {
+    indent.writeln('import Foundation');
+    indent.writeln('import Flutter');
+  }
+
+  void writeEnum(Enum anEnum) {
+    indent.write('enum ${anEnum.name}: Int ');
+    indent.scoped('{', '}', () {
+      // We use explicit indexing here as use of the ordinal() method is
+      // discouraged. The toMap and fromMap API matches class API to allow
+      // the same code to work with enums and classes, but this
+      // can also be done directly in the host and flutter APIs.
+      int index = 0;
+      for (final String member in anEnum.members) {
+        indent.writeln('case ${_camelCase(member)} = $index');
+        index++;
+      }
+    });
+  }
+
+  void writeDataClass(Class klass) {
+    void writeField(NamedType field) {
+      indent.write(
+          'var ${field.name}: ${_nullsafeSwiftTypeForDartType(field.type)}');
+      final String defaultNil = field.type.isNullable ? ' = nil' : '';
+      indent.addln(defaultNil);
+    }
+
+    void writeToMap() {
+      indent.write('func toMap() -> [String: Any?] ');
+      indent.scoped('{', '}', () {
+        indent.write('return ');
+        indent.scoped('[', ']', () {
+          for (final NamedType field in klass.fields) {
+            final HostDatatype hostDatatype = _getHostDatatype(field);
+            String toWriteValue = '';
+            final String fieldName = field.name;
+            final String nullsafe = field.type.isNullable ? '?' : '';
+            if (!hostDatatype.isBuiltin &&
+                rootClassNameSet.contains(field.type.baseName)) {
+              toWriteValue = '$fieldName$nullsafe.toMap()';
+            } else if (!hostDatatype.isBuiltin &&
+                rootEnumNameSet.contains(field.type.baseName)) {
+              toWriteValue = '$fieldName$nullsafe.rawValue';
+            } else {
+              toWriteValue = field.name;
+            }
+
+            final String comma = klass.fields.last == field ? '' : ',';
+
+            indent.writeln('"${field.name}": $toWriteValue$comma');
+          }
+        });
+      });
+    }
+
+    void writeFromMap() {
+      final String className = klass.name;
+      indent
+          .write('static func fromMap(_ map: [String: Any?]) -> $className? ');
+
+      indent.scoped('{', '}', () {
+        for (final NamedType field in klass.fields) {
+          final HostDatatype hostDatatype = _getHostDatatype(field);
+
+          final String mapValue = 'map["${field.name}"]';
+          final String fieldType = _swiftTypeForDartType(field.type);
+
+          if (field.type.isNullable) {
+            if (!hostDatatype.isBuiltin &&
+                rootClassNameSet.contains(field.type.baseName)) {
+              indent.writeln('var ${field.name}: $fieldType? = nil');
+              indent.write(
+                  'if let ${field.name}Map = $mapValue as? [String: Any?] ');
+              indent.scoped('{', '}', () {
+                indent.writeln(
+                    '${field.name} = $fieldType.fromMap(${field.name}Map)');
+              });
+            } else if (!hostDatatype.isBuiltin &&
+                rootEnumNameSet.contains(field.type.baseName)) {
+              indent.writeln('var ${field.name}: $fieldType? = nil');
+              indent.write('if let ${field.name}RawValue = $mapValue as? Int ');
+              indent.scoped('{', '}', () {
+                indent.writeln(
+                    '${field.name} = $fieldType(rawValue: ${field.name}RawValue)');
+              });
+            } else {
+              indent.writeln('let ${field.name} = $mapValue as? $fieldType ');
+            }
+          } else {
+            if (!hostDatatype.isBuiltin &&
+                rootClassNameSet.contains(field.type.baseName)) {
+              indent.writeln(
+                  'let ${field.name} = $fieldType.fromMap($mapValue as! [String: Any?])!');
+            } else if (!hostDatatype.isBuiltin &&
+                rootEnumNameSet.contains(field.type.baseName)) {
+              indent.writeln(
+                  'let ${field.name} = $fieldType(rawValue: $mapValue as! Int)!');
+            } else {
+              indent.writeln('let ${field.name} = $mapValue as! $fieldType');
+            }
+          }
+        }
+
+        indent.writeln('');
+        indent.write('return ');
+        indent.scoped('$className(', ')', () {
+          for (final NamedType field in klass.fields) {
+            final String comma = klass.fields.last == field ? '' : ',';
+            indent.writeln('${field.name}: ${field.name}$comma');
+          }
+        });
+      });
+    }
+
+    indent.writeln(
+        '/// Generated class from Pigeon that represents data sent in messages.');
+    indent.write('struct ${klass.name} ');
+    indent.scoped('{', '}', () {
+      klass.fields.forEach(writeField);
+
+      indent.writeln('');
+      writeFromMap();
+      writeToMap();
+    });
+  }
+
+  void writeApi(Api api, Root root) {
+    if (api.location == ApiLocation.host) {
+      _writeHostApi(indent, api, root);
+    } else if (api.location == ApiLocation.flutter) {
+      _writeFlutterApi(indent, api, root);
+    }
+  }
+
+  void writeWrapResult() {
+    indent.write('private func wrapResult(_ result: Any?) -> [String: Any?] ');
+    indent.scoped('{', '}', () {
+      indent.writeln('return ["result": result]');
+    });
+  }
+
+  void writeWrapError() {
+    indent.write(
+        'private func wrapError(_ error: FlutterError) -> [String: Any?] ');
+    indent.scoped('{', '}', () {
+      indent.write('return ');
+      indent.scoped('[', ']', () {
+        indent.write('"error": ');
+        indent.scoped('[', ']', () {
+          indent.writeln('"${Keys.errorCode}": error.code,');
+          indent.writeln('"${Keys.errorMessage}": error.message,');
+          indent.writeln('"${Keys.errorDetails}": error.details');
+        });
+      });
+    });
+  }
+
+  writeHeader();
+  indent.addln('');
+  writeImports();
+  indent.addln('');
+  indent.writeln('/// Generated class from Pigeon.');
+  for (final Enum anEnum in root.enums) {
+    indent.writeln('');
+    writeEnum(anEnum);
+  }
+
+  for (final Class klass in root.classes) {
+    indent.addln('');
+    writeDataClass(klass);
+  }
+
+  if (root.apis.any((Api api) =>
+      api.location == ApiLocation.host &&
+      api.methods.any((Method it) => it.isAsynchronous))) {
+    indent.addln('');
+  }
+
+  for (final Api api in root.apis) {
+    _writeCodec(indent, api, root);
+    indent.addln('');
+    writeApi(api, root);
+  }
+
+  indent.addln('');
+  writeWrapResult();
+  indent.addln('');
+  writeWrapError();
+}
diff --git a/packages/pigeon/mock_handler_tester/test/message.dart b/packages/pigeon/mock_handler_tester/test/message.dart
index bade840..c62520a 100644
--- a/packages/pigeon/mock_handler_tester/test/message.dart
+++ b/packages/pigeon/mock_handler_tester/test/message.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.0.4), do not edit directly.
+// Autogenerated from Pigeon (v3.0.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
@@ -12,14 +12,14 @@
 import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
 import 'package:flutter/services.dart';
 
-enum RequestState {
+enum MessageRequestState {
   pending,
   success,
   failure,
 }
 
-class SearchRequest {
-  SearchRequest({
+class MessageSearchRequest {
+  MessageSearchRequest({
     this.query,
     this.anInt,
     this.aBool,
@@ -37,9 +37,9 @@
     return pigeonMap;
   }
 
-  static SearchRequest decode(Object message) {
+  static MessageSearchRequest decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequest(
+    return MessageSearchRequest(
       query: pigeonMap['query'] as String?,
       anInt: pigeonMap['anInt'] as int?,
       aBool: pigeonMap['aBool'] as bool?,
@@ -47,8 +47,8 @@
   }
 }
 
-class SearchReply {
-  SearchReply({
+class MessageSearchReply {
+  MessageSearchReply({
     this.result,
     this.error,
     this.state,
@@ -56,59 +56,59 @@
 
   String? result;
   String? error;
-  RequestState? state;
+  MessageRequestState? state;
 
   Object encode() {
     final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
     pigeonMap['result'] = result;
     pigeonMap['error'] = error;
-    pigeonMap['state'] = state == null ? null : state!.index;
+    pigeonMap['state'] = state?.index;
     return pigeonMap;
   }
 
-  static SearchReply decode(Object message) {
+  static MessageSearchReply decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchReply(
+    return MessageSearchReply(
       result: pigeonMap['result'] as String?,
       error: pigeonMap['error'] as String?,
       state: pigeonMap['state'] != null
-          ? RequestState.values[pigeonMap['state']! as int]
+          ? MessageRequestState.values[pigeonMap['state']! as int]
           : null,
     );
   }
 }
 
-class Nested {
-  Nested({
+class MessageNested {
+  MessageNested({
     this.request,
   });
 
-  SearchRequest? request;
+  MessageSearchRequest? request;
 
   Object encode() {
     final Map<Object?, Object?> pigeonMap = <Object?, Object?>{};
-    pigeonMap['request'] = request == null ? null : request!.encode();
+    pigeonMap['request'] = request?.encode();
     return pigeonMap;
   }
 
-  static Nested decode(Object message) {
+  static MessageNested decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return Nested(
+    return MessageNested(
       request: pigeonMap['request'] != null
-          ? SearchRequest.decode(pigeonMap['request']!)
+          ? MessageSearchRequest.decode(pigeonMap['request']!)
           : null,
     );
   }
 }
 
-class _ApiCodec extends StandardMessageCodec {
-  const _ApiCodec();
+class _MessageApiCodec extends StandardMessageCodec {
+  const _MessageApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
-    if (value is SearchReply) {
+    if (value is MessageSearchReply) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is MessageSearchRequest) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
     } else {
@@ -120,10 +120,10 @@
   Object? readValueOfType(int type, ReadBuffer buffer) {
     switch (type) {
       case 128:
-        return SearchReply.decode(readValue(buffer)!);
+        return MessageSearchReply.decode(readValue(buffer)!);
 
       case 129:
-        return SearchRequest.decode(readValue(buffer)!);
+        return MessageSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -131,19 +131,20 @@
   }
 }
 
-class Api {
-  /// Constructor for [Api].  The [binaryMessenger] named argument is
+class MessageApi {
+  /// Constructor for [MessageApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
-  Api({BinaryMessenger? binaryMessenger}) : _binaryMessenger = binaryMessenger;
+  MessageApi({BinaryMessenger? binaryMessenger})
+      : _binaryMessenger = binaryMessenger;
 
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _ApiCodec();
+  static const MessageCodec<Object?> codec = _MessageApiCodec();
 
   Future<void> initialize() async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.Api.initialize', codec,
+        'dev.flutter.pigeon.MessageApi.initialize', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(null) as Map<Object?, Object?>?;
@@ -165,9 +166,9 @@
     }
   }
 
-  Future<SearchReply> search(SearchRequest arg_request) async {
+  Future<MessageSearchReply> search(MessageSearchRequest arg_request) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.Api.search', codec,
+        'dev.flutter.pigeon.MessageApi.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(<Object?>[arg_request]) as Map<Object?, Object?>?;
@@ -190,22 +191,22 @@
         message: 'Host platform returned null value for non-null return value.',
       );
     } else {
-      return (replyMap['result'] as SearchReply?)!;
+      return (replyMap['result'] as MessageSearchReply?)!;
     }
   }
 }
 
-class _NestedApiCodec extends StandardMessageCodec {
-  const _NestedApiCodec();
+class _MessageNestedApiCodec extends StandardMessageCodec {
+  const _MessageNestedApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
-    if (value is Nested) {
+    if (value is MessageNested) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchReply) {
+    } else if (value is MessageSearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is MessageSearchRequest) {
       buffer.putUint8(130);
       writeValue(buffer, value.encode());
     } else {
@@ -217,13 +218,13 @@
   Object? readValueOfType(int type, ReadBuffer buffer) {
     switch (type) {
       case 128:
-        return Nested.decode(readValue(buffer)!);
+        return MessageNested.decode(readValue(buffer)!);
 
       case 129:
-        return SearchReply.decode(readValue(buffer)!);
+        return MessageSearchReply.decode(readValue(buffer)!);
 
       case 130:
-        return SearchRequest.decode(readValue(buffer)!);
+        return MessageSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -231,20 +232,20 @@
   }
 }
 
-class NestedApi {
-  /// Constructor for [NestedApi].  The [binaryMessenger] named argument is
+class MessageNestedApi {
+  /// Constructor for [MessageNestedApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
-  NestedApi({BinaryMessenger? binaryMessenger})
+  MessageNestedApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
 
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _NestedApiCodec();
+  static const MessageCodec<Object?> codec = _MessageNestedApiCodec();
 
-  Future<SearchReply> search(Nested arg_nested) async {
+  Future<MessageSearchReply> search(MessageNested arg_nested) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.NestedApi.search', codec,
+        'dev.flutter.pigeon.MessageNestedApi.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(<Object?>[arg_nested]) as Map<Object?, Object?>?;
@@ -267,19 +268,19 @@
         message: 'Host platform returned null value for non-null return value.',
       );
     } else {
-      return (replyMap['result'] as SearchReply?)!;
+      return (replyMap['result'] as MessageSearchReply?)!;
     }
   }
 }
 
-class _FlutterSearchApiCodec extends StandardMessageCodec {
-  const _FlutterSearchApiCodec();
+class _MessageFlutterSearchApiCodec extends StandardMessageCodec {
+  const _MessageFlutterSearchApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
-    if (value is SearchReply) {
+    if (value is MessageSearchReply) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is MessageSearchRequest) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
     } else {
@@ -291,10 +292,10 @@
   Object? readValueOfType(int type, ReadBuffer buffer) {
     switch (type) {
       case 128:
-        return SearchReply.decode(readValue(buffer)!);
+        return MessageSearchReply.decode(readValue(buffer)!);
 
       case 129:
-        return SearchRequest.decode(readValue(buffer)!);
+        return MessageSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -302,26 +303,28 @@
   }
 }
 
-abstract class FlutterSearchApi {
-  static const MessageCodec<Object?> codec = _FlutterSearchApiCodec();
+abstract class MessageFlutterSearchApi {
+  static const MessageCodec<Object?> codec = _MessageFlutterSearchApiCodec();
 
-  SearchReply search(SearchRequest request);
-  static void setup(FlutterSearchApi? api, {BinaryMessenger? binaryMessenger}) {
+  MessageSearchReply search(MessageSearchRequest request);
+  static void setup(MessageFlutterSearchApi? api,
+      {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.FlutterSearchApi.search', codec,
+          'dev.flutter.pigeon.MessageFlutterSearchApi.search', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMessageHandler(null);
       } else {
         channel.setMessageHandler((Object? message) async {
           assert(message != null,
-              'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null.');
+              'Argument for dev.flutter.pigeon.MessageFlutterSearchApi.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final SearchRequest? arg_request = (args[0] as SearchRequest?);
+          final MessageSearchRequest? arg_request =
+              (args[0] as MessageSearchRequest?);
           assert(arg_request != null,
-              'Argument for dev.flutter.pigeon.FlutterSearchApi.search was null, expected non-null SearchRequest.');
-          final SearchReply output = api.search(arg_request!);
+              'Argument for dev.flutter.pigeon.MessageFlutterSearchApi.search was null, expected non-null MessageSearchRequest.');
+          final MessageSearchReply output = api.search(arg_request!);
           return output;
         });
       }
diff --git a/packages/pigeon/mock_handler_tester/test/test.dart b/packages/pigeon/mock_handler_tester/test/test.dart
index 2abd3a6..648a933 100644
--- a/packages/pigeon/mock_handler_tester/test/test.dart
+++ b/packages/pigeon/mock_handler_tester/test/test.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v2.0.4), do not edit directly.
+// Autogenerated from Pigeon (v3.0.4), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis
 // ignore_for_file: avoid_relative_lib_imports
@@ -19,10 +19,10 @@
   const _TestHostApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
-    if (value is SearchReply) {
+    if (value is MessageSearchReply) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is MessageSearchRequest) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
     } else {
@@ -34,10 +34,10 @@
   Object? readValueOfType(int type, ReadBuffer buffer) {
     switch (type) {
       case 128:
-        return SearchReply.decode(readValue(buffer)!);
+        return MessageSearchReply.decode(readValue(buffer)!);
 
       case 129:
-        return SearchRequest.decode(readValue(buffer)!);
+        return MessageSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -49,11 +49,11 @@
   static const MessageCodec<Object?> codec = _TestHostApiCodec();
 
   void initialize();
-  SearchReply search(SearchRequest request);
+  MessageSearchReply search(MessageSearchRequest request);
   static void setup(TestHostApi? api, {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.Api.initialize', codec,
+          'dev.flutter.pigeon.MessageApi.initialize', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMockMessageHandler(null);
@@ -67,19 +67,20 @@
     }
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.Api.search', codec,
+          'dev.flutter.pigeon.MessageApi.search', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMockMessageHandler(null);
       } else {
         channel.setMockMessageHandler((Object? message) async {
           assert(message != null,
-              'Argument for dev.flutter.pigeon.Api.search was null.');
+              'Argument for dev.flutter.pigeon.MessageApi.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final SearchRequest? arg_request = (args[0] as SearchRequest?);
+          final MessageSearchRequest? arg_request =
+              (args[0] as MessageSearchRequest?);
           assert(arg_request != null,
-              'Argument for dev.flutter.pigeon.Api.search was null, expected non-null SearchRequest.');
-          final SearchReply output = api.search(arg_request!);
+              'Argument for dev.flutter.pigeon.MessageApi.search was null, expected non-null MessageSearchRequest.');
+          final MessageSearchReply output = api.search(arg_request!);
           return <Object?, Object?>{'result': output};
         });
       }
@@ -91,13 +92,13 @@
   const _TestNestedApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
-    if (value is Nested) {
+    if (value is MessageNested) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchReply) {
+    } else if (value is MessageSearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is MessageSearchRequest) {
       buffer.putUint8(130);
       writeValue(buffer, value.encode());
     } else {
@@ -109,13 +110,13 @@
   Object? readValueOfType(int type, ReadBuffer buffer) {
     switch (type) {
       case 128:
-        return Nested.decode(readValue(buffer)!);
+        return MessageNested.decode(readValue(buffer)!);
 
       case 129:
-        return SearchReply.decode(readValue(buffer)!);
+        return MessageSearchReply.decode(readValue(buffer)!);
 
       case 130:
-        return SearchRequest.decode(readValue(buffer)!);
+        return MessageSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -126,23 +127,23 @@
 abstract class TestNestedApi {
   static const MessageCodec<Object?> codec = _TestNestedApiCodec();
 
-  SearchReply search(Nested nested);
+  MessageSearchReply search(MessageNested nested);
   static void setup(TestNestedApi? api, {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.NestedApi.search', codec,
+          'dev.flutter.pigeon.MessageNestedApi.search', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMockMessageHandler(null);
       } else {
         channel.setMockMessageHandler((Object? message) async {
           assert(message != null,
-              'Argument for dev.flutter.pigeon.NestedApi.search was null.');
+              'Argument for dev.flutter.pigeon.MessageNestedApi.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final Nested? arg_nested = (args[0] as Nested?);
+          final MessageNested? arg_nested = (args[0] as MessageNested?);
           assert(arg_nested != null,
-              'Argument for dev.flutter.pigeon.NestedApi.search was null, expected non-null Nested.');
-          final SearchReply output = api.search(arg_nested!);
+              'Argument for dev.flutter.pigeon.MessageNestedApi.search was null, expected non-null MessageNested.');
+          final MessageSearchReply output = api.search(arg_nested!);
           return <Object?, Object?>{'result': output};
         });
       }
diff --git a/packages/pigeon/mock_handler_tester/test/widget_test.dart b/packages/pigeon/mock_handler_tester/test/widget_test.dart
index 8e56f63..4ffded1 100644
--- a/packages/pigeon/mock_handler_tester/test/widget_test.dart
+++ b/packages/pigeon/mock_handler_tester/test/widget_test.dart
@@ -19,21 +19,21 @@
   }
 
   @override
-  SearchReply search(SearchRequest arg) {
+  MessageSearchReply search(MessageSearchRequest arg) {
     log.add('search');
-    return SearchReply()..result = arg.query;
+    return MessageSearchReply()..result = arg.query;
   }
 }
 
 class MockNested implements TestNestedApi {
   bool didCall = false;
   @override
-  SearchReply search(Nested arg) {
+  MessageSearchReply search(MessageNested arg) {
     didCall = true;
     if (arg.request == null) {
-      return SearchReply();
+      return MessageSearchReply();
     } else {
-      return SearchReply()..result = arg.request?.query;
+      return MessageSearchReply()..result = arg.request?.query;
     }
   }
 }
@@ -42,25 +42,27 @@
   TestWidgetsFlutterBinding.ensureInitialized();
 
   test('simple', () async {
-    final NestedApi api = NestedApi();
+    final MessageNestedApi api = MessageNestedApi();
     final MockNested mock = MockNested();
     TestNestedApi.setup(mock);
-    final SearchReply reply = await api.search(Nested()..request = null);
+    final MessageSearchReply reply =
+        await api.search(MessageNested()..request = null);
     expect(mock.didCall, true);
     expect(reply.result, null);
   });
 
   test('nested', () async {
-    final Api api = Api();
+    final MessageApi api = MessageApi();
     final Mock mock = Mock();
     TestHostApi.setup(mock);
-    final SearchReply reply = await api.search(SearchRequest()..query = 'foo');
+    final MessageSearchReply reply =
+        await api.search(MessageSearchRequest()..query = 'foo');
     expect(mock.log, <String>['search']);
     expect(reply.result, 'foo');
   });
 
   test('no-arg calls', () async {
-    final Api api = Api();
+    final MessageApi api = MessageApi();
     final Mock mock = Mock();
     TestHostApi.setup(mock);
     await api.initialize();
@@ -74,14 +76,14 @@
       TestHostApi.setup(mock);
       expect(
         await const BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.Api.initialize',
+          'dev.flutter.pigeon.MessageApi.initialize',
           StandardMessageCodec(),
         ).send(<Object?>[null]),
         isEmpty,
       );
       try {
         await const BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.Api.search',
+          'dev.flutter.pigeon.MessageApi.search',
           StandardMessageCodec(),
         ).send(<Object?>[null]) as Map<Object?, Object?>?;
         expect(true, isFalse); // should not reach here
@@ -90,7 +92,7 @@
         expect(
           error.toString(),
           contains(
-            'Argument for dev.flutter.pigeon.Api.search was null, expected non-null SearchRequest.',
+            'Argument for dev.flutter.pigeon.MessageApi.search was null, expected non-null MessageSearchRequest.',
           ),
         );
       }
diff --git a/packages/pigeon/pigeons/android_unittests.dart b/packages/pigeon/pigeons/android_unittests.dart
index 77ca837..45ecf19 100644
--- a/packages/pigeon/pigeons/android_unittests.dart
+++ b/packages/pigeon/pigeons/android_unittests.dart
@@ -4,27 +4,27 @@
 
 import 'package:pigeon/pigeon.dart';
 
-enum LoadingState {
+enum AndroidLoadingState {
   loading,
   complete,
 }
 
-class SetRequest {
+class AndroidSetRequest {
   int? value;
-  LoadingState? state;
+  AndroidLoadingState? state;
 }
 
-class NestedRequest {
+class AndroidNestedRequest {
   String? context;
-  SetRequest? request;
+  AndroidSetRequest? request;
 }
 
 @HostApi()
-abstract class Api {
-  void setValue(SetRequest request);
+abstract class AndroidApi {
+  void setValue(AndroidSetRequest request);
 }
 
 @HostApi()
-abstract class NestedApi {
-  void setValueWithContext(NestedRequest request);
+abstract class AndroidNestedApi {
+  void setValueWithContext(AndroidNestedRequest request);
 }
diff --git a/packages/pigeon/pigeons/configure_pigeon_dart_out.dart b/packages/pigeon/pigeons/configure_pigeon_dart_out.dart
index 226e1be..9010dca 100644
--- a/packages/pigeon/pigeons/configure_pigeon_dart_out.dart
+++ b/packages/pigeon/pigeons/configure_pigeon_dart_out.dart
@@ -6,6 +6,6 @@
 
 @ConfigurePigeon(PigeonOptions(dartOut: 'stdout', javaOut: 'stdout'))
 @HostApi()
-abstract class Api {
+abstract class ConfigurePigeonApi {
   void ping();
 }
diff --git a/packages/pigeon/pigeons/enum.dart b/packages/pigeon/pigeons/enum.dart
index 570b68e..8b35176 100644
--- a/packages/pigeon/pigeons/enum.dart
+++ b/packages/pigeon/pigeons/enum.dart
@@ -4,22 +4,22 @@
 
 import 'package:pigeon/pigeon.dart';
 
-enum State {
+enum EnumState {
   Pending,
   Success,
   Error,
 }
 
-class Data {
-  State? state;
+class DataWithEnum {
+  EnumState? state;
 }
 
 @HostApi()
 abstract class EnumApi2Host {
-  Data echo(Data data);
+  DataWithEnum echo(DataWithEnum data);
 }
 
 @FlutterApi()
 abstract class EnumApi2Flutter {
-  Data echo(Data data);
+  DataWithEnum echo(DataWithEnum data);
 }
diff --git a/packages/pigeon/pigeons/enum_args.dart b/packages/pigeon/pigeons/enum_args.dart
index 63f9eeb..886b924 100644
--- a/packages/pigeon/pigeons/enum_args.dart
+++ b/packages/pigeon/pigeons/enum_args.dart
@@ -4,17 +4,17 @@
 
 import 'package:pigeon/pigeon.dart';
 
-enum State {
+enum EnumArgsState {
   Pending,
   Success,
   Error,
 }
 
-class Data {
-  State? state;
+class EnumArgsData {
+  EnumArgsState? state;
 }
 
 @HostApi()
-abstract class EnumArg2Host {
-  void foo(State state);
+abstract class EnumArgs2Host {
+  void foo(EnumArgsState state);
 }
diff --git a/packages/pigeon/pigeons/flutter_unittests.dart b/packages/pigeon/pigeons/flutter_unittests.dart
index de66fed..acde483 100644
--- a/packages/pigeon/pigeons/flutter_unittests.dart
+++ b/packages/pigeon/pigeons/flutter_unittests.dart
@@ -4,29 +4,29 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class SearchRequest {
+class FlutterSearchRequest {
   String? query;
 }
 
-class SearchReply {
+class FlutterSearchReply {
   String? result;
   String? error;
 }
 
-class SearchRequests {
+class FlutterSearchRequests {
   // ignore: always_specify_types
   List? requests;
 }
 
-class SearchReplies {
+class FlutterSearchReplies {
   // ignore: always_specify_types
   List? replies;
 }
 
 @HostApi()
 abstract class Api {
-  SearchReply search(SearchRequest request);
-  SearchReplies doSearches(SearchRequests request);
-  SearchRequests echo(SearchRequests requests);
+  FlutterSearchReply search(FlutterSearchRequest request);
+  FlutterSearchReplies doSearches(FlutterSearchRequests request);
+  FlutterSearchRequests echo(FlutterSearchRequests requests);
   int anInt(int value);
 }
diff --git a/packages/pigeon/pigeons/host2flutter.dart b/packages/pigeon/pigeons/host2flutter.dart
index 98f8e1c..143df60 100644
--- a/packages/pigeon/pigeons/host2flutter.dart
+++ b/packages/pigeon/pigeons/host2flutter.dart
@@ -9,15 +9,15 @@
     prefix: 'H2F',
   ),
 ))
-class SearchRequest {
+class Host2FlutterSearchRequest {
   String? query;
 }
 
-class SearchReply {
+class Host2FlutterSearchReply {
   String? result;
 }
 
 @FlutterApi()
-abstract class Api {
-  SearchReply search(SearchRequest request);
+abstract class H2FApi {
+  Host2FlutterSearchReply search(Host2FlutterSearchRequest request);
 }
diff --git a/packages/pigeon/pigeons/java_double_host_api.dart b/packages/pigeon/pigeons/java_double_host_api.dart
index 0ff3e0b..d07cb58 100644
--- a/packages/pigeon/pigeons/java_double_host_api.dart
+++ b/packages/pigeon/pigeons/java_double_host_api.dart
@@ -4,18 +4,18 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class Response {
+class BridgeResponse {
   int? result;
 }
 
 @HostApi()
 abstract class BridgeApi1 {
   @async
-  Response call();
+  BridgeResponse call();
 }
 
 @HostApi()
 abstract class BridgeApi2 {
   @async
-  Response call();
+  BridgeResponse call();
 }
diff --git a/packages/pigeon/pigeons/message.dart b/packages/pigeon/pigeons/message.dart
index 2658dc3..7ffe172 100644
--- a/packages/pigeon/pigeons/message.dart
+++ b/packages/pigeon/pigeons/message.dart
@@ -16,40 +16,40 @@
     prefix: 'AC',
   ),
 ))
-enum RequestState {
+enum MessageRequestState {
   pending,
   success,
   failure,
 }
 
-class SearchRequest {
+class MessageSearchRequest {
   String? query;
   int? anInt;
   bool? aBool;
 }
 
-class SearchReply {
+class MessageSearchReply {
   String? result;
   String? error;
-  RequestState? state;
+  MessageRequestState? state;
 }
 
 @HostApi(dartHostTestHandler: 'TestHostApi')
-abstract class Api {
+abstract class MessageApi {
   void initialize();
-  SearchReply search(SearchRequest request);
+  MessageSearchReply search(MessageSearchRequest request);
 }
 
-class Nested {
-  SearchRequest? request;
+class MessageNested {
+  MessageSearchRequest? request;
 }
 
 @HostApi(dartHostTestHandler: 'TestNestedApi')
-abstract class NestedApi {
-  SearchReply search(Nested nested);
+abstract class MessageNestedApi {
+  MessageSearchReply search(MessageNested nested);
 }
 
 @FlutterApi()
-abstract class FlutterSearchApi {
-  SearchReply search(SearchRequest request);
+abstract class MessageFlutterSearchApi {
+  MessageSearchReply search(MessageSearchRequest request);
 }
diff --git a/packages/pigeon/pigeons/non_null_fields.dart b/packages/pigeon/pigeons/non_null_fields.dart
index aa96b87..f79a262 100644
--- a/packages/pigeon/pigeons/non_null_fields.dart
+++ b/packages/pigeon/pigeons/non_null_fields.dart
@@ -7,8 +7,8 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class SearchRequest {
-  SearchRequest({required this.query});
+class NonNullFieldSearchRequest {
+  NonNullFieldSearchRequest({required this.query});
   String query;
 }
 
@@ -20,8 +20,9 @@
 
 enum ReplyType { success, error }
 
-class SearchReply {
-  SearchReply(this.result, this.error, this.indices, this.extraData, this.type);
+class NonNullFieldSearchReply {
+  NonNullFieldSearchReply(
+      this.result, this.error, this.indices, this.extraData, this.type);
   String result;
   String error;
   List<int?> indices;
@@ -30,11 +31,11 @@
 }
 
 @HostApi()
-abstract class NonNullHostApi {
-  SearchReply search(SearchRequest nested);
+abstract class NonNullFieldHostApi {
+  NonNullFieldSearchReply search(NonNullFieldSearchRequest nested);
 }
 
 @FlutterApi()
-abstract class NonNullFlutterApi {
-  SearchReply search(SearchRequest request);
+abstract class NonNullFieldFlutterApi {
+  NonNullFieldSearchReply search(NonNullFieldSearchRequest request);
 }
diff --git a/packages/pigeon/pigeons/void_arg_flutter.dart b/packages/pigeon/pigeons/void_arg_flutter.dart
index bef79b3..7428812 100644
--- a/packages/pigeon/pigeons/void_arg_flutter.dart
+++ b/packages/pigeon/pigeons/void_arg_flutter.dart
@@ -4,11 +4,11 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class Result {
+class VoidArgFlutterResult {
   int? code;
 }
 
 @FlutterApi()
-abstract class Api {
-  Result getCode();
+abstract class VoidArgApi {
+  VoidArgFlutterResult getCode();
 }
diff --git a/packages/pigeon/pigeons/void_arg_host.dart b/packages/pigeon/pigeons/void_arg_host.dart
index f6afcbc..44ba5ac 100644
--- a/packages/pigeon/pigeons/void_arg_host.dart
+++ b/packages/pigeon/pigeons/void_arg_host.dart
@@ -4,11 +4,11 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class Result {
+class VoidArgHostResult {
   int? code;
 }
 
 @HostApi()
-abstract class Api {
-  Result getCode();
+abstract class VoidArgHostApi {
+  VoidArgHostResult getCode();
 }
diff --git a/packages/pigeon/pigeons/voidflutter.dart b/packages/pigeon/pigeons/voidflutter.dart
index 003af5e..51fe3a9 100644
--- a/packages/pigeon/pigeons/voidflutter.dart
+++ b/packages/pigeon/pigeons/voidflutter.dart
@@ -4,11 +4,11 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class SetRequest {
+class VoidFlutterSetRequest {
   int? value;
 }
 
 @FlutterApi()
-abstract class Api {
-  void setValue(SetRequest request);
+abstract class VoidFlutterApi {
+  void setValue(VoidFlutterSetRequest request);
 }
diff --git a/packages/pigeon/pigeons/voidhost.dart b/packages/pigeon/pigeons/voidhost.dart
index 06d6678..3ff48f8 100644
--- a/packages/pigeon/pigeons/voidhost.dart
+++ b/packages/pigeon/pigeons/voidhost.dart
@@ -4,11 +4,11 @@
 
 import 'package:pigeon/pigeon.dart';
 
-class SetRequest {
+class VoidHostSetRequest {
   int? value;
 }
 
 @HostApi()
-abstract class Api {
-  void setValue(SetRequest request);
+abstract class VoidHostApi {
+  void setValue(VoidHostSetRequest request);
 }
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java
index c8da07d..4ae2062 100644
--- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/AsyncTest.java
@@ -50,7 +50,7 @@
     verify(binaryMessenger).setMessageHandler(eq("dev.flutter.pigeon.Api2Host.calculate"), any());
     verify(binaryMessenger)
         .setMessageHandler(eq("dev.flutter.pigeon.Api2Host.voidVoid"), handler.capture());
-    MessageCodec<Object> codec = Pigeon.Api.getCodec();
+    MessageCodec<Object> codec = Pigeon.AndroidApi.getCodec();
     ByteBuffer message = codec.encodeMessage(null);
     Boolean[] didCall = {false};
     handler
@@ -77,7 +77,7 @@
     verify(binaryMessenger).setMessageHandler(eq("dev.flutter.pigeon.Api2Host.calculate"), any());
     verify(binaryMessenger)
         .setMessageHandler(eq("dev.flutter.pigeon.Api2Host.voidVoid"), handler.capture());
-    MessageCodec<Object> codec = Pigeon.Api.getCodec();
+    MessageCodec<Object> codec = Pigeon.AndroidApi.getCodec();
     ByteBuffer message = codec.encodeMessage(null);
     Boolean[] didCall = {false};
     handler
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/EnumTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/EnumTest.java
index 32225a4..84b5b5d 100644
--- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/EnumTest.java
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/EnumTest.java
@@ -12,10 +12,10 @@
 public class EnumTest {
   @Test
   public void nullValue() {
-    Enum.Data value = new Enum.Data();
+    Enum.DataWithEnum value = new Enum.DataWithEnum();
     value.setState(null);
     Map<String, Object> map = value.toMap();
-    Enum.Data readValue = Enum.Data.fromMap(map);
+    Enum.DataWithEnum readValue = Enum.DataWithEnum.fromMap(map);
     assertEquals(value.getState(), readValue.getState());
   }
 }
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NonNullFieldsTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NonNullFieldsTest.java
index 3b1e150..3336fcf 100644
--- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NonNullFieldsTest.java
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/NonNullFieldsTest.java
@@ -6,19 +6,20 @@
 
 import static org.junit.Assert.*;
 
-import com.example.android_unit_tests.NonNullFields.SearchRequest;
+import com.example.android_unit_tests.NonNullFields.NonNullFieldSearchRequest;
 import java.lang.IllegalStateException;
 import org.junit.Test;
 
 public class NonNullFieldsTest {
   @Test
   public void builder() {
-    SearchRequest request = new SearchRequest.Builder().setQuery("hello").build();
+    NonNullFieldSearchRequest request =
+        new NonNullFieldSearchRequest.Builder().setQuery("hello").build();
     assertEquals(request.getQuery(), "hello");
   }
 
   @Test(expected = IllegalStateException.class)
   public void builderThrowsIfNull() {
-    SearchRequest request = new SearchRequest.Builder().build();
+    NonNullFieldSearchRequest request = new NonNullFieldSearchRequest.Builder().build();
   }
 }
diff --git a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java
index e38c382..8808c13 100644
--- a/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java
+++ b/packages/pigeon/platform_tests/android_unit_tests/android/app/src/test/java/com/example/android_unit_tests/PigeonTest.java
@@ -19,49 +19,49 @@
 public class PigeonTest {
   @Test
   public void toMapAndBack() {
-    Pigeon.SetRequest request = new Pigeon.SetRequest();
+    Pigeon.AndroidSetRequest request = new Pigeon.AndroidSetRequest();
     request.setValue(1234l);
-    request.setState(Pigeon.LoadingState.complete);
+    request.setState(Pigeon.AndroidLoadingState.complete);
     Map<String, Object> map = request.toMap();
-    Pigeon.SetRequest readRequest = Pigeon.SetRequest.fromMap(map);
+    Pigeon.AndroidSetRequest readRequest = Pigeon.AndroidSetRequest.fromMap(map);
     assertEquals(request.getValue(), readRequest.getValue());
     assertEquals(request.getState(), readRequest.getState());
   }
 
   @Test
   public void toMapAndBackNested() {
-    Pigeon.NestedRequest nested = new Pigeon.NestedRequest();
-    Pigeon.SetRequest request = new Pigeon.SetRequest();
+    Pigeon.AndroidNestedRequest nested = new Pigeon.AndroidNestedRequest();
+    Pigeon.AndroidSetRequest request = new Pigeon.AndroidSetRequest();
     request.setValue(1234l);
-    request.setState(Pigeon.LoadingState.complete);
+    request.setState(Pigeon.AndroidLoadingState.complete);
     nested.setRequest(request);
     Map<String, Object> map = nested.toMap();
-    Pigeon.NestedRequest readNested = Pigeon.NestedRequest.fromMap(map);
+    Pigeon.AndroidNestedRequest readNested = Pigeon.AndroidNestedRequest.fromMap(map);
     assertEquals(nested.getRequest().getValue(), readNested.getRequest().getValue());
     assertEquals(nested.getRequest().getState(), readNested.getRequest().getState());
   }
 
   @Test
   public void clearsHandler() {
-    Pigeon.Api mockApi = mock(Pigeon.Api.class);
+    Pigeon.AndroidApi mockApi = mock(Pigeon.AndroidApi.class);
     BinaryMessenger binaryMessenger = mock(BinaryMessenger.class);
-    Pigeon.Api.setup(binaryMessenger, mockApi);
+    Pigeon.AndroidApi.setup(binaryMessenger, mockApi);
     ArgumentCaptor<String> channelName = ArgumentCaptor.forClass(String.class);
     verify(binaryMessenger).setMessageHandler(channelName.capture(), isNotNull());
-    Pigeon.Api.setup(binaryMessenger, null);
+    Pigeon.AndroidApi.setup(binaryMessenger, null);
     verify(binaryMessenger).setMessageHandler(eq(channelName.getValue()), isNull());
   }
 
-  /** Causes an exception in the handler by passing in null when a SetRequest is expected. */
+  /** Causes an exception in the handler by passing in null when a AndroidSetRequest is expected. */
   @Test
   public void errorMessage() {
-    Pigeon.Api mockApi = mock(Pigeon.Api.class);
+    Pigeon.AndroidApi mockApi = mock(Pigeon.AndroidApi.class);
     BinaryMessenger binaryMessenger = mock(BinaryMessenger.class);
-    Pigeon.Api.setup(binaryMessenger, mockApi);
+    Pigeon.AndroidApi.setup(binaryMessenger, mockApi);
     ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> handler =
         ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
     verify(binaryMessenger).setMessageHandler(anyString(), handler.capture());
-    MessageCodec<Object> codec = Pigeon.Api.getCodec();
+    MessageCodec<Object> codec = Pigeon.AndroidApi.getCodec();
     ByteBuffer message = codec.encodeMessage(null);
     handler
         .getValue()
@@ -82,16 +82,16 @@
 
   @Test
   public void callsVoidMethod() {
-    Pigeon.Api mockApi = mock(Pigeon.Api.class);
+    Pigeon.AndroidApi mockApi = mock(Pigeon.AndroidApi.class);
     BinaryMessenger binaryMessenger = mock(BinaryMessenger.class);
-    Pigeon.Api.setup(binaryMessenger, mockApi);
+    Pigeon.AndroidApi.setup(binaryMessenger, mockApi);
     ArgumentCaptor<BinaryMessenger.BinaryMessageHandler> handler =
         ArgumentCaptor.forClass(BinaryMessenger.BinaryMessageHandler.class);
     verify(binaryMessenger).setMessageHandler(anyString(), handler.capture());
-    Pigeon.SetRequest request = new Pigeon.SetRequest();
+    Pigeon.AndroidSetRequest request = new Pigeon.AndroidSetRequest();
     request.setValue(1234l);
-    request.setState(Pigeon.LoadingState.complete);
-    MessageCodec<Object> codec = Pigeon.Api.getCodec();
+    request.setState(Pigeon.AndroidLoadingState.complete);
+    MessageCodec<Object> codec = Pigeon.AndroidApi.getCodec();
     ByteBuffer message = codec.encodeMessage(new ArrayList<Object>(Arrays.asList(request)));
     message.rewind();
     handler
@@ -105,17 +105,17 @@
               assertTrue(wrapped.containsKey("result"));
               assertNull(wrapped.get("result"));
             });
-    ArgumentCaptor<Pigeon.SetRequest> receivedRequest =
-        ArgumentCaptor.forClass(Pigeon.SetRequest.class);
+    ArgumentCaptor<Pigeon.AndroidSetRequest> receivedRequest =
+        ArgumentCaptor.forClass(Pigeon.AndroidSetRequest.class);
     verify(mockApi).setValue(receivedRequest.capture());
     assertEquals(request.getValue(), receivedRequest.getValue().getValue());
   }
 
   @Test
   public void encodeWithNullField() {
-    Pigeon.NestedRequest request = new Pigeon.NestedRequest();
+    Pigeon.AndroidNestedRequest request = new Pigeon.AndroidNestedRequest();
     request.setContext("hello");
-    MessageCodec<Object> codec = Pigeon.NestedApi.getCodec();
+    MessageCodec<Object> codec = Pigeon.AndroidNestedApi.getCodec();
     ByteBuffer message = codec.encodeMessage(request);
     assertNotNull(message);
   }
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
index 4b48543..d3da81b 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/all_datatypes.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
@@ -66,7 +66,7 @@
     return Everything(
       aBool: pigeonMap['aBool'] as bool?,
       anInt: pigeonMap['anInt'] as int?,
-      aDouble: pigeonMap['aDouble'] as double?,
+      aDouble: (pigeonMap['aDouble'] as num?)?.toDouble(),
       aString: pigeonMap['aString'] as String?,
       aByteArray: pigeonMap['aByteArray'] as Uint8List?,
       a4ByteArray: pigeonMap['a4ByteArray'] as Int32List?,
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
index c2856cd..397a65a 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/multiple_arity.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
index 725a5b3..511767e 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/non_null_fields.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
@@ -17,8 +17,8 @@
   error,
 }
 
-class SearchRequest {
-  SearchRequest({
+class NonNullFieldSearchRequest {
+  NonNullFieldSearchRequest({
     required this.query,
   });
 
@@ -30,9 +30,9 @@
     return pigeonMap;
   }
 
-  static SearchRequest decode(Object message) {
+  static NonNullFieldSearchRequest decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequest(
+    return NonNullFieldSearchRequest(
       query: pigeonMap['query']! as String,
     );
   }
@@ -63,8 +63,8 @@
   }
 }
 
-class SearchReply {
-  SearchReply({
+class NonNullFieldSearchReply {
+  NonNullFieldSearchReply({
     required this.result,
     required this.error,
     required this.indices,
@@ -88,9 +88,9 @@
     return pigeonMap;
   }
 
-  static SearchReply decode(Object message) {
+  static NonNullFieldSearchReply decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchReply(
+    return NonNullFieldSearchReply(
       result: pigeonMap['result']! as String,
       error: pigeonMap['error']! as String,
       indices: (pigeonMap['indices'] as List<Object?>?)!.cast<int?>(),
@@ -100,17 +100,17 @@
   }
 }
 
-class _NonNullHostApiCodec extends StandardMessageCodec {
-  const _NonNullHostApiCodec();
+class _NonNullFieldHostApiCodec extends StandardMessageCodec {
+  const _NonNullFieldHostApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
     if (value is ExtraData) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchReply) {
+    } else if (value is NonNullFieldSearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is NonNullFieldSearchRequest) {
       buffer.putUint8(130);
       writeValue(buffer, value.encode());
     } else {
@@ -125,10 +125,10 @@
         return ExtraData.decode(readValue(buffer)!);
 
       case 129:
-        return SearchReply.decode(readValue(buffer)!);
+        return NonNullFieldSearchReply.decode(readValue(buffer)!);
 
       case 130:
-        return SearchRequest.decode(readValue(buffer)!);
+        return NonNullFieldSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -136,20 +136,21 @@
   }
 }
 
-class NonNullHostApi {
-  /// Constructor for [NonNullHostApi].  The [binaryMessenger] named argument is
+class NonNullFieldHostApi {
+  /// Constructor for [NonNullFieldHostApi].  The [binaryMessenger] named argument is
   /// available for dependency injection.  If it is left null, the default
   /// BinaryMessenger will be used which routes to the host platform.
-  NonNullHostApi({BinaryMessenger? binaryMessenger})
+  NonNullFieldHostApi({BinaryMessenger? binaryMessenger})
       : _binaryMessenger = binaryMessenger;
 
   final BinaryMessenger? _binaryMessenger;
 
-  static const MessageCodec<Object?> codec = _NonNullHostApiCodec();
+  static const MessageCodec<Object?> codec = _NonNullFieldHostApiCodec();
 
-  Future<SearchReply> search(SearchRequest arg_nested) async {
+  Future<NonNullFieldSearchReply> search(
+      NonNullFieldSearchRequest arg_nested) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-        'dev.flutter.pigeon.NonNullHostApi.search', codec,
+        'dev.flutter.pigeon.NonNullFieldHostApi.search', codec,
         binaryMessenger: _binaryMessenger);
     final Map<Object?, Object?>? replyMap =
         await channel.send(<Object?>[arg_nested]) as Map<Object?, Object?>?;
@@ -172,22 +173,22 @@
         message: 'Host platform returned null value for non-null return value.',
       );
     } else {
-      return (replyMap['result'] as SearchReply?)!;
+      return (replyMap['result'] as NonNullFieldSearchReply?)!;
     }
   }
 }
 
-class _NonNullFlutterApiCodec extends StandardMessageCodec {
-  const _NonNullFlutterApiCodec();
+class _NonNullFieldFlutterApiCodec extends StandardMessageCodec {
+  const _NonNullFieldFlutterApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
     if (value is ExtraData) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchReply) {
+    } else if (value is NonNullFieldSearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is NonNullFieldSearchRequest) {
       buffer.putUint8(130);
       writeValue(buffer, value.encode());
     } else {
@@ -202,10 +203,10 @@
         return ExtraData.decode(readValue(buffer)!);
 
       case 129:
-        return SearchReply.decode(readValue(buffer)!);
+        return NonNullFieldSearchReply.decode(readValue(buffer)!);
 
       case 130:
-        return SearchRequest.decode(readValue(buffer)!);
+        return NonNullFieldSearchRequest.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -213,27 +214,28 @@
   }
 }
 
-abstract class NonNullFlutterApi {
-  static const MessageCodec<Object?> codec = _NonNullFlutterApiCodec();
+abstract class NonNullFieldFlutterApi {
+  static const MessageCodec<Object?> codec = _NonNullFieldFlutterApiCodec();
 
-  SearchReply search(SearchRequest request);
-  static void setup(NonNullFlutterApi? api,
+  NonNullFieldSearchReply search(NonNullFieldSearchRequest request);
+  static void setup(NonNullFieldFlutterApi? api,
       {BinaryMessenger? binaryMessenger}) {
     {
       final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
-          'dev.flutter.pigeon.NonNullFlutterApi.search', codec,
+          'dev.flutter.pigeon.NonNullFieldFlutterApi.search', codec,
           binaryMessenger: binaryMessenger);
       if (api == null) {
         channel.setMessageHandler(null);
       } else {
         channel.setMessageHandler((Object? message) async {
           assert(message != null,
-              'Argument for dev.flutter.pigeon.NonNullFlutterApi.search was null.');
+              'Argument for dev.flutter.pigeon.NonNullFieldFlutterApi.search was null.');
           final List<Object?> args = (message as List<Object?>?)!;
-          final SearchRequest? arg_request = (args[0] as SearchRequest?);
+          final NonNullFieldSearchRequest? arg_request =
+              (args[0] as NonNullFieldSearchRequest?);
           assert(arg_request != null,
-              'Argument for dev.flutter.pigeon.NonNullFlutterApi.search was null, expected non-null SearchRequest.');
-          final SearchReply output = api.search(arg_request!);
+              'Argument for dev.flutter.pigeon.NonNullFieldFlutterApi.search was null, expected non-null NonNullFieldSearchRequest.');
+          final NonNullFieldSearchReply output = api.search(arg_request!);
           return output;
         });
       }
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
index 5511e41..e7306bb 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_fields.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
index 676d889..06d7405 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/null_safe_pigeon.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
@@ -12,8 +12,8 @@
 import 'package:flutter/foundation.dart' show WriteBuffer, ReadBuffer;
 import 'package:flutter/services.dart';
 
-class SearchRequest {
-  SearchRequest({
+class FlutterSearchRequest {
+  FlutterSearchRequest({
     this.query,
   });
 
@@ -25,16 +25,16 @@
     return pigeonMap;
   }
 
-  static SearchRequest decode(Object message) {
+  static FlutterSearchRequest decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequest(
+    return FlutterSearchRequest(
       query: pigeonMap['query'] as String?,
     );
   }
 }
 
-class SearchReply {
-  SearchReply({
+class FlutterSearchReply {
+  FlutterSearchReply({
     this.result,
     this.error,
   });
@@ -49,17 +49,17 @@
     return pigeonMap;
   }
 
-  static SearchReply decode(Object message) {
+  static FlutterSearchReply decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchReply(
+    return FlutterSearchReply(
       result: pigeonMap['result'] as String?,
       error: pigeonMap['error'] as String?,
     );
   }
 }
 
-class SearchRequests {
-  SearchRequests({
+class FlutterSearchRequests {
+  FlutterSearchRequests({
     this.requests,
   });
 
@@ -71,16 +71,16 @@
     return pigeonMap;
   }
 
-  static SearchRequests decode(Object message) {
+  static FlutterSearchRequests decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchRequests(
+    return FlutterSearchRequests(
       requests: pigeonMap['requests'] as List<Object?>?,
     );
   }
 }
 
-class SearchReplies {
-  SearchReplies({
+class FlutterSearchReplies {
+  FlutterSearchReplies({
     this.replies,
   });
 
@@ -92,9 +92,9 @@
     return pigeonMap;
   }
 
-  static SearchReplies decode(Object message) {
+  static FlutterSearchReplies decode(Object message) {
     final Map<Object?, Object?> pigeonMap = message as Map<Object?, Object?>;
-    return SearchReplies(
+    return FlutterSearchReplies(
       replies: pigeonMap['replies'] as List<Object?>?,
     );
   }
@@ -104,16 +104,16 @@
   const _ApiCodec();
   @override
   void writeValue(WriteBuffer buffer, Object? value) {
-    if (value is SearchReplies) {
+    if (value is FlutterSearchReplies) {
       buffer.putUint8(128);
       writeValue(buffer, value.encode());
-    } else if (value is SearchReply) {
+    } else if (value is FlutterSearchReply) {
       buffer.putUint8(129);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequest) {
+    } else if (value is FlutterSearchRequest) {
       buffer.putUint8(130);
       writeValue(buffer, value.encode());
-    } else if (value is SearchRequests) {
+    } else if (value is FlutterSearchRequests) {
       buffer.putUint8(131);
       writeValue(buffer, value.encode());
     } else {
@@ -125,16 +125,16 @@
   Object? readValueOfType(int type, ReadBuffer buffer) {
     switch (type) {
       case 128:
-        return SearchReplies.decode(readValue(buffer)!);
+        return FlutterSearchReplies.decode(readValue(buffer)!);
 
       case 129:
-        return SearchReply.decode(readValue(buffer)!);
+        return FlutterSearchReply.decode(readValue(buffer)!);
 
       case 130:
-        return SearchRequest.decode(readValue(buffer)!);
+        return FlutterSearchRequest.decode(readValue(buffer)!);
 
       case 131:
-        return SearchRequests.decode(readValue(buffer)!);
+        return FlutterSearchRequests.decode(readValue(buffer)!);
 
       default:
         return super.readValueOfType(type, buffer);
@@ -152,7 +152,7 @@
 
   static const MessageCodec<Object?> codec = _ApiCodec();
 
-  Future<SearchReply> search(SearchRequest arg_request) async {
+  Future<FlutterSearchReply> search(FlutterSearchRequest arg_request) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.Api.search', codec,
         binaryMessenger: _binaryMessenger);
@@ -177,11 +177,12 @@
         message: 'Host platform returned null value for non-null return value.',
       );
     } else {
-      return (replyMap['result'] as SearchReply?)!;
+      return (replyMap['result'] as FlutterSearchReply?)!;
     }
   }
 
-  Future<SearchReplies> doSearches(SearchRequests arg_request) async {
+  Future<FlutterSearchReplies> doSearches(
+      FlutterSearchRequests arg_request) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.Api.doSearches', codec,
         binaryMessenger: _binaryMessenger);
@@ -206,11 +207,11 @@
         message: 'Host platform returned null value for non-null return value.',
       );
     } else {
-      return (replyMap['result'] as SearchReplies?)!;
+      return (replyMap['result'] as FlutterSearchReplies?)!;
     }
   }
 
-  Future<SearchRequests> echo(SearchRequests arg_requests) async {
+  Future<FlutterSearchRequests> echo(FlutterSearchRequests arg_requests) async {
     final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
         'dev.flutter.pigeon.Api.echo', codec,
         binaryMessenger: _binaryMessenger);
@@ -235,7 +236,7 @@
         message: 'Host platform returned null value for non-null return value.',
       );
     } else {
-      return (replyMap['result'] as SearchRequests?)!;
+      return (replyMap['result'] as FlutterSearchRequests?)!;
     }
   }
 
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
index 3c8e836..7079358 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/nullable_returns.gen.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
index 3454087..7aa576a 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/lib/primitive.dart
@@ -2,7 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 //
-// Autogenerated from Pigeon (v3.1.4), do not edit directly.
+// Autogenerated from Pigeon (v3.2.0), do not edit directly.
 // See also: https://pub.dev/packages/pigeon
 // ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name
 // @dart = 2.12
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/non_null_fields_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/non_null_fields_test.dart
index b4ef08c..34b4193 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/non_null_fields_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/non_null_fields_test.dart
@@ -7,7 +7,8 @@
 
 void main() {
   test('test constructor', () {
-    final SearchRequest request = SearchRequest(query: 'what?');
+    final NonNullFieldSearchRequest request =
+        NonNullFieldSearchRequest(query: 'what?');
     expect(request.query, 'what?');
   });
 }
diff --git a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
index cae18df..4d944a4 100644
--- a/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
+++ b/packages/pigeon/platform_tests/flutter_null_safe_unit_tests/test/null_safe_test.dart
@@ -25,28 +25,28 @@
   TestWidgetsFlutterBinding.ensureInitialized();
 
   test('with values filled', () {
-    final SearchReply reply = SearchReply()
+    final FlutterSearchReply reply = FlutterSearchReply()
       ..result = 'foo'
       ..error = 'bar';
     final Object encoded = reply.encode();
-    final SearchReply decoded = SearchReply.decode(encoded);
+    final FlutterSearchReply decoded = FlutterSearchReply.decode(encoded);
     expect(reply.result, decoded.result);
     expect(reply.error, decoded.error);
   });
 
   test('with null value', () {
-    final SearchReply reply = SearchReply()
+    final FlutterSearchReply reply = FlutterSearchReply()
       ..result = 'foo'
       ..error = null;
     final Object encoded = reply.encode();
-    final SearchReply decoded = SearchReply.decode(encoded);
+    final FlutterSearchReply decoded = FlutterSearchReply.decode(encoded);
     expect(reply.result, decoded.result);
     expect(reply.error, decoded.error);
   });
 
   test('send/receive', () async {
-    final SearchRequest request = SearchRequest()..query = 'hey';
-    final SearchReply reply = SearchReply()..result = 'ho';
+    final FlutterSearchRequest request = FlutterSearchRequest()..query = 'hey';
+    final FlutterSearchReply reply = FlutterSearchReply()..result = 'ho';
     final BinaryMessenger mockMessenger = MockBinaryMessenger();
     final Completer<ByteData?> completer = Completer<ByteData?>();
     completer
@@ -55,15 +55,15 @@
     when(mockMessenger.send('dev.flutter.pigeon.Api.search', any))
         .thenAnswer((Invocation realInvocation) => sendResult);
     final Api api = Api(binaryMessenger: mockMessenger);
-    final SearchReply readReply = await api.search(request);
+    final FlutterSearchReply readReply = await api.search(request);
     expect(readReply, isNotNull);
     expect(reply.result, readReply.result);
   });
 
   test('send/receive list classes', () async {
-    final SearchRequest request = SearchRequest()..query = 'hey';
-    final SearchRequests requests = SearchRequests()
-      ..requests = <SearchRequest>[request];
+    final FlutterSearchRequest request = FlutterSearchRequest()..query = 'hey';
+    final FlutterSearchRequests requests = FlutterSearchRequests()
+      ..requests = <FlutterSearchRequest>[request];
     final BinaryMessenger mockMessenger = MockBinaryMessenger();
     echoOneArgument(
       mockMessenger,
@@ -71,9 +71,9 @@
       Api.codec,
     );
     final Api api = Api(binaryMessenger: mockMessenger);
-    final SearchRequests echo = await api.echo(requests);
+    final FlutterSearchRequests echo = await api.echo(requests);
     expect(echo.requests!.length, 1);
-    expect((echo.requests![0] as SearchRequest?)!.query, 'hey');
+    expect((echo.requests![0] as FlutterSearchRequest?)!.query, 'hey');
   });
 
   test('primitive datatypes', () async {
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/.gitignore b/packages/pigeon/platform_tests/ios_swift_unit_tests/.gitignore
new file mode 100644
index 0000000..ae1f183
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/.gitignore
@@ -0,0 +1,37 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Exceptions to above rules.
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/.metadata b/packages/pigeon/platform_tests/ios_swift_unit_tests/.metadata
new file mode 100644
index 0000000..01d2dcb
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+  revision: 0b8abb4724aa590dd0f429683339b1e045a1594d
+  channel: stable
+
+project_type: app
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/README.md b/packages/pigeon/platform_tests/ios_swift_unit_tests/README.md
new file mode 100644
index 0000000..1d554d3
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/README.md
@@ -0,0 +1,16 @@
+# ios_swift_unit_tests
+
+A new Flutter project.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/.gitignore b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/.gitignore
new file mode 100644
index 0000000..e96ef60
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/.gitignore
@@ -0,0 +1,32 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/AppFrameworkInfo.plist b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000..f2872cf
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+  <key>CFBundleDevelopmentRegion</key>
+  <string>$(DEVELOPMENT_LANGUAGE)</string>
+  <key>CFBundleExecutable</key>
+  <string>App</string>
+  <key>CFBundleIdentifier</key>
+  <string>io.flutter.flutter.app</string>
+  <key>CFBundleInfoDictionaryVersion</key>
+  <string>6.0</string>
+  <key>CFBundleName</key>
+  <string>App</string>
+  <key>CFBundlePackageType</key>
+  <string>FMWK</string>
+  <key>CFBundleShortVersionString</key>
+  <string>1.0</string>
+  <key>CFBundleSignature</key>
+  <string>????</string>
+  <key>CFBundleVersion</key>
+  <string>1.0</string>
+  <key>MinimumOSVersion</key>
+  <string>9.0</string>
+</dict>
+</plist>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/Debug.xcconfig b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/Release.xcconfig b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000..592ceee
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.pbxproj b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..cf94cff
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,803 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 50;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+		5E1DCBFF2853F8C200D6B537 /* EnumArgs.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E1DCBFE2853F8C200D6B537 /* EnumArgs.gen.swift */; };
+		978B8F6F1D3862AE00F588F7 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.swift */; };
+		97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+		CE32CF8C27D8225300E876A3 /* NullableReturnsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE32CF8B27D8225300E876A3 /* NullableReturnsTests.swift */; };
+		CEA48F5E27CD90CE003DD654 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48F5D27CD90CE003DD654 /* RunnerTests.swift */; };
+		CEA48F6027CD921C003DD654 /* AllDatatypesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48F5F27CD921C003DD654 /* AllDatatypesTests.swift */; };
+		CEA48F6227CD93EF003DD654 /* EchoBinaryMessenger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48F6127CD93EF003DD654 /* EchoBinaryMessenger.swift */; };
+		CEA48F6427CE4D43003DD654 /* AsyncHandlersTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48F6327CE4D43003DD654 /* AsyncHandlersTest.swift */; };
+		CEA48FA227CE617C003DD654 /* EnumTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FA127CE617C003DD654 /* EnumTests.swift */; };
+		CEA48FA427CE626B003DD654 /* ListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FA327CE626B003DD654 /* ListTests.swift */; };
+		CEA48FA627CE637A003DD654 /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FA527CE637A003DD654 /* Utils.swift */; };
+		CEA48FA827CE64C8003DD654 /* MultipleArityTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FA727CE64C8003DD654 /* MultipleArityTests.swift */; };
+		CEA48FAA27CE64FD003DD654 /* HandlerBinaryMessenger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FA927CE64FD003DD654 /* HandlerBinaryMessenger.swift */; };
+		CEA48FAC27CE66FA003DD654 /* PrimitiveTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FAB27CE66FA003DD654 /* PrimitiveTests.swift */; };
+		CEA48FC227CE6A76003DD654 /* Host2flutter.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FAD27CE6A76003DD654 /* Host2flutter.gen.swift */; };
+		CEA48FC327CE6A76003DD654 /* AllDatatypes.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FAE27CE6A76003DD654 /* AllDatatypes.gen.swift */; };
+		CEA48FC427CE6A76003DD654 /* NonNullFields.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FAF27CE6A76003DD654 /* NonNullFields.gen.swift */; };
+		CEA48FC527CE6A76003DD654 /* NullableReturns.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FB027CE6A76003DD654 /* NullableReturns.gen.swift */; };
+		CEA48FC627CE6A76003DD654 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = CEA48FB127CE6A76003DD654 /* Assets.xcassets */; };
+		CEA48FC727CE6A76003DD654 /* MultipleArity.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FB227CE6A76003DD654 /* MultipleArity.gen.swift */; };
+		CEA48FC827CE6A76003DD654 /* Voidflutter.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FB327CE6A76003DD654 /* Voidflutter.gen.swift */; };
+		CEA48FC927CE6A76003DD654 /* List.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FB427CE6A76003DD654 /* List.gen.swift */; };
+		CEA48FCA27CE6A76003DD654 /* AllVoid.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FB527CE6A76003DD654 /* AllVoid.gen.swift */; };
+		CEA48FCB27CE6A76003DD654 /* VoidArgHost.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FB627CE6A76003DD654 /* VoidArgHost.gen.swift */; };
+		CEA48FCE27CE6A77003DD654 /* Message.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FBC27CE6A76003DD654 /* Message.gen.swift */; };
+		CEA48FCF27CE6A77003DD654 /* VoidArgFlutter.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FBD27CE6A76003DD654 /* VoidArgFlutter.gen.swift */; };
+		CEA48FD027CE6A77003DD654 /* AsyncHandlers.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FBE27CE6A76003DD654 /* AsyncHandlers.gen.swift */; };
+		CEA48FD127CE6A77003DD654 /* Primitive.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FBF27CE6A76003DD654 /* Primitive.gen.swift */; };
+		CEA48FD227CE6A77003DD654 /* Enum.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FC027CE6A76003DD654 /* Enum.gen.swift */; };
+		CEA48FD327CE6A77003DD654 /* Voidhost.gen.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEA48FC127CE6A76003DD654 /* Voidhost.gen.swift */; };
+		CEB9F1B32822036700DEE2CD /* NonNullFieldsTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEB9F1B22822036700DEE2CD /* NonNullFieldsTest.swift */; };
+		CED7EED027D03B2E00CD3EC6 /* MockBinaryMessenger.swift in Sources */ = {isa = PBXBuildFile; fileRef = CED7EECF27D03B2E00CD3EC6 /* MockBinaryMessenger.swift */; };
+		CEFF9FAA27CC44C4007029D4 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CEFF9F9627CC44C3007029D4 /* Main.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+		0D50127723FF75B100CD5B95 /* PBXContainerItemProxy */ = {
+			isa = PBXContainerItemProxy;
+			containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+			proxyType = 1;
+			remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+			remoteInfo = Runner;
+		};
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		0D50127223FF75B100CD5B95 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		0D50127623FF75B100CD5B95 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
+		1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
+		3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
+		5E1DCBFE2853F8C200D6B537 /* EnumArgs.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EnumArgs.gen.swift; sourceTree = "<group>"; };
+		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
+		7AFFD8EE1D35381100E5BB4D /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		CE32CF8B27D8225300E876A3 /* NullableReturnsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NullableReturnsTests.swift; sourceTree = "<group>"; };
+		CE6607BD27CC1D8300E437DB /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
+		CEA48F5D27CD90CE003DD654 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
+		CEA48F5F27CD921C003DD654 /* AllDatatypesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AllDatatypesTests.swift; sourceTree = "<group>"; };
+		CEA48F6127CD93EF003DD654 /* EchoBinaryMessenger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EchoBinaryMessenger.swift; sourceTree = "<group>"; };
+		CEA48F6327CE4D43003DD654 /* AsyncHandlersTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AsyncHandlersTest.swift; sourceTree = "<group>"; };
+		CEA48FA127CE617C003DD654 /* EnumTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumTests.swift; sourceTree = "<group>"; };
+		CEA48FA327CE626B003DD654 /* ListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListTests.swift; sourceTree = "<group>"; };
+		CEA48FA527CE637A003DD654 /* Utils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utils.swift; sourceTree = "<group>"; };
+		CEA48FA727CE64C8003DD654 /* MultipleArityTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultipleArityTests.swift; sourceTree = "<group>"; };
+		CEA48FA927CE64FD003DD654 /* HandlerBinaryMessenger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HandlerBinaryMessenger.swift; sourceTree = "<group>"; };
+		CEA48FAB27CE66FA003DD654 /* PrimitiveTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrimitiveTests.swift; sourceTree = "<group>"; };
+		CEA48FAD27CE6A76003DD654 /* Host2flutter.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Host2flutter.gen.swift; sourceTree = "<group>"; };
+		CEA48FAE27CE6A76003DD654 /* AllDatatypes.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllDatatypes.gen.swift; sourceTree = "<group>"; };
+		CEA48FAF27CE6A76003DD654 /* NonNullFields.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NonNullFields.gen.swift; sourceTree = "<group>"; };
+		CEA48FB027CE6A76003DD654 /* NullableReturns.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NullableReturns.gen.swift; sourceTree = "<group>"; };
+		CEA48FB127CE6A76003DD654 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		CEA48FB227CE6A76003DD654 /* MultipleArity.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MultipleArity.gen.swift; sourceTree = "<group>"; };
+		CEA48FB327CE6A76003DD654 /* Voidflutter.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Voidflutter.gen.swift; sourceTree = "<group>"; };
+		CEA48FB427CE6A76003DD654 /* List.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = List.gen.swift; sourceTree = "<group>"; };
+		CEA48FB527CE6A76003DD654 /* AllVoid.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AllVoid.gen.swift; sourceTree = "<group>"; };
+		CEA48FB627CE6A76003DD654 /* VoidArgHost.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoidArgHost.gen.swift; sourceTree = "<group>"; };
+		CEA48FBC27CE6A76003DD654 /* Message.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Message.gen.swift; sourceTree = "<group>"; };
+		CEA48FBD27CE6A76003DD654 /* VoidArgFlutter.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VoidArgFlutter.gen.swift; sourceTree = "<group>"; };
+		CEA48FBE27CE6A76003DD654 /* AsyncHandlers.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncHandlers.gen.swift; sourceTree = "<group>"; };
+		CEA48FBF27CE6A76003DD654 /* Primitive.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Primitive.gen.swift; sourceTree = "<group>"; };
+		CEA48FC027CE6A76003DD654 /* Enum.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Enum.gen.swift; sourceTree = "<group>"; };
+		CEA48FC127CE6A76003DD654 /* Voidhost.gen.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Voidhost.gen.swift; sourceTree = "<group>"; };
+		CEB9F1B22822036700DEE2CD /* NonNullFieldsTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NonNullFieldsTest.swift; sourceTree = "<group>"; };
+		CED7EECF27D03B2E00CD3EC6 /* MockBinaryMessenger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockBinaryMessenger.swift; sourceTree = "<group>"; };
+		CEFF9F8F27CC44C3007029D4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		CEFF9F9527CC44C3007029D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
+		CEFF9F9727CC44C3007029D4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Main.storyboard; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		0D50126F23FF75B100CD5B95 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		97C146EB1CF9000F007C117D /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		0D50127323FF75B100CD5B95 /* RunnerTests */ = {
+			isa = PBXGroup;
+			children = (
+				0D50127623FF75B100CD5B95 /* Info.plist */,
+				CEA48FA527CE637A003DD654 /* Utils.swift */,
+				CEA48F5D27CD90CE003DD654 /* RunnerTests.swift */,
+				CEA48F5F27CD921C003DD654 /* AllDatatypesTests.swift */,
+				CEA48F6127CD93EF003DD654 /* EchoBinaryMessenger.swift */,
+				CEA48F6327CE4D43003DD654 /* AsyncHandlersTest.swift */,
+				CEA48FA127CE617C003DD654 /* EnumTests.swift */,
+				CEA48FA327CE626B003DD654 /* ListTests.swift */,
+				CEA48FA727CE64C8003DD654 /* MultipleArityTests.swift */,
+				CEA48FA927CE64FD003DD654 /* HandlerBinaryMessenger.swift */,
+				CEA48FAB27CE66FA003DD654 /* PrimitiveTests.swift */,
+				CED7EECF27D03B2E00CD3EC6 /* MockBinaryMessenger.swift */,
+				CEB9F1B22822036700DEE2CD /* NonNullFieldsTest.swift */,
+				CE32CF8B27D8225300E876A3 /* NullableReturnsTests.swift */,
+			);
+			path = RunnerTests;
+			sourceTree = "<group>";
+		};
+		9740EEB11CF90186004384FC /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+				9740EEB21CF90195004384FC /* Debug.xcconfig */,
+				7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+				9740EEB31CF90195004384FC /* Generated.xcconfig */,
+			);
+			name = Flutter;
+			sourceTree = "<group>";
+		};
+		97C146E51CF9000F007C117D = {
+			isa = PBXGroup;
+			children = (
+				9740EEB11CF90186004384FC /* Flutter */,
+				97C146F01CF9000F007C117D /* Runner */,
+				0D50127323FF75B100CD5B95 /* RunnerTests */,
+				97C146EF1CF9000F007C117D /* Products */,
+				CE3C1BD227CE6AA600190407 /* Recovered References */,
+			);
+			sourceTree = "<group>";
+		};
+		97C146EF1CF9000F007C117D /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				97C146EE1CF9000F007C117D /* Runner.app */,
+				0D50127223FF75B100CD5B95 /* RunnerTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		97C146F01CF9000F007C117D /* Runner */ = {
+			isa = PBXGroup;
+			children = (
+				CEA48FB127CE6A76003DD654 /* Assets.xcassets */,
+				CEFF9F9327CC44C3007029D4 /* Base.lproj */,
+				7AFFD8EE1D35381100E5BB4D /* AppDelegate.swift */,
+				97C146FA1CF9000F007C117D /* Main.storyboard */,
+				97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+				97C147021CF9000F007C117D /* Info.plist */,
+				1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+				1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+				CE6607BD27CC1D8300E437DB /* Runner-Bridging-Header.h */,
+				CEA48FB527CE6A76003DD654 /* AllVoid.gen.swift */,
+				CEA48FBE27CE6A76003DD654 /* AsyncHandlers.gen.swift */,
+				CEA48FAE27CE6A76003DD654 /* AllDatatypes.gen.swift */,
+				5E1DCBFE2853F8C200D6B537 /* EnumArgs.gen.swift */,
+				CEA48FC027CE6A76003DD654 /* Enum.gen.swift */,
+				CEA48FAD27CE6A76003DD654 /* Host2flutter.gen.swift */,
+				CEA48FB427CE6A76003DD654 /* List.gen.swift */,
+				CEA48FBC27CE6A76003DD654 /* Message.gen.swift */,
+				CEA48FB227CE6A76003DD654 /* MultipleArity.gen.swift */,
+				CEA48FAF27CE6A76003DD654 /* NonNullFields.gen.swift */,
+				CEA48FB027CE6A76003DD654 /* NullableReturns.gen.swift */,
+				CEA48FBF27CE6A76003DD654 /* Primitive.gen.swift */,
+				CEA48FBD27CE6A76003DD654 /* VoidArgFlutter.gen.swift */,
+				CEA48FB627CE6A76003DD654 /* VoidArgHost.gen.swift */,
+				CEA48FB327CE6A76003DD654 /* Voidflutter.gen.swift */,
+				CEA48FC127CE6A76003DD654 /* Voidhost.gen.swift */,
+			);
+			path = Runner;
+			sourceTree = "<group>";
+		};
+		CE3C1BD227CE6AA600190407 /* Recovered References */ = {
+			isa = PBXGroup;
+			children = (
+				CEFF9F8F27CC44C3007029D4 /* Assets.xcassets */,
+			);
+			name = "Recovered References";
+			sourceTree = "<group>";
+		};
+		CEFF9F9327CC44C3007029D4 /* Base.lproj */ = {
+			isa = PBXGroup;
+			children = (
+				CEFF9F9427CC44C3007029D4 /* LaunchScreen.storyboard */,
+				CEFF9F9627CC44C3007029D4 /* Main.storyboard */,
+			);
+			path = Base.lproj;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		0D50127123FF75B100CD5B95 /* RunnerTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 0D50127923FF75B100CD5B95 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+			buildPhases = (
+				0D50126E23FF75B100CD5B95 /* Sources */,
+				0D50126F23FF75B100CD5B95 /* Frameworks */,
+				0D50127023FF75B100CD5B95 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+				0D50127823FF75B100CD5B95 /* PBXTargetDependency */,
+			);
+			name = RunnerTests;
+			productName = RunnerTests;
+			productReference = 0D50127223FF75B100CD5B95 /* RunnerTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+		97C146ED1CF9000F007C117D /* Runner */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+			buildPhases = (
+				9740EEB61CF901F6004384FC /* Run Script */,
+				97C146EA1CF9000F007C117D /* Sources */,
+				97C146EB1CF9000F007C117D /* Frameworks */,
+				97C146EC1CF9000F007C117D /* Resources */,
+				9705A1C41CF9048500538489 /* Embed Frameworks */,
+				3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Runner;
+			productName = Runner;
+			productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		97C146E61CF9000F007C117D /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 1300;
+				ORGANIZATIONNAME = "The Flutter Authors";
+				TargetAttributes = {
+					0D50127123FF75B100CD5B95 = {
+						CreatedOnToolsVersion = 11.3;
+						LastSwiftMigration = 1320;
+						ProvisioningStyle = Automatic;
+						TestTargetID = 97C146ED1CF9000F007C117D;
+					};
+					97C146ED1CF9000F007C117D = {
+						CreatedOnToolsVersion = 7.3.1;
+					};
+				};
+			};
+			buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 97C146E51CF9000F007C117D;
+			productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				97C146ED1CF9000F007C117D /* Runner */,
+				0D50127123FF75B100CD5B95 /* RunnerTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		0D50127023FF75B100CD5B95 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		97C146EC1CF9000F007C117D /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CEFF9FAA27CC44C4007029D4 /* Main.storyboard in Resources */,
+				97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+				3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+				CEA48FC627CE6A76003DD654 /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Thin Binary";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+		};
+		9740EEB61CF901F6004384FC /* Run Script */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+			);
+			name = "Run Script";
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		0D50126E23FF75B100CD5B95 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CEA48F5E27CD90CE003DD654 /* RunnerTests.swift in Sources */,
+				CEB9F1B32822036700DEE2CD /* NonNullFieldsTest.swift in Sources */,
+				CEA48FAC27CE66FA003DD654 /* PrimitiveTests.swift in Sources */,
+				CEA48FAA27CE64FD003DD654 /* HandlerBinaryMessenger.swift in Sources */,
+				CEA48FA227CE617C003DD654 /* EnumTests.swift in Sources */,
+				CED7EED027D03B2E00CD3EC6 /* MockBinaryMessenger.swift in Sources */,
+				CEA48FA627CE637A003DD654 /* Utils.swift in Sources */,
+				CEA48FA827CE64C8003DD654 /* MultipleArityTests.swift in Sources */,
+				CEA48F6427CE4D43003DD654 /* AsyncHandlersTest.swift in Sources */,
+				CEA48F6027CD921C003DD654 /* AllDatatypesTests.swift in Sources */,
+				CEA48F6227CD93EF003DD654 /* EchoBinaryMessenger.swift in Sources */,
+				CE32CF8C27D8225300E876A3 /* NullableReturnsTests.swift in Sources */,
+				CEA48FA427CE626B003DD654 /* ListTests.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+		97C146EA1CF9000F007C117D /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CEA48FC827CE6A76003DD654 /* Voidflutter.gen.swift in Sources */,
+				CEA48FCE27CE6A77003DD654 /* Message.gen.swift in Sources */,
+				CEA48FD127CE6A77003DD654 /* Primitive.gen.swift in Sources */,
+				CEA48FC427CE6A76003DD654 /* NonNullFields.gen.swift in Sources */,
+				978B8F6F1D3862AE00F588F7 /* AppDelegate.swift in Sources */,
+				5E1DCBFF2853F8C200D6B537 /* EnumArgs.gen.swift in Sources */,
+				CEA48FCB27CE6A76003DD654 /* VoidArgHost.gen.swift in Sources */,
+				CEA48FD227CE6A77003DD654 /* Enum.gen.swift in Sources */,
+				CEA48FC927CE6A76003DD654 /* List.gen.swift in Sources */,
+				CEA48FC327CE6A76003DD654 /* AllDatatypes.gen.swift in Sources */,
+				CEA48FCA27CE6A76003DD654 /* AllVoid.gen.swift in Sources */,
+				1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+				CEA48FCF27CE6A77003DD654 /* VoidArgFlutter.gen.swift in Sources */,
+				CEA48FC527CE6A76003DD654 /* NullableReturns.gen.swift in Sources */,
+				CEA48FD027CE6A77003DD654 /* AsyncHandlers.gen.swift in Sources */,
+				CEA48FC227CE6A76003DD654 /* Host2flutter.gen.swift in Sources */,
+				CEA48FD327CE6A77003DD654 /* Voidhost.gen.swift in Sources */,
+				CEA48FC727CE6A76003DD654 /* MultipleArity.gen.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+		0D50127823FF75B100CD5B95 /* PBXTargetDependency */ = {
+			isa = PBXTargetDependency;
+			target = 97C146ED1CF9000F007C117D /* Runner */;
+			targetProxy = 0D50127723FF75B100CD5B95 /* PBXContainerItemProxy */;
+		};
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+		97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C146FB1CF9000F007C117D /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				97C147001CF9000F007C117D /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+		CEFF9F9427CC44C3007029D4 /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				CEFF9F9527CC44C3007029D4 /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+		CEFF9F9627CC44C3007029D4 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				CEFF9F9727CC44C3007029D4 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		0D50127A23FF75B100CD5B95 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = RunnerTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.aaclarke.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+			};
+			name = Debug;
+		};
+		0D50127B23FF75B100CD5B95 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = RunnerTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.aaclarke.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+			};
+			name = Release;
+		};
+		0D50127C23FF75B100CD5B95 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				BUNDLE_LOADER = "$(TEST_HOST)";
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CODE_SIGN_STYLE = Automatic;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				INFOPLIST_FILE = RunnerTests/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 13.2;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				MTL_FAST_MATH = YES;
+				PRODUCT_BUNDLE_IDENTIFIER = com.google.aaclarke.RunnerTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/Runner";
+			};
+			name = Profile;
+		};
+		249021D3217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Profile;
+		};
+		249021D4217E4FDB00AE95B9 /* Profile */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.example.iosSwiftUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Profile;
+		};
+		97C147031CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		97C147041CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				SUPPORTED_PLATFORMS = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		97C147061CF9000F007C117D /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.example.iosSwiftUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Debug;
+		};
+		97C147071CF9000F007C117D /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				INFOPLIST_FILE = Runner/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Flutter",
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = com.example.iosSwiftUnitTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+				SWIFT_VERSION = 5.0;
+				VERSIONING_SYSTEM = "apple-generic";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		0D50127923FF75B100CD5B95 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				0D50127A23FF75B100CD5B95 /* Debug */,
+				0D50127B23FF75B100CD5B95 /* Release */,
+				0D50127C23FF75B100CD5B95 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147031CF9000F007C117D /* Debug */,
+				97C147041CF9000F007C117D /* Release */,
+				249021D3217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				97C147061CF9000F007C117D /* Debug */,
+				97C147071CF9000F007C117D /* Release */,
+				249021D4217E4FDB00AE95B9 /* Profile */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:">
+   </FileRef>
+</Workspace>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000..20fcf69
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,97 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1300"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+               BuildableName = "Runner.app"
+               BlueprintName = "Runner"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "0D50127123FF75B100CD5B95"
+               BuildableName = "RunnerTests.xctest"
+               BlueprintName = "RunnerTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Profile"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <BuildableProductRunnable
+         runnableDebuggingMode = "0">
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "97C146ED1CF9000F007C117D"
+            BuildableName = "Runner.app"
+            BlueprintName = "Runner"
+            ReferencedContainer = "container:Runner.xcodeproj">
+         </BuildableReference>
+      </BuildableProductRunnable>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerTests.xcscheme b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerTests.xcscheme
new file mode 100644
index 0000000..606dad1
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcodeproj/xcshareddata/xcschemes/RunnerTests.xcscheme
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "1130"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "0D50127123FF75B100CD5B95"
+               BuildableName = "RunnerTests.xctest"
+               BlueprintName = "RunnerTests"
+               ReferencedContainer = "container:Runner.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..1d526a1
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Runner.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/.gitignore b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/.gitignore
new file mode 100644
index 0000000..6707470
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/.gitignore
@@ -0,0 +1 @@
+*.gen.swift
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/AppDelegate.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000..caf9983
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/AppDelegate.swift
@@ -0,0 +1,17 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+  override func application(
+    _ application: UIApplication,
+    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+  ) -> Bool {
+    GeneratedPluginRegistrant.register(with: self)
+    return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..ee868db
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,103 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "20x20"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "20x20"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "1x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "40x40"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "2x",
+      "size" : "60x60"
+    },
+    {
+      "idiom" : "iphone",
+      "scale" : "3x",
+      "size" : "60x60"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "20x20"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "20x20"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "29x29"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "40x40"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "40x40"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "1x",
+      "size" : "76x76"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "76x76"
+    },
+    {
+      "idiom" : "ipad",
+      "scale" : "2x",
+      "size" : "83.5x83.5"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "scale" : "1x",
+      "size" : "1024x1024"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..3efa7d1
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "filename" : "LaunchImage.png",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000..9da19ea
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
Binary files differ
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
+                        <viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <subviews>
+                            <imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
+                            </imageView>
+                        </subviews>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <constraints>
+                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
+                            <constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
+                        </constraints>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+    <resources>
+        <image name="LaunchImage" width="168" height="185"/>
+    </resources>
+</document>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Base.lproj/Main.storyboard b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <deployment identifier="iOS"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
+    </dependencies>
+    <scenes>
+        <!--Flutter View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
+                    <layoutGuides>
+                        <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+                        <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+                    </layoutGuides>
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Info.plist b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Info.plist
new file mode 100644
index 0000000..eb73a95
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Info.plist
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>ios_swift_unit_tests</string>
+	<key>CFBundlePackageType</key>
+	<string>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>$(FLUTTER_BUILD_NAME)</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>$(FLUTTER_BUILD_NUMBER)</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UIViewControllerBasedStatusBarAppearance</key>
+	<false/>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
+	<true/>
+</dict>
+</plist>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Runner-Bridging-Header.h b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..eb7e8ba
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1,5 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/AllDatatypesTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/AllDatatypesTests.swift
new file mode 100644
index 0000000..e962177
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/AllDatatypesTests.swift
@@ -0,0 +1,78 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class AllDatatypesTests: XCTestCase {
+  
+  func testAllNull() throws {
+    let everything = Everything()
+    let binaryMessenger = EchoBinaryMessenger(codec: FlutterEverythingCodec.shared)
+    let api = FlutterEverything(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    
+    api.echo(everything: everything) { result in
+      XCTAssertNil(result.aBool)
+      XCTAssertNil(result.anInt)
+      XCTAssertNil(result.aDouble)
+      XCTAssertNil(result.aString)
+      XCTAssertNil(result.aByteArray)
+      XCTAssertNil(result.a4ByteArray)
+      XCTAssertNil(result.a8ByteArray)
+      XCTAssertNil(result.aFloatArray)
+      XCTAssertNil(result.aList)
+      XCTAssertNil(result.aMap)
+      XCTAssertNil(result.nestedList)
+      XCTAssertNil(result.mapWithAnnotations)
+      XCTAssertNil(result.mapWithObject)
+      expectation.fulfill()
+    }
+    
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testAllEquals() throws {
+    let everything = Everything(
+      aBool: false,
+      anInt: 1,
+      aDouble: 2.0,
+      aString: "123",
+      aByteArray: [UInt8]("1234".data(using: .utf8)!),
+      a4ByteArray: [Int32].init(arrayLiteral: 1, 2, 3, 4),
+      a8ByteArray: [Int64].init(arrayLiteral: 1, 2, 3, 4, 5, 6, 7, 8),
+      aFloatArray: [Float64].init(arrayLiteral: 1, 2, 3, 4, 5, 6, 7, 8),
+      aList: [1, 2],
+      aMap: ["hello": 1234],
+      nestedList: [[true, false], [true]],
+      mapWithAnnotations: ["hello": "world"],
+      mapWithObject: ["hello": 1234, "goodbye" : "world"]
+    )
+    let binaryMessenger = EchoBinaryMessenger(codec: FlutterEverythingCodec.shared)
+    let api = FlutterEverything(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    
+    api.echo(everything: everything) { result in
+      XCTAssertEqual(result.aBool, everything.aBool)
+      XCTAssertEqual(result.anInt, everything.anInt)
+      XCTAssertEqual(result.aDouble, everything.aDouble)
+      XCTAssertEqual(result.aString, everything.aString)
+      XCTAssertEqual(result.aByteArray, everything.aByteArray)
+      XCTAssertEqual(result.a4ByteArray, everything.a4ByteArray)
+      XCTAssertEqual(result.a8ByteArray, everything.a8ByteArray)
+      XCTAssertEqual(result.aFloatArray, everything.aFloatArray)
+      XCTAssert(equalsList(result.aList, everything.aList))
+      XCTAssert(equalsDictionary(result.aMap, everything.aMap))
+      XCTAssertEqual(result.nestedList, everything.nestedList)
+      XCTAssertEqual(result.mapWithAnnotations, everything.mapWithAnnotations)
+      XCTAssert(equalsDictionary(result.mapWithObject, everything.mapWithObject))
+      
+      expectation.fulfill()
+    }
+    
+    wait(for: [expectation], timeout: 1.0)
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/AsyncHandlersTest.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/AsyncHandlersTest.swift
new file mode 100644
index 0000000..d7b2cb8
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/AsyncHandlersTest.swift
@@ -0,0 +1,74 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class MockApi2Host: Api2Host {
+  var output: Int32?
+  
+  func calculate(value: Value, completion: @escaping (Value) -> Void) {
+    completion(Value(number: output))
+  }
+  
+  func voidVoid(completion: @escaping () -> Void) {
+    completion()
+  }
+}
+
+class AsyncHandlersTest: XCTestCase {
+  
+  func testAsyncHost2Flutter() throws {
+    let binaryMessenger = MockBinaryMessenger<Value>(codec: Api2FlutterCodec.shared)
+    binaryMessenger.result = Value(number: 2)
+    let api2Flutter = Api2Flutter(binaryMessenger: binaryMessenger)
+    let input = Value(number: 1)
+    
+    let expectation = XCTestExpectation(description: "calculate callback")
+    api2Flutter.calculate(value: input) { output in
+      XCTAssertEqual(output.number, 2)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testAsyncFlutter2HostVoidVoid() throws {
+    let binaryMessenger = MockBinaryMessenger<Value>(codec: Api2HostCodec.shared)
+    let mockApi2Host = MockApi2Host()
+    mockApi2Host.output = 2
+    Api2HostSetup.setUp(binaryMessenger: binaryMessenger, api: mockApi2Host)
+    let channelName = "dev.flutter.pigeon.Api2Host.voidVoid"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let expectation = XCTestExpectation(description: "voidvoid callback")
+    binaryMessenger.handlers[channelName]?(nil) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNil(outputMap?["result"])
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testAsyncFlutter2Host() throws {
+    let binaryMessenger = MockBinaryMessenger<Value>(codec: Api2HostCodec.shared)
+    let mockApi2Host = MockApi2Host()
+    mockApi2Host.output = 2
+    Api2HostSetup.setUp(binaryMessenger: binaryMessenger, api: mockApi2Host)
+    let channelName = "dev.flutter.pigeon.Api2Host.calculate"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input = Value(number: 1)
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "calculate callback")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      let output = outputMap?["result"] as? Value
+      XCTAssertEqual(output?.number, 2)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/EchoBinaryMessenger.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/EchoBinaryMessenger.swift
new file mode 100644
index 0000000..393ff02
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/EchoBinaryMessenger.swift
@@ -0,0 +1,52 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Foundation
+import Flutter
+
+class EchoBinaryMessenger: NSObject, FlutterBinaryMessenger {
+  let codec: FlutterMessageCodec
+  private(set) var count = 0
+  var defaultReturn: Any?
+  
+  init(codec: FlutterMessageCodec) {
+    self.codec = codec
+    super.init()
+  }
+  
+  func send(onChannel channel: String, message: Data?) {
+    // Method not implemented because this messenger is just for echoing
+  }
+  
+  func send(
+    onChannel channel: String,
+    message: Data?,
+    binaryReply callback: FlutterBinaryReply? = nil
+  ) {
+    guard let callback = callback else { return }
+    
+    guard
+      let args = self.codec.decode(message) as? [Any?],
+      let firstArg = args.first,
+      !(firstArg is NSNull)
+    else {
+      callback(self.defaultReturn.flatMap { self.codec.encode($0) })
+      return
+    }
+    
+    callback(self.codec.encode(firstArg))
+  }
+  
+  func setMessageHandlerOnChannel(
+    _ channel: String, binaryMessageHandler handler:
+    FlutterBinaryMessageHandler? = nil
+  ) -> FlutterBinaryMessengerConnection {
+    self.count += 1
+    return FlutterBinaryMessengerConnection(self.count)
+  }
+  
+  func cleanUpConnection(_ connection: FlutterBinaryMessengerConnection) {
+    // Method not implemented because this messenger is just for echoing    
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/EnumTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/EnumTests.swift
new file mode 100644
index 0000000..3f6cc51
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/EnumTests.swift
@@ -0,0 +1,57 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class MockEnumApi2Host: EnumApi2Host {
+  func echo(data: DataWithEnum) -> DataWithEnum {
+    return data
+  }
+}
+
+extension DataWithEnum: Equatable {
+  public static func == (lhs: DataWithEnum, rhs: DataWithEnum) -> Bool {
+    lhs.state == rhs.state
+  }
+}
+
+class EnumTests: XCTestCase {
+  
+  func testEchoHost() throws {
+    let binaryMessenger = MockBinaryMessenger<DataWithEnum>(codec: EnumApi2HostCodec.shared)
+    EnumApi2HostSetup.setUp(binaryMessenger: binaryMessenger, api: MockEnumApi2Host())
+    let channelName = "dev.flutter.pigeon.EnumApi2Host.echo"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input = DataWithEnum(state: .success)
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "echo")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap?["result"] as? DataWithEnum
+      XCTAssertEqual(output, input)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testEchoFlutter() throws {
+    let data = DataWithEnum(state: .error)
+    let binaryMessenger = EchoBinaryMessenger(codec: EnumApi2HostCodec.shared)
+    let api = EnumApi2Flutter(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    api.echo(data: data) { result in
+      XCTAssertEqual(data.state, result.state)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/HandlerBinaryMessenger.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/HandlerBinaryMessenger.swift
new file mode 100644
index 0000000..998a55d
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/HandlerBinaryMessenger.swift
@@ -0,0 +1,52 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Foundation
+import Flutter
+
+typealias HandlerBinaryMessengerHandler = ([Any?])-> Any
+
+class HandlerBinaryMessenger: NSObject, FlutterBinaryMessenger {
+  let codec: FlutterMessageCodec
+  let handler: HandlerBinaryMessengerHandler
+  private var count = 0
+  
+  init(codec: FlutterMessageCodec, handler: @escaping HandlerBinaryMessengerHandler) {
+    self.codec = codec
+    self.handler = handler
+    super.init()
+  }
+  
+  func send(onChannel channel: String, message: Data?) {
+    // Method not implemented because this messenger is just for handling
+  }
+  
+  func send(
+    onChannel channel: String,
+    message: Data?,
+    binaryReply callback: FlutterBinaryReply? = nil
+  ) {
+    guard let callback = callback else { return }
+    
+    guard let args = self.codec.decode(message) as? [Any?] else {
+      callback(nil)
+      return
+    }
+    
+    let result = self.handler(args)
+    callback(self.codec.encode(result))
+  }
+  
+  func setMessageHandlerOnChannel(
+    _ channel: String,
+    binaryMessageHandler handler: FlutterBinaryMessageHandler? = nil
+  ) -> FlutterBinaryMessengerConnection {
+    self.count += 1
+    return FlutterBinaryMessengerConnection(self.count)
+  }
+  
+  func cleanUpConnection(_ connection: FlutterBinaryMessengerConnection) {
+    // Method not implemented because this messenger is just for handling
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/Info.plist b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/Info.plist
new file mode 100644
index 0000000..64d65ca
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/ListTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/ListTests.swift
new file mode 100644
index 0000000..f7d2a9e
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/ListTests.swift
@@ -0,0 +1,26 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class ListTests: XCTestCase {
+  
+  func testListInList() throws {
+    let inside = TestMessage(testList: [1, 2, 3])
+    let top = TestMessage(testList: [inside])
+    let binaryMessenger = EchoBinaryMessenger(codec: EchoApiCodec.shared)
+    let api = EchoApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    api.echo(msg: top) { result in
+      XCTAssertEqual(1, result.testList?.count)
+      XCTAssertTrue(result.testList?[0] is TestMessage)
+      XCTAssert(equalsList(inside.testList, (result.testList?[0] as! TestMessage).testList))
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/MockBinaryMessenger.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/MockBinaryMessenger.swift
new file mode 100644
index 0000000..c1818fd
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/MockBinaryMessenger.swift
@@ -0,0 +1,39 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Flutter
+@testable import Runner
+
+class MockBinaryMessenger<T>: NSObject, FlutterBinaryMessenger {
+  let codec: FlutterMessageCodec
+  var result: T?
+  private(set) var handlers: [String: FlutterBinaryMessageHandler] = [:]
+  
+  init(codec: FlutterMessageCodec) {
+    self.codec = codec
+    super.init()
+  }
+  
+  func send(onChannel channel: String, message: Data?) {}
+  
+  func send(
+    onChannel channel: String,
+    message: Data?,
+    binaryReply callback: FlutterBinaryReply? = nil
+  ) {
+    if let result = result {
+      callback?(codec.encode(result))
+    }
+  }
+  
+  func setMessageHandlerOnChannel(
+    _ channel: String,
+    binaryMessageHandler handler: FlutterBinaryMessageHandler? = nil
+  ) -> FlutterBinaryMessengerConnection {
+    handlers[channel] = handler
+    return .init(handlers.count)
+  }
+  
+  func cleanUpConnection(_ connection: FlutterBinaryMessengerConnection) {}
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/MultipleArityTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/MultipleArityTests.swift
new file mode 100644
index 0000000..f70f0e9
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/MultipleArityTests.swift
@@ -0,0 +1,53 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class MockMultipleArityHostApi: MultipleArityHostApi {
+  func subtract(x: Int32, y: Int32) -> Int32 {
+    return x - y
+  }
+}
+
+class MultipleArityTests: XCTestCase {
+  
+  func testSimpleHost() throws {
+    let binaryMessenger = MockBinaryMessenger<Int32>(codec: EnumApi2HostCodec.shared)
+    MultipleArityHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockMultipleArityHostApi())
+    let channelName = "dev.flutter.pigeon.MultipleArityHostApi.subtract"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let inputX = 10
+    let inputY = 7
+    let inputEncoded = binaryMessenger.codec.encode([inputX, inputY])
+    
+    let expectation = XCTestExpectation(description: "subtraction")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? Int32
+      XCTAssertEqual(3, output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testSimpleFlutter() throws {
+    let binaryMessenger = HandlerBinaryMessenger(codec: MultipleArityHostApiCodec.shared) { args in
+      return (args[0] as! Int) - (args[1] as! Int)
+    }
+    let api = MultipleArityFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "subtraction")
+    api.subtract(x: 30, y: 10) { result in
+      XCTAssertEqual(20, result)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/NonNullFieldsTest.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/NonNullFieldsTest.swift
new file mode 100644
index 0000000..805489f
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/NonNullFieldsTest.swift
@@ -0,0 +1,13 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class NonNullFieldsTests: XCTestCase {
+  func testMake() {
+    let request = NonNullFieldSearchRequest(query: "hello")
+    XCTAssertEqual("hello", request.query)
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/NullableReturnsTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/NullableReturnsTests.swift
new file mode 100644
index 0000000..7d8acf5
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/NullableReturnsTests.swift
@@ -0,0 +1,53 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class MockNullableArgHostApi: NullableArgHostApi {
+  var didCall: Bool = false
+  var x: Int32?
+  
+  func doit(x: Int32?) -> Int32 {
+    didCall = true
+    self.x = x
+    return x ?? 0
+  }
+}
+
+class NullableReturnsTests: XCTestCase {
+  func testNullableParameterWithFlutterApi() {
+    let binaryMessenger = EchoBinaryMessenger(codec: NullableArgFlutterApiCodec.shared)
+    binaryMessenger.defaultReturn = 99
+    let api = NullableArgFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    api.doit(x: nil) { result in
+      XCTAssertEqual(99, result)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testNullableParameterWithHostApi() {
+    let api = MockNullableArgHostApi()
+    let binaryMessenger = MockBinaryMessenger<Int32?>(codec: NullableArgHostApiCodec.shared)
+    let channel = "dev.flutter.pigeon.NullableArgHostApi.doit"
+    
+    NullableArgHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: api)
+    XCTAssertNotNil(binaryMessenger.handlers[channel])
+    
+    let inputEncoded = binaryMessenger.codec.encode([nil])
+    
+    let expectation = XCTestExpectation(description: "callback")
+    binaryMessenger.handlers[channel]?(inputEncoded) { _ in
+      expectation.fulfill()
+    }
+    
+    XCTAssertTrue(api.didCall)
+    XCTAssertNil(api.x)
+    wait(for: [expectation], timeout: 1.0)
+    
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/PrimitiveTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/PrimitiveTests.swift
new file mode 100644
index 0000000..f9c6b5f
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/PrimitiveTests.swift
@@ -0,0 +1,230 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class MockPrimitiveHostApi: PrimitiveHostApi {
+  func anInt(value: Int32) -> Int32 { value }
+  func aBool(value: Bool) -> Bool { value }
+  func aString(value: String) -> String { value  }
+  func aDouble(value: Double) -> Double { value }
+  func aMap(value: [AnyHashable: Any?]) -> [AnyHashable: Any?] { value }
+  func aList(value: [Any?]) -> [Any?] { value }
+  func anInt32List(value: [Int32]) -> [Int32] { value }
+  func aBoolList(value: [Bool?]) -> [Bool?] { value }
+  func aStringIntMap(value: [String?: Int32?]) -> [String?: Int32?] { value }
+}
+
+class PrimitiveTests: XCTestCase {
+  
+  func testIntPrimitiveHost() throws {
+    let binaryMessenger = MockBinaryMessenger<Int32>(codec: PrimitiveHostApiCodec.shared)
+    PrimitiveHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockPrimitiveHostApi())
+    let channelName = "dev.flutter.pigeon.PrimitiveHostApi.anInt"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input = 1
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "anInt")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? Int32
+      XCTAssertEqual(1, output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testIntPrimitiveFlutter() throws {
+    let binaryMessenger = EchoBinaryMessenger(codec: PrimitiveHostApiCodec.shared)
+    let api = PrimitiveFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    api.anInt(value: 1) { result in
+      XCTAssertEqual(1, result)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testBoolPrimitiveHost() throws {
+    let binaryMessenger = MockBinaryMessenger<Bool>(codec: PrimitiveHostApiCodec.shared)
+    PrimitiveHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockPrimitiveHostApi())
+    let channelName = "dev.flutter.pigeon.PrimitiveHostApi.aBool"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input = true
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "aBool")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? Bool
+      XCTAssertEqual(true, output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testBoolPrimitiveFlutter() throws {
+    let binaryMessenger = EchoBinaryMessenger(codec: PrimitiveHostApiCodec.shared)
+    let api = PrimitiveFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    api.aBool(value: true) { result in
+      XCTAssertEqual(true, result)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testDoublePrimitiveHost() throws {
+    let binaryMessenger = MockBinaryMessenger<Double>(codec: PrimitiveHostApiCodec.shared)
+    PrimitiveHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockPrimitiveHostApi())
+    let channelName = "dev.flutter.pigeon.PrimitiveHostApi.aDouble"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input: Double = 1.0
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "aDouble")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? Double
+      XCTAssertEqual(1.0, output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testDoublePrimitiveFlutter() throws {
+    let binaryMessenger = EchoBinaryMessenger(codec: PrimitiveHostApiCodec.shared)
+    let api = PrimitiveFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    let arg: Double = 1.5
+    api.aDouble(value: arg) { result in
+      XCTAssertEqual(arg, result)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testStringPrimitiveHost() throws {
+    let binaryMessenger = MockBinaryMessenger<String>(codec: PrimitiveHostApiCodec.shared)
+    PrimitiveHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockPrimitiveHostApi())
+    let channelName = "dev.flutter.pigeon.PrimitiveHostApi.aString"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input: String = "hello"
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "aString")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? String
+      XCTAssertEqual("hello", output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testStringPrimitiveFlutter() throws {
+    let binaryMessenger = EchoBinaryMessenger(codec: PrimitiveHostApiCodec.shared)
+    let api = PrimitiveFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    let arg: String = "hello"
+    api.aString(value: arg) { result in
+      XCTAssertEqual(arg, result)
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testListPrimitiveHost() throws {
+    let binaryMessenger = MockBinaryMessenger<[Int]>(codec: PrimitiveHostApiCodec.shared)
+    PrimitiveHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockPrimitiveHostApi())
+    let channelName = "dev.flutter.pigeon.PrimitiveHostApi.aList"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input: [Int] = [1, 2, 3]
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "aList")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? [Int]
+      XCTAssertEqual([1, 2, 3], output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testListPrimitiveFlutter() throws {
+    let binaryMessenger = EchoBinaryMessenger(codec: PrimitiveHostApiCodec.shared)
+    let api = PrimitiveFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    let arg = ["hello"]
+    api.aList(value: arg) { result in
+      XCTAssert(equalsList(arg, result))
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testMapPrimitiveHost() throws {
+    let binaryMessenger = MockBinaryMessenger<[String: Int]>(codec: PrimitiveHostApiCodec.shared)
+    PrimitiveHostApiSetup.setUp(binaryMessenger: binaryMessenger, api: MockPrimitiveHostApi())
+    let channelName = "dev.flutter.pigeon.PrimitiveHostApi.aMap"
+    XCTAssertNotNil(binaryMessenger.handlers[channelName])
+    
+    let input: [String: Int] = ["hello": 1, "world": 2]
+    let inputEncoded = binaryMessenger.codec.encode([input])
+    
+    let expectation = XCTestExpectation(description: "aMap")
+    binaryMessenger.handlers[channelName]?(inputEncoded) { data in
+      let outputMap = binaryMessenger.codec.decode(data) as? [String: Any]
+      XCTAssertNotNil(outputMap)
+      
+      let output = outputMap!["result"] as? [String: Int]
+      XCTAssertEqual(["hello": 1, "world": 2], output)
+      XCTAssertNil(outputMap?["error"])
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+  func testMapPrimitiveFlutter() throws {
+    let binaryMessenger = EchoBinaryMessenger(codec: PrimitiveHostApiCodec.shared)
+    let api = PrimitiveFlutterApi(binaryMessenger: binaryMessenger)
+    
+    let expectation = XCTestExpectation(description: "callback")
+    let arg = ["hello": 1]
+    api.aMap(value: arg) { result in
+      XCTAssert(equalsDictionary(arg, result))
+      expectation.fulfill()
+    }
+    wait(for: [expectation], timeout: 1.0)
+  }
+  
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/RunnerTests.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 0000000..cf55e4f
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,30 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import XCTest
+@testable import Runner
+
+class RunnerTests: XCTestCase {
+  
+  func testToMapAndBack() throws {
+    let reply = MessageSearchReply(result: "foobar")
+    let dict = reply.toMap()
+    let copy = MessageSearchReply.fromMap(dict)
+    XCTAssertEqual(reply.result, copy?.result)
+  }
+  
+  func testHandlesNull() throws {
+    let reply = MessageSearchReply()
+    let dict = reply.toMap()
+    let copy = MessageSearchReply.fromMap(dict)
+    XCTAssertNil(copy?.result)
+  }
+  
+  func testHandlesNullFirst() throws {
+    let reply = MessageSearchReply(error: "foobar")
+    let dict = reply.toMap()
+    let copy = MessageSearchReply.fromMap(dict)
+    XCTAssertEqual(reply.error, copy?.error)
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/Utils.swift b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/Utils.swift
new file mode 100644
index 0000000..341021b
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/ios/RunnerTests/Utils.swift
@@ -0,0 +1,38 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import Foundation
+
+func equals(_ x: Any?, _ y: Any?) -> Bool {
+  if x == nil, y == nil {
+    return true
+  }
+  
+  guard let x = x as? AnyHashable, let y = y as? AnyHashable else {
+    return false
+  }
+  return x == y
+}
+
+func equalsList(_ x: [Any?]?, _ y: [Any?]?) -> Bool {
+  if x == nil, y == nil {
+    return true
+  }
+  
+  guard x?.count == y?.count else { return false }
+  guard let x = x, let y = y else { return false }
+  
+  return (0..<x.count).allSatisfy { equals(x[$0], y[$0]) }
+}
+
+func equalsDictionary(_ x: [AnyHashable: Any?]?, _ y: [AnyHashable: Any?]?) -> Bool {
+  if x == nil, y == nil {
+    return true
+  }
+  
+  guard x?.count == y?.count else { return false }
+  guard let x = x, let y = y else { return false }
+  
+  return x.allSatisfy { equals($0.value, y[$0.key] as Any?) }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/lib/main.dart b/packages/pigeon/platform_tests/ios_swift_unit_tests/lib/main.dart
new file mode 100644
index 0000000..69750c1
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/lib/main.dart
@@ -0,0 +1,18 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+void main() => runApp(const MyApp());
+
+/// An empty app.
+class MyApp extends StatelessWidget {
+  /// Creates an empty app.
+  const MyApp({Key key}) : super(key: key);
+
+  @override
+  Widget build(BuildContext context) {
+    return Container();
+  }
+}
diff --git a/packages/pigeon/platform_tests/ios_swift_unit_tests/pubspec.yaml b/packages/pigeon/platform_tests/ios_swift_unit_tests/pubspec.yaml
new file mode 100644
index 0000000..1a3c6d8
--- /dev/null
+++ b/packages/pigeon/platform_tests/ios_swift_unit_tests/pubspec.yaml
@@ -0,0 +1,20 @@
+name: ios_swift_unit_tests
+description: A new Flutter project.
+publish_to: none
+
+version: 1.0.0+1
+
+environment:
+  sdk: ">=2.1.0 <3.0.0"
+
+dependencies:
+  cupertino_icons: ^0.1.2
+  flutter:
+    sdk: flutter
+
+dev_dependencies:
+  flutter_test:
+    sdk: flutter
+
+flutter:
+  uses-material-design: true
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner/Info.plist b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner/Info.plist
index 444570d..db82370 100644
--- a/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner/Info.plist
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/Runner/Info.plist
@@ -41,5 +41,7 @@
 	</array>
 	<key>UIViewControllerBasedStatusBarAppearance</key>
 	<false/>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
+	<true/>
 </dict>
 </plist>
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/EnumTest.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/EnumTest.m
index d3b63ea..f8b42c3 100644
--- a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/EnumTest.m
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/EnumTest.m
@@ -15,14 +15,14 @@
 @implementation EnumTest
 
 - (void)testEcho {
-  ACData *data = [[ACData alloc] init];
-  data.state = ACStateError;
+  ACDataWithEnum *data = [[ACDataWithEnum alloc] init];
+  data.state = ACEnumStateError;
   EchoBinaryMessenger *binaryMessenger =
       [[EchoBinaryMessenger alloc] initWithCodec:ACEnumApi2HostGetCodec()];
   ACEnumApi2Flutter *api = [[ACEnumApi2Flutter alloc] initWithBinaryMessenger:binaryMessenger];
   XCTestExpectation *expectation = [self expectationWithDescription:@"callback"];
   [api echoData:data
-      completion:^(ACData *_Nonnull result, NSError *_Nullable error) {
+      completion:^(ACDataWithEnum *_Nonnull result, NSError *_Nullable error) {
         XCTAssertEqual(data.state, result.state);
         [expectation fulfill];
       }];
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NonNullFieldsTest.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NonNullFieldsTest.m
index cd44eac..c3019f0 100644
--- a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NonNullFieldsTest.m
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/NonNullFieldsTest.m
@@ -16,7 +16,7 @@
 @implementation NonNullFieldsTest
 
 - (void)testMake {
-  NNFSearchRequest *request = [NNFSearchRequest makeWithQuery:@"hello"];
+  NNFNonNullFieldSearchRequest *request = [NNFNonNullFieldSearchRequest makeWithQuery:@"hello"];
   XCTAssertEqualObjects(@"hello", request.query);
 }
 
diff --git a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/RunnerTests.m b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/RunnerTests.m
index 6f50f43..8ee719e 100644
--- a/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/RunnerTests.m
+++ b/packages/pigeon/platform_tests/ios_unit_tests/ios/RunnerTests/RunnerTests.m
@@ -5,8 +5,8 @@
 #import <XCTest/XCTest.h>
 #import "message.gen.h"
 
-@interface ACSearchReply ()
-+ (ACSearchReply *)fromMap:(NSDictionary *)dict;
+@interface ACMessageSearchReply ()
++ (ACMessageSearchReply *)fromMap:(NSDictionary *)dict;
 - (NSDictionary *)toMap;
 @end
 
@@ -17,26 +17,26 @@
 @implementation RunnerTests
 
 - (void)testToMapAndBack {
-  ACSearchReply *reply = [[ACSearchReply alloc] init];
+  ACMessageSearchReply *reply = [[ACMessageSearchReply alloc] init];
   reply.result = @"foobar";
   NSDictionary *dict = [reply toMap];
-  ACSearchReply *copy = [ACSearchReply fromMap:dict];
+  ACMessageSearchReply *copy = [ACMessageSearchReply fromMap:dict];
   XCTAssertEqual(reply.result, copy.result);
 }
 
 - (void)testHandlesNull {
-  ACSearchReply *reply = [[ACSearchReply alloc] init];
+  ACMessageSearchReply *reply = [[ACMessageSearchReply alloc] init];
   reply.result = nil;
   NSDictionary *dict = [reply toMap];
-  ACSearchReply *copy = [ACSearchReply fromMap:dict];
+  ACMessageSearchReply *copy = [ACMessageSearchReply fromMap:dict];
   XCTAssertNil(copy.result);
 }
 
 - (void)testHandlesNullFirst {
-  ACSearchReply *reply = [[ACSearchReply alloc] init];
+  ACMessageSearchReply *reply = [[ACMessageSearchReply alloc] init];
   reply.error = @"foobar";
   NSDictionary *dict = [reply toMap];
-  ACSearchReply *copy = [ACSearchReply fromMap:dict];
+  ACMessageSearchReply *copy = [ACMessageSearchReply fromMap:dict];
   XCTAssertEqual(reply.error, copy.error);
 }
 
diff --git a/packages/pigeon/platform_tests/windows_unit_tests/windows/test/non_null_fields_test.cpp b/packages/pigeon/platform_tests/windows_unit_tests/windows/test/non_null_fields_test.cpp
index 95ee07a..ad443ba 100644
--- a/packages/pigeon/platform_tests/windows_unit_tests/windows/test/non_null_fields_test.cpp
+++ b/packages/pigeon/platform_tests/windows_unit_tests/windows/test/non_null_fields_test.cpp
@@ -9,7 +9,7 @@
 namespace non_null_fields_pigeontest {
 
 TEST(NonNullFields, Build) {
-  SearchRequest request;
+  NonNullFieldSearchRequest request;
   request.set_query("hello");
 
   EXPECT_EQ(request.query(), "hello");
diff --git a/packages/pigeon/platform_tests/windows_unit_tests/windows/test/pigeon_test.cpp b/packages/pigeon/platform_tests/windows_unit_tests/windows/test/pigeon_test.cpp
index eeabc3e..558d9d6 100644
--- a/packages/pigeon/platform_tests/windows_unit_tests/windows/test/pigeon_test.cpp
+++ b/packages/pigeon/platform_tests/windows_unit_tests/windows/test/pigeon_test.cpp
@@ -54,13 +54,13 @@
               (override));
 };
 
-class MockApi : public Api {
+class MockApi : public MessageApi {
  public:
   ~MockApi() = default;
 
   MOCK_METHOD(std::optional<FlutterError>, Initialize, (), (override));
-  MOCK_METHOD(ErrorOr<std::unique_ptr<SearchReply>>, Search,
-              (const SearchRequest&), (override));
+  MOCK_METHOD(ErrorOr<std::unique_ptr<MessageSearchReply>>, Search,
+              (const MessageSearchRequest&), (override));
 };
 
 class Writer : public flutter::ByteStreamWriter {
@@ -98,14 +98,15 @@
   flutter::BinaryMessageHandler handler;
   EXPECT_CALL(
       mock_messenger,
-      SetMessageHandler("dev.flutter.pigeon.Api.initialize", testing::_))
+      SetMessageHandler("dev.flutter.pigeon.MessageApi.initialize", testing::_))
       .Times(1)
       .WillOnce(testing::SaveArg<1>(&handler));
-  EXPECT_CALL(mock_messenger,
-              SetMessageHandler("dev.flutter.pigeon.Api.search", testing::_))
+  EXPECT_CALL(
+      mock_messenger,
+      SetMessageHandler("dev.flutter.pigeon.MessageApi.search", testing::_))
       .Times(1);
   EXPECT_CALL(mock_api, Initialize());
-  Api::SetUp(&mock_messenger, &mock_api);
+  MessageApi::SetUp(&mock_messenger, &mock_api);
   bool did_call_reply = false;
   flutter::BinaryReply reply = [&did_call_reply](const uint8_t* data,
                                                  size_t size) {
@@ -121,26 +122,27 @@
   flutter::BinaryMessageHandler handler;
   EXPECT_CALL(
       mock_messenger,
-      SetMessageHandler("dev.flutter.pigeon.Api.initialize", testing::_))
+      SetMessageHandler("dev.flutter.pigeon.MessageApi.initialize", testing::_))
       .Times(1);
-  EXPECT_CALL(mock_messenger,
-              SetMessageHandler("dev.flutter.pigeon.Api.search", testing::_))
+  EXPECT_CALL(
+      mock_messenger,
+      SetMessageHandler("dev.flutter.pigeon.MessageApi.search", testing::_))
       .Times(1)
       .WillOnce(testing::SaveArg<1>(&handler));
   EXPECT_CALL(mock_api, Search(testing::_))
-      .WillOnce(Return(ByMove(ErrorOr<SearchReply>::MakeWithUniquePtr(
-          std::make_unique<SearchReply>()))));
-  Api::SetUp(&mock_messenger, &mock_api);
+      .WillOnce(Return(ByMove(ErrorOr<MessageSearchReply>::MakeWithUniquePtr(
+          std::make_unique<MessageSearchReply>()))));
+  MessageApi::SetUp(&mock_messenger, &mock_api);
   bool did_call_reply = false;
   flutter::BinaryReply reply = [&did_call_reply](const uint8_t* data,
                                                  size_t size) {
     did_call_reply = true;
   };
-  SearchRequest request;
+  MessageSearchRequest request;
   Writer writer;
   flutter::EncodableList args;
   args.push_back(flutter::CustomEncodableValue(request));
-  ApiCodecSerializer::GetInstance().WriteValue(args, &writer);
+  MessageApiCodecSerializer::GetInstance().WriteValue(args, &writer);
   handler(writer.data_.data(), writer.data_.size(), reply);
   EXPECT_TRUE(did_call_reply);
 }
diff --git a/packages/pigeon/pubspec.yaml b/packages/pigeon/pubspec.yaml
index cb09a19..311f920 100644
--- a/packages/pigeon/pubspec.yaml
+++ b/packages/pigeon/pubspec.yaml
@@ -2,7 +2,7 @@
 description: Code generator tool to make communication between Flutter and the host platform type-safe and easier.
 repository: https://github.com/flutter/packages/tree/main/packages/pigeon
 issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3Apigeon
-version: 3.1.7 # This must match the version in lib/generator_tools.dart
+version: 3.2.0 # This must match the version in lib/generator_tools.dart
 
 environment:
   sdk: ">=2.12.0 <3.0.0"
diff --git a/packages/pigeon/test/pigeon_lib_test.dart b/packages/pigeon/test/pigeon_lib_test.dart
index 12bf9dc..da061d0 100644
--- a/packages/pigeon/test/pigeon_lib_test.dart
+++ b/packages/pigeon/test/pigeon_lib_test.dart
@@ -92,6 +92,12 @@
     expect(opts.objcSourceOut, equals('foo.m'));
   });
 
+  test('parse args - swift_out', () {
+    final PigeonOptions opts =
+        Pigeon.parseArgs(<String>['--experimental_swift_out', 'Foo.swift']);
+    expect(opts.swiftOut, equals('Foo.swift'));
+  });
+
   test('parse args - experimental_cpp_header_out', () {
     final PigeonOptions opts =
         Pigeon.parseArgs(<String>['--experimental_cpp_header_out', 'foo.h']);
@@ -415,6 +421,16 @@
     expect(buffer.toString(), startsWith('// Copyright 2013'));
   });
 
+  test('Swift generater copyright flag', () {
+    final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
+    const PigeonOptions options = PigeonOptions(
+        swiftOut: 'Foo.swift', copyrightHeader: './copyright_header.txt');
+    const SwiftGenerator swiftGenerator = SwiftGenerator();
+    final StringBuffer buffer = StringBuffer();
+    swiftGenerator.generate(buffer, options, root);
+    expect(buffer.toString(), startsWith('// Copyright 2013'));
+  });
+
   test('C++ header generater copyright flag', () {
     final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
     const PigeonOptions options = PigeonOptions(
diff --git a/packages/pigeon/test/swift_generator_test.dart b/packages/pigeon/test/swift_generator_test.dart
new file mode 100644
index 0000000..436d291
--- /dev/null
+++ b/packages/pigeon/test/swift_generator_test.dart
@@ -0,0 +1,997 @@
+// Copyright 2013 The Flutter Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:pigeon/ast.dart';
+import 'package:pigeon/swift_generator.dart';
+import 'package:test/test.dart';
+
+void main() {
+  test('gen one class', () {
+    final Class klass = Class(
+      name: 'Foobar',
+      fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'int',
+              isNullable: true,
+            ),
+            name: 'field1',
+            offset: null),
+      ],
+    );
+    final Root root = Root(
+      apis: <Api>[],
+      classes: <Class>[klass],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('struct Foobar'));
+    expect(code, contains('var field1: Int32? = nil'));
+    expect(code,
+        contains('static func fromMap(_ map: [String: Any?]) -> Foobar?'));
+    expect(code, contains('func toMap() -> [String: Any?]'));
+  });
+
+  test('gen one enum', () {
+    final Enum anEnum = Enum(
+      name: 'Foobar',
+      members: <String>[
+        'one',
+        'two',
+      ],
+    );
+    final Root root = Root(
+      apis: <Api>[],
+      classes: <Class>[],
+      enums: <Enum>[anEnum],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('enum Foobar: Int'));
+    expect(code, contains('  case one = 0'));
+    expect(code, contains('  case two = 1'));
+  });
+
+  test('primitive enum host', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Bar', location: ApiLocation.host, methods: <Method>[
+        Method(
+            name: 'bar',
+            returnType: const TypeDeclaration.voidDeclaration(),
+            arguments: <NamedType>[
+              NamedType(
+                  name: 'foo',
+                  type:
+                      const TypeDeclaration(baseName: 'Foo', isNullable: false))
+            ])
+      ])
+    ], classes: <Class>[], enums: <Enum>[
+      Enum(name: 'Foo', members: <String>['one', 'two'])
+    ]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('enum Foo: Int'));
+    expect(code, contains('let fooArg = Foo(rawValue: args[0] as! Int)!'));
+  });
+
+  test('gen one host api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: '',
+                offset: null)
+          ],
+          returnType:
+              const TypeDeclaration(baseName: 'Output', isNullable: false),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+      Class(name: 'Output', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'output',
+            offset: null)
+      ])
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('protocol Api'));
+    expect(code, matches('func doSomething.*Input.*Output'));
+    expect(code, contains('doSomethingChannel.setMessageHandler'));
+  });
+
+  test('all the simple datatypes header', () {
+    final Root root = Root(apis: <Api>[], classes: <Class>[
+      Class(name: 'Foobar', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'bool',
+              isNullable: true,
+            ),
+            name: 'aBool',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'int',
+              isNullable: true,
+            ),
+            name: 'aInt',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'double',
+              isNullable: true,
+            ),
+            name: 'aDouble',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'aString',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Uint8List',
+              isNullable: true,
+            ),
+            name: 'aUint8List',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Int32List',
+              isNullable: true,
+            ),
+            name: 'aInt32List',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Int64List',
+              isNullable: true,
+            ),
+            name: 'aInt64List',
+            offset: null),
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Float64List',
+              isNullable: true,
+            ),
+            name: 'aFloat64List',
+            offset: null),
+      ]),
+    ], enums: <Enum>[]);
+
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('var aBool: Bool? = nil'));
+    expect(code, contains('var aInt: Int32? = nil'));
+    expect(code, contains('var aDouble: Double? = nil'));
+    expect(code, contains('var aString: String? = nil'));
+    expect(code, contains('var aUint8List: [UInt8]? = nil'));
+    expect(code, contains('var aInt32List: [Int32]? = nil'));
+    expect(code, contains('var aInt64List: [Int64]? = nil'));
+    expect(code, contains('var aFloat64List: [Float64]? = nil'));
+  });
+
+  test('gen one flutter api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: '',
+                offset: null)
+          ],
+          returnType:
+              const TypeDeclaration(baseName: 'Output', isNullable: false),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+      Class(name: 'Output', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'output',
+            offset: null)
+      ])
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('class Api'));
+    expect(code, contains('init(binaryMessenger: FlutterBinaryMessenger)'));
+    expect(code, matches('func doSomething.*Input.*Output'));
+  });
+
+  test('gen host void api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: '',
+                offset: null)
+          ],
+          returnType: const TypeDeclaration.voidDeclaration(),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, isNot(matches('.*doSomething(.*) ->')));
+    expect(code, matches('doSomething(.*)'));
+  });
+
+  test('gen flutter void return api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: '',
+                offset: null)
+          ],
+          returnType: const TypeDeclaration.voidDeclaration(),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('completion: @escaping () -> Void'));
+    expect(code, contains('completion()'));
+  });
+
+  test('gen host void argument api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[],
+          returnType:
+              const TypeDeclaration(baseName: 'Output', isNullable: false),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Output', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'output',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func doSomething() -> Output'));
+    expect(code, contains('let result = api.doSomething()'));
+    expect(code, contains('reply(wrapResult(result))'));
+  });
+
+  test('gen flutter void argument api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[],
+          returnType:
+              const TypeDeclaration(baseName: 'Output', isNullable: false),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Output', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'output',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code,
+        contains('func doSomething(completion: @escaping (Output) -> Void)'));
+    expect(code, contains('channel.sendMessage(nil'));
+  });
+
+  test('gen list', () {
+    final Root root = Root(apis: <Api>[], classes: <Class>[
+      Class(name: 'Foobar', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'List',
+              isNullable: true,
+            ),
+            name: 'field1',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('struct Foobar'));
+    expect(code, contains('var field1: [Any?]? = nil'));
+  });
+
+  test('gen map', () {
+    final Root root = Root(apis: <Api>[], classes: <Class>[
+      Class(name: 'Foobar', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Map',
+              isNullable: true,
+            ),
+            name: 'field1',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('struct Foobar'));
+    expect(code, contains('var field1: [AnyHashable: Any?]? = nil'));
+  });
+
+  test('gen nested', () {
+    final Class klass = Class(
+      name: 'Outer',
+      fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Nested',
+              isNullable: true,
+            ),
+            name: 'nested',
+            offset: null)
+      ],
+    );
+    final Class nestedClass = Class(
+      name: 'Nested',
+      fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'int',
+              isNullable: true,
+            ),
+            name: 'data',
+            offset: null)
+      ],
+    );
+    final Root root = Root(
+      apis: <Api>[],
+      classes: <Class>[klass, nestedClass],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('struct Outer'));
+    expect(code, contains('struct Nested'));
+    expect(code, contains('var nested: Nested? = nil'));
+    expect(
+        code, contains('static func fromMap(_ map: [String: Any?]) -> Outer?'));
+    expect(code, contains('nested = Nested.fromMap(nestedMap)'));
+    expect(code, contains('func toMap() -> [String: Any?]'));
+  });
+
+  test('gen one async Host Api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: 'arg',
+                offset: null)
+          ],
+          returnType:
+              const TypeDeclaration(baseName: 'Output', isNullable: false),
+          isAsynchronous: true,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+      Class(name: 'Output', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'output',
+            offset: null)
+      ])
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('protocol Api'));
+    expect(code, contains('api.doSomething(arg: argArg) { result in'));
+    expect(code, contains('reply(wrapResult(result))'));
+  });
+
+  test('gen one async Flutter Api', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: '',
+                offset: null)
+          ],
+          returnType:
+              const TypeDeclaration(baseName: 'Output', isNullable: false),
+          isAsynchronous: true,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+      Class(name: 'Output', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: true,
+            ),
+            name: 'output',
+            offset: null)
+      ])
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('class Api'));
+    expect(code, matches('func doSomething.*Input.*completion.*Output.*Void'));
+  });
+
+  test('gen one enum class', () {
+    final Enum anEnum = Enum(
+      name: 'Enum1',
+      members: <String>[
+        'one',
+        'two',
+      ],
+    );
+    final Class klass = Class(
+      name: 'EnumClass',
+      fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'Enum1',
+              isNullable: true,
+            ),
+            name: 'enum1',
+            offset: null),
+      ],
+    );
+    final Root root = Root(
+      apis: <Api>[],
+      classes: <Class>[klass],
+      enums: <Enum>[anEnum],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('enum Enum1: Int'));
+    expect(code, contains('case one = 0'));
+    expect(code, contains('case two = 1'));
+  });
+
+  Iterable<String> _makeIterable(String string) sync* {
+    yield string;
+  }
+
+  test('header', () {
+    final Root root = Root(apis: <Api>[], classes: <Class>[], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    final SwiftOptions swiftOptions = SwiftOptions(
+      copyrightHeader: _makeIterable('hello world'),
+    );
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, startsWith('// hello world'));
+  });
+
+  test('generics - list', () {
+    final Class klass = Class(
+      name: 'Foobar',
+      fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+                baseName: 'List',
+                isNullable: true,
+                typeArguments: <TypeDeclaration>[
+                  TypeDeclaration(baseName: 'int', isNullable: true)
+                ]),
+            name: 'field1',
+            offset: null),
+      ],
+    );
+    final Root root = Root(
+      apis: <Api>[],
+      classes: <Class>[klass],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('struct Foobar'));
+    expect(code, contains('var field1: [Int32?]'));
+  });
+
+  test('generics - maps', () {
+    final Class klass = Class(
+      name: 'Foobar',
+      fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+                baseName: 'Map',
+                isNullable: true,
+                typeArguments: <TypeDeclaration>[
+                  TypeDeclaration(baseName: 'String', isNullable: true),
+                  TypeDeclaration(baseName: 'String', isNullable: true),
+                ]),
+            name: 'field1',
+            offset: null),
+      ],
+    );
+    final Root root = Root(
+      apis: <Api>[],
+      classes: <Class>[klass],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('struct Foobar'));
+    expect(code, contains('var field1: [String?: String?]'));
+  });
+
+  test('host generics argument', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              arguments: <NamedType>[
+                NamedType(
+                    type: const TypeDeclaration(
+                        baseName: 'List',
+                        isNullable: false,
+                        typeArguments: <TypeDeclaration>[
+                          TypeDeclaration(baseName: 'int', isNullable: true)
+                        ]),
+                    name: 'arg',
+                    offset: null)
+              ])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func doit(arg: [Int32?]'));
+  });
+
+  test('flutter generics argument', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              arguments: <NamedType>[
+                NamedType(
+                    type: const TypeDeclaration(
+                        baseName: 'List',
+                        isNullable: false,
+                        typeArguments: <TypeDeclaration>[
+                          TypeDeclaration(baseName: 'int', isNullable: true)
+                        ]),
+                    name: 'arg',
+                    offset: null)
+              ])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func doit(arg argArg: [Int32?]'));
+  });
+
+  test('host generics return', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration(
+                  baseName: 'List',
+                  isNullable: false,
+                  typeArguments: <TypeDeclaration>[
+                    TypeDeclaration(baseName: 'int', isNullable: true)
+                  ]),
+              arguments: <NamedType>[])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func doit() -> [Int32?]'));
+    expect(code, contains('let result = api.doit()'));
+    expect(code, contains('reply(wrapResult(result))'));
+  });
+
+  test('flutter generics return', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration(
+                  baseName: 'List',
+                  isNullable: false,
+                  typeArguments: <TypeDeclaration>[
+                    TypeDeclaration(baseName: 'int', isNullable: true)
+                  ]),
+              arguments: <NamedType>[])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(
+        code, contains('func doit(completion: @escaping ([Int32?]) -> Void'));
+    expect(code, contains('let result = response as! [Int32?]'));
+    expect(code, contains('completion(result)'));
+  });
+
+  test('host multiple args', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+        Method(
+          name: 'add',
+          arguments: <NamedType>[
+            NamedType(
+                name: 'x',
+                type:
+                    const TypeDeclaration(isNullable: false, baseName: 'int')),
+            NamedType(
+                name: 'y',
+                type:
+                    const TypeDeclaration(isNullable: false, baseName: 'int')),
+          ],
+          returnType: const TypeDeclaration(baseName: 'int', isNullable: false),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func add(x: Int32, y: Int32) -> Int32'));
+    expect(code, contains('let args = message as! [Any?]'));
+    expect(code, contains('let xArg = args[0] as! Int32'));
+    expect(code, contains('let yArg = args[1] as! Int32'));
+    expect(code, contains('let result = api.add(x: xArg, y: yArg)'));
+    expect(code, contains('reply(wrapResult(result))'));
+  });
+
+  test('flutter multiple args', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+        Method(
+          name: 'add',
+          arguments: <NamedType>[
+            NamedType(
+                name: 'x',
+                type:
+                    const TypeDeclaration(baseName: 'int', isNullable: false)),
+            NamedType(
+                name: 'y',
+                type:
+                    const TypeDeclaration(baseName: 'int', isNullable: false)),
+          ],
+          returnType: const TypeDeclaration(baseName: 'int', isNullable: false),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('let channel = FlutterBasicMessageChannel'));
+    expect(code, contains('let result = response as! Int32'));
+    expect(code, contains('completion(result)'));
+    expect(
+        code,
+        contains(
+            'func add(x xArg: Int32, y yArg: Int32, completion: @escaping (Int32) -> Void)'));
+    expect(code, contains('channel.sendMessage([xArg, yArg]) { response in'));
+  });
+
+  test('return nullable host', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration(
+                baseName: 'int',
+                isNullable: true,
+              ),
+              arguments: <NamedType>[])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func doit() -> Int32?'));
+  });
+
+  test('return nullable host async', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration(
+                baseName: 'int',
+                isNullable: true,
+              ),
+              isAsynchronous: true,
+              arguments: <NamedType>[])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('func doit(completion: @escaping (Int32?) -> Void'));
+  });
+
+  test('nullable argument host', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              arguments: <NamedType>[
+                NamedType(
+                    name: 'foo',
+                    type: const TypeDeclaration(
+                      baseName: 'int',
+                      isNullable: true,
+                    )),
+              ])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('let fooArg = args[0] as? Int32'));
+  });
+
+  test('nullable argument flutter', () {
+    final Root root = Root(
+      apis: <Api>[
+        Api(name: 'Api', location: ApiLocation.flutter, methods: <Method>[
+          Method(
+              name: 'doit',
+              returnType: const TypeDeclaration.voidDeclaration(),
+              arguments: <NamedType>[
+                NamedType(
+                    name: 'foo',
+                    type: const TypeDeclaration(
+                      baseName: 'int',
+                      isNullable: true,
+                    )),
+              ])
+        ])
+      ],
+      classes: <Class>[],
+      enums: <Enum>[],
+    );
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(
+        code,
+        contains(
+            'func doit(foo fooArg: Int32?, completion: @escaping () -> Void'));
+  });
+
+  test('nonnull fields', () {
+    final Root root = Root(apis: <Api>[
+      Api(name: 'Api', location: ApiLocation.host, methods: <Method>[
+        Method(
+          name: 'doSomething',
+          arguments: <NamedType>[
+            NamedType(
+                type: const TypeDeclaration(
+                  baseName: 'Input',
+                  isNullable: false,
+                ),
+                name: '',
+                offset: null)
+          ],
+          returnType: const TypeDeclaration.voidDeclaration(),
+          isAsynchronous: false,
+        )
+      ])
+    ], classes: <Class>[
+      Class(name: 'Input', fields: <NamedType>[
+        NamedType(
+            type: const TypeDeclaration(
+              baseName: 'String',
+              isNullable: false,
+            ),
+            name: 'input',
+            offset: null)
+      ]),
+    ], enums: <Enum>[]);
+    final StringBuffer sink = StringBuffer();
+    const SwiftOptions swiftOptions = SwiftOptions();
+    generateSwift(swiftOptions, root, sink);
+    final String code = sink.toString();
+    expect(code, contains('var input: String\n'));
+  });
+}
diff --git a/packages/pigeon/tool/run_tests.dart b/packages/pigeon/tool/run_tests.dart
index 791b67b..35d9851 100644
--- a/packages/pigeon/tool/run_tests.dart
+++ b/packages/pigeon/tool/run_tests.dart
@@ -44,11 +44,22 @@
   'ios_unittests': _TestInfo(
       function: _runIosUnitTests,
       description: 'Unit tests on generated Objective-C code.'),
+  'ios_swift_unittests': _TestInfo(
+      function: _runIosSwiftUnitTests,
+      description: 'Unit tests on generated Swift code.'),
   'mock_handler_tests': _TestInfo(
       function: _runMockHandlerTests,
       description: 'Unit tests on generated Dart mock handler code.'),
 };
 
+String snakeToPascalCase(String snake) {
+  final List<String> parts = snake.split('_');
+  return parts
+      .map((String part) =>
+          part.substring(0, 1).toUpperCase() + part.substring(1))
+      .join();
+}
+
 Future<Process> _streamOutput(Future<Process> processFuture) async {
   final Process process = await processFuture;
   stdout.addStream(process.stdout);
@@ -178,6 +189,70 @@
   throw UnimplementedError('See run_tests.sh.');
 }
 
+Future<int> _runIosSwiftUnitTests() async {
+  const String iosSwiftUnitTestsPath = './platform_tests/ios_swift_unit_tests';
+  const List<String> tests = <String>[
+    'all_datatypes',
+    'all_void',
+    'async_handlers',
+    'enum_args',
+    'enum',
+    'host2flutter',
+    'list',
+    'message',
+    'multiple_arity',
+    'non_null_fields',
+    'null_fields',
+    'nullable_returns',
+    'primitive',
+    'void_arg_flutter',
+    'void_arg_host',
+    'voidflutter',
+    'voidhost'
+  ];
+  int generateCode = 0;
+
+  for (final String test in tests) {
+    generateCode = await _runPigeon(
+      input: './pigeons/$test.dart',
+      iosSwiftOut:
+          '$iosSwiftUnitTestsPath/ios/Runner/${snakeToPascalCase(test)}.gen.swift',
+    );
+    if (generateCode != 0) {
+      return generateCode;
+    }
+  }
+
+  final Process compile = await _streamOutput(Process.start(
+    'flutter',
+    <String>['build', 'ios', '--simulator'],
+    workingDirectory: iosSwiftUnitTestsPath,
+    runInShell: true,
+  ));
+  final int compileCode = await compile.exitCode;
+  if (compileCode != 0) {
+    return compileCode;
+  }
+
+  final Process run = await _streamOutput(Process.start(
+    'xcodebuild',
+    <String>[
+      '-workspace',
+      'Runner.xcworkspace',
+      '-scheme',
+      'RunnerTests',
+      '-sdk',
+      'iphonesimulator',
+      '-destination',
+      'platform=iOS Simulator,name=iPhone 8',
+      'test',
+    ],
+    workingDirectory: '$iosSwiftUnitTestsPath/ios',
+  ));
+
+  return run.exitCode;
+}
+
 Future<int> _runMockHandlerTests() async {
   const String unitTestsPath = './mock_handler_tester';
   final int generateCode = await _runPigeon(
@@ -202,6 +277,7 @@
 
 Future<int> _runPigeon(
     {required String input,
+    String? iosSwiftOut,
     String? cppHeaderOut,
     String? cppSourceOut,
     String? cppNamespace,
@@ -217,6 +293,9 @@
     '--copyright_header',
     './copyright_header.txt',
   ];
+  if (iosSwiftOut != null) {
+    args.addAll(<String>['--experimental_swift_out', iosSwiftOut]);
+  }
   if (cppHeaderOut != null) {
     args.addAll(<String>[
       '--experimental_cpp_header_out',