Support materializing Flutter module host app on iOS (#21276)

* Prototype

* Fix paths to Flutter library resources

* Invoke pod install as necessary for materialized modules

* Add devicelab test for module use on iOS

* Remove debug output

* Rebase, reame materialize editable

* Add devicelab test editable iOS host app

* Removed add2app test section
diff --git a/dev/devicelab/bin/tasks/module_test.dart b/dev/devicelab/bin/tasks/module_test.dart
index 0014939..45e48b2 100644
--- a/dev/devicelab/bin/tasks/module_test.dart
+++ b/dev/devicelab/bin/tasks/module_test.dart
@@ -24,6 +24,7 @@
     section('Create Flutter module project');
 
     final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
+    final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
     try {
       await inDirectory(tempDir, () async {
         await flutter(
@@ -34,14 +35,14 @@
 
       section('Add plugins');
 
-      final File pubspec = File(path.join(tempDir.path, 'hello', 'pubspec.yaml'));
+      final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
       String content = await pubspec.readAsString();
       content = content.replaceFirst(
         '\ndependencies:\n',
         '\ndependencies:\n  battery:\n  package_info:\n',
       );
       await pubspec.writeAsString(content, flush: true);
-      await inDirectory(Directory(path.join(tempDir.path, 'hello')), () async {
+      await inDirectory(projectDir, () async {
         await flutter(
           'packages',
           options: <String>['get'],
@@ -50,7 +51,7 @@
 
       section('Build Flutter module library archive');
 
-      await inDirectory(Directory(path.join(tempDir.path, 'hello', '.android')), () async {
+      await inDirectory(Directory(path.join(projectDir.path, '.android')), () async {
         await exec(
           './gradlew',
           <String>['flutter:assembleDebug'],
@@ -59,8 +60,7 @@
       });
 
       final bool aarBuilt = exists(File(path.join(
-        tempDir.path,
-        'hello',
+        projectDir.path,
         '.android',
         'Flutter',
         'build',
@@ -75,7 +75,7 @@
 
       section('Build ephemeral host app');
 
-      await inDirectory(Directory(path.join(tempDir.path, 'hello')), () async {
+      await inDirectory(projectDir, () async {
         await flutter(
           'build',
           options: <String>['apk'],
@@ -83,8 +83,7 @@
       });
 
       final bool ephemeralHostApkBuilt = exists(File(path.join(
-        tempDir.path,
-        'hello',
+        projectDir.path,
         'build',
         'host',
         'outputs',
@@ -99,31 +98,30 @@
 
       section('Clean build');
 
-      await inDirectory(Directory(path.join(tempDir.path, 'hello')), () async {
+      await inDirectory(projectDir, () async {
         await flutter('clean');
       });
 
-      section('Running `flutter make-host-app-editable` to Materialize host app');
+      section('Make Android host app editable');
 
-      await inDirectory(Directory(path.join(tempDir.path, 'hello')), () async {
+      await inDirectory(projectDir, () async {
         await flutter(
           'make-host-app-editable',
           options: <String>['android'],
         );
       });
 
-      section('Build materialized host app');
+      section('Build editable host app');
 
-      await inDirectory(Directory(path.join(tempDir.path, 'hello')), () async {
+      await inDirectory(projectDir, () async {
         await flutter(
           'build',
           options: <String>['apk'],
         );
       });
 
-      final bool materializedHostApkBuilt = exists(File(path.join(
-        tempDir.path,
-        'hello',
+      final bool editableHostApkBuilt = exists(File(path.join(
+        projectDir.path,
         'build',
         'host',
         'outputs',
@@ -132,11 +130,11 @@
         'app-release.apk',
       )));
 
-      if (!materializedHostApkBuilt) {
-        return TaskResult.failure('Failed to build materialized host .apk');
+      if (!editableHostApkBuilt) {
+        return TaskResult.failure('Failed to build editable host .apk');
       }
 
-      section('Add to Android app');
+      section('Add to existing Android app');
 
       final Directory hostApp = Directory(path.join(tempDir.path, 'hello_host_app'));
       mkdir(hostApp);
@@ -145,11 +143,11 @@
         hostApp,
       );
       copy(
-        File(path.join(tempDir.path, 'hello', '.android', 'gradlew')),
+        File(path.join(projectDir.path, '.android', 'gradlew')),
         hostApp,
       );
       copy(
-        File(path.join(tempDir.path, 'hello', '.android', 'gradle', 'wrapper', 'gradle-wrapper.jar')),
+        File(path.join(projectDir.path, '.android', 'gradle', 'wrapper', 'gradle-wrapper.jar')),
         Directory(path.join(hostApp.path, 'gradle', 'wrapper')),
       );
 
diff --git a/dev/devicelab/bin/tasks/module_test_ios.dart b/dev/devicelab/bin/tasks/module_test_ios.dart
new file mode 100644
index 0000000..b69db06
--- /dev/null
+++ b/dev/devicelab/bin/tasks/module_test_ios.dart
@@ -0,0 +1,137 @@
+// Copyright (c) 2018 The Chromium 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 'dart:async';
+import 'dart:io';
+
+import 'package:flutter_devicelab/framework/framework.dart';
+import 'package:flutter_devicelab/framework/ios.dart';
+import 'package:flutter_devicelab/framework/utils.dart';
+import 'package:path/path.dart' as path;
+
+/// Tests that the Flutter module project template works and supports
+/// adding Flutter to an existing iOS app.
+Future<Null> main() async {
+  await task(() async {
+
+    section('Create Flutter module project');
+
+    final Directory tempDir = Directory.systemTemp.createTempSync('flutter_module_test.');
+    final Directory projectDir = Directory(path.join(tempDir.path, 'hello'));
+    try {
+      await inDirectory(tempDir, () async {
+        await flutter(
+          'create',
+          options: <String>['--org', 'io.flutter.devicelab', '-t', 'module', 'hello'],
+        );
+      });
+      await prepareProvisioningCertificates(projectDir.path);
+
+      section('Build ephemeral host app without CocoaPods');
+
+      await inDirectory(projectDir, () async {
+        await flutter(
+          'build',
+          options: <String>['ios'],
+        );
+      });
+
+      final bool ephemeralHostAppBuilt = exists(Directory(path.join(
+        projectDir.path,
+        'build',
+        'ios',
+        'iphoneos',
+        'Runner.app',
+      )));
+
+      if (!ephemeralHostAppBuilt) {
+        return TaskResult.failure('Failed to build ephemeral host .app');
+      }
+
+      section('Clean build');
+
+      await inDirectory(projectDir, () async {
+        await flutter('clean');
+      });
+
+      section('Add plugins');
+
+      final File pubspec = File(path.join(projectDir.path, 'pubspec.yaml'));
+      String content = await pubspec.readAsString();
+      content = content.replaceFirst(
+        '\ndependencies:\n',
+        '\ndependencies:\n  battery:\n  package_info:\n',
+      );
+      await pubspec.writeAsString(content, flush: true);
+      await inDirectory(projectDir, () async {
+        await flutter(
+          'packages',
+          options: <String>['get'],
+        );
+      });
+
+      section('Build ephemeral host app with CocoaPods');
+
+      await inDirectory(projectDir, () async {
+        await flutter(
+          'build',
+          options: <String>['ios'],
+        );
+      });
+
+      final bool ephemeralHostAppWithCocoaPodsBuilt = exists(Directory(path.join(
+        projectDir.path,
+        'build',
+        'ios',
+        'iphoneos',
+        'Runner.app',
+      )));
+
+      if (!ephemeralHostAppWithCocoaPodsBuilt) {
+        return TaskResult.failure('Failed to build ephemeral host .app with CocoaPods');
+      }
+
+      section('Clean build');
+
+      await inDirectory(projectDir, () async {
+        await flutter('clean');
+      });
+
+      section('Make iOS host app editable');
+
+      await inDirectory(projectDir, () async {
+        await flutter(
+          'make-host-app-editable',
+          options: <String>['ios'],
+        );
+      });
+
+      section('Build editable host app');
+
+      await inDirectory(projectDir, () async {
+        await flutter(
+          'build',
+          options: <String>['ios'],
+        );
+      });
+
+      final bool editableHostAppBuilt = exists(Directory(path.join(
+        projectDir.path,
+        'build',
+        'ios',
+        'iphoneos',
+        'Runner.app',
+      )));
+
+      if (!editableHostAppBuilt) {
+        return TaskResult.failure('Failed to build editable host .app');
+      }
+      return TaskResult.success(null);
+    } catch (e) {
+      return TaskResult.failure(e.toString());
+    } finally {
+      //rmTree(tempDir);
+    }
+  });
+}
diff --git a/dev/devicelab/manifest.yaml b/dev/devicelab/manifest.yaml
index 793421d..0078092 100644
--- a/dev/devicelab/manifest.yaml
+++ b/dev/devicelab/manifest.yaml
@@ -297,6 +297,13 @@
     stage: devicelab_ios
     required_agent_capabilities: ["mac/ios"]
 
+  module_test_ios:
+    description: >
+      Checks that the module project template works and supports add2app on iOS.
+    stage: devicelab
+    required_agent_capabilities: ["mac/ios"]
+    flaky: true
+
   external_ui_integration_test_ios:
     description: >
       Checks that external UIs work on iOS.
diff --git a/dev/integration_tests/ios_host_app/Config/Debug.xcconfig b/dev/integration_tests/ios_host_app/Config/Debug.xcconfig
new file mode 100644
index 0000000..55d2bd8
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Config/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "Flutter.xcconfig"
+#include "Pods/Target Support Files/Pods-Host/Pods-Host.debug.xcconfig"
diff --git a/dev/integration_tests/ios_host_app/Config/Flutter.xcconfig b/dev/integration_tests/ios_host_app/Config/Flutter.xcconfig
new file mode 100644
index 0000000..ccfdd63
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Config/Flutter.xcconfig
@@ -0,0 +1,2 @@
+#include "../../hello/.ios/Flutter/Generated.xcconfig"
+ENABLE_BITCODE=NO
diff --git a/dev/integration_tests/ios_host_app/Config/Release.xcconfig b/dev/integration_tests/ios_host_app/Config/Release.xcconfig
new file mode 100644
index 0000000..a0a0197
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Config/Release.xcconfig
@@ -0,0 +1,3 @@
+#include "Flutter.xcconfig"
+#include "Pods/Target Support Files/Pods-Host/Pods-Host.release.xcconfig"
+FLUTTER_BUILD_MODE=release
diff --git a/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..c6086b7
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj
@@ -0,0 +1,449 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 50;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		74DB4A4E2152F3F900E9B550 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 74DB4A4D2152F3F900E9B550 /* AppDelegate.m */; };
+		74DB4A512152F3F900E9B550 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 74DB4A502152F3F900E9B550 /* ViewController.m */; };
+		74DB4A542152F3F900E9B550 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74DB4A522152F3F900E9B550 /* Main.storyboard */; };
+		74DB4A562152F3FB00E9B550 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 74DB4A552152F3FB00E9B550 /* Assets.xcassets */; };
+		74DB4A592152F3FB00E9B550 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 74DB4A572152F3FB00E9B550 /* LaunchScreen.storyboard */; };
+		74DB4A5C2152F3FB00E9B550 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 74DB4A5B2152F3FB00E9B550 /* main.m */; };
+		74DB4A872154203700E9B550 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 74DB4A862154203700E9B550 /* flutter_assets */; };
+		74DB4A8B2154205B00E9B550 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 74DB4A842154201200E9B550 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		74DB4A8E2154205F00E9B550 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 74DB4A882154204700E9B550 /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+		F34F00DB71F8C65CEA61A90A /* libPods-Host.a in Frameworks */ = {isa = PBXBuildFile; fileRef = A7845ED770D25CF67B243D1A /* libPods-Host.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		74DB4A8C2154205B00E9B550 /* Embed Frameworks */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				74DB4A8B2154205B00E9B550 /* App.framework in Embed Frameworks */,
+				74DB4A8E2154205F00E9B550 /* Flutter.framework in Embed Frameworks */,
+			);
+			name = "Embed Frameworks";
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		74DB4A492152F3F900E9B550 /* Host.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Host.app; sourceTree = BUILT_PRODUCTS_DIR; };
+		74DB4A4C2152F3F900E9B550 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+		74DB4A4D2152F3F900E9B550 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+		74DB4A4F2152F3F900E9B550 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
+		74DB4A502152F3F900E9B550 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
+		74DB4A532152F3F900E9B550 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		74DB4A552152F3FB00E9B550 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		74DB4A582152F3FB00E9B550 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
+		74DB4A5A2152F3FB00E9B550 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		74DB4A5B2152F3FB00E9B550 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+		74DB4A7F2152F49200E9B550 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
+		74DB4A802152F4A400E9B550 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
+		74DB4A8221541FEE00E9B550 /* Flutter.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = "<group>"; };
+		74DB4A842154201200E9B550 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = ../../hello/.ios/Flutter/App.framework; sourceTree = "<group>"; };
+		74DB4A862154203700E9B550 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../../hello/.ios/Flutter/flutter_assets; sourceTree = "<group>"; };
+		74DB4A882154204700E9B550 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = ../../hello/.ios/Flutter/engine/Flutter.framework; sourceTree = "<group>"; };
+		A7845ED770D25CF67B243D1A /* libPods-Host.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Host.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		74DB4A462152F3F900E9B550 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				F34F00DB71F8C65CEA61A90A /* libPods-Host.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		74DB4A402152F3F900E9B550 = {
+			isa = PBXGroup;
+			children = (
+				74DB4A8121541FDF00E9B550 /* Flutter */,
+				74DB4A7E2152F47500E9B550 /* Config */,
+				74DB4A4B2152F3F900E9B550 /* Host */,
+				74DB4A4A2152F3F900E9B550 /* Products */,
+				D22B5E2B5577AC172019DDE2 /* Pods */,
+				76443A8345AFB0A4BBAA0AC0 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		74DB4A4A2152F3F900E9B550 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				74DB4A492152F3F900E9B550 /* Host.app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		74DB4A4B2152F3F900E9B550 /* Host */ = {
+			isa = PBXGroup;
+			children = (
+				74DB4A4C2152F3F900E9B550 /* AppDelegate.h */,
+				74DB4A4D2152F3F900E9B550 /* AppDelegate.m */,
+				74DB4A4F2152F3F900E9B550 /* ViewController.h */,
+				74DB4A502152F3F900E9B550 /* ViewController.m */,
+				74DB4A522152F3F900E9B550 /* Main.storyboard */,
+				74DB4A552152F3FB00E9B550 /* Assets.xcassets */,
+				74DB4A572152F3FB00E9B550 /* LaunchScreen.storyboard */,
+				74DB4A5A2152F3FB00E9B550 /* Info.plist */,
+				74DB4A5B2152F3FB00E9B550 /* main.m */,
+			);
+			path = Host;
+			sourceTree = "<group>";
+		};
+		74DB4A7E2152F47500E9B550 /* Config */ = {
+			isa = PBXGroup;
+			children = (
+				74DB4A8221541FEE00E9B550 /* Flutter.xcconfig */,
+				74DB4A7F2152F49200E9B550 /* Debug.xcconfig */,
+				74DB4A802152F4A400E9B550 /* Release.xcconfig */,
+			);
+			path = Config;
+			sourceTree = "<group>";
+		};
+		74DB4A8121541FDF00E9B550 /* Flutter */ = {
+			isa = PBXGroup;
+			children = (
+				74DB4A882154204700E9B550 /* Flutter.framework */,
+				74DB4A862154203700E9B550 /* flutter_assets */,
+				74DB4A842154201200E9B550 /* App.framework */,
+			);
+			path = Flutter;
+			sourceTree = "<group>";
+		};
+		76443A8345AFB0A4BBAA0AC0 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				A7845ED770D25CF67B243D1A /* libPods-Host.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		D22B5E2B5577AC172019DDE2 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		74DB4A482152F3F900E9B550 /* Host */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 74DB4A752152F3FB00E9B550 /* Build configuration list for PBXNativeTarget "Host" */;
+			buildPhases = (
+				30AC91A315B5AD0C33571E2F /* [CP] Check Pods Manifest.lock */,
+				74DB4A452152F3F900E9B550 /* Sources */,
+				74DB4A462152F3F900E9B550 /* Frameworks */,
+				74DB4A472152F3F900E9B550 /* Resources */,
+				00F45C5930D73692644264A6 /* [CP] Embed Pods Frameworks */,
+				74DB4A8C2154205B00E9B550 /* Embed Frameworks */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = Host;
+			productName = Host;
+			productReference = 74DB4A492152F3F900E9B550 /* Host.app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		74DB4A412152F3F900E9B550 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0940;
+				ORGANIZATIONNAME = Flutter;
+				TargetAttributes = {
+					74DB4A482152F3F900E9B550 = {
+						CreatedOnToolsVersion = 9.4.1;
+					};
+				};
+			};
+			buildConfigurationList = 74DB4A442152F3F900E9B550 /* Build configuration list for PBXProject "Host" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 74DB4A402152F3F900E9B550;
+			productRefGroup = 74DB4A4A2152F3F900E9B550 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				74DB4A482152F3F900E9B550 /* Host */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		74DB4A472152F3F900E9B550 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				74DB4A592152F3FB00E9B550 /* LaunchScreen.storyboard in Resources */,
+				74DB4A872154203700E9B550 /* flutter_assets in Resources */,
+				74DB4A562152F3FB00E9B550 /* Assets.xcassets in Resources */,
+				74DB4A542152F3F900E9B550 /* Main.storyboard in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		00F45C5930D73692644264A6 /* [CP] Embed Pods Frameworks */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${SRCROOT}/Pods/Target Support Files/Pods-Host/Pods-Host-frameworks.sh",
+				"${PODS_ROOT}/../../tst04/.ios/Flutter/engine/Flutter.framework",
+			);
+			name = "[CP] Embed Pods Frameworks";
+			outputPaths = (
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Host/Pods-Host-frameworks.sh\"\n";
+			showEnvVarsInLog = 0;
+		};
+		30AC91A315B5AD0C33571E2F /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-Host-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		74DB4A452152F3F900E9B550 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				74DB4A512152F3F900E9B550 /* ViewController.m in Sources */,
+				74DB4A5C2152F3FB00E9B550 /* main.m in Sources */,
+				74DB4A4E2152F3F900E9B550 /* AppDelegate.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+		74DB4A522152F3F900E9B550 /* Main.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				74DB4A532152F3F900E9B550 /* Base */,
+			);
+			name = Main.storyboard;
+			sourceTree = "<group>";
+		};
+		74DB4A572152F3FB00E9B550 /* LaunchScreen.storyboard */ = {
+			isa = PBXVariantGroup;
+			children = (
+				74DB4A582152F3FB00E9B550 /* Base */,
+			);
+			name = LaunchScreen.storyboard;
+			sourceTree = "<group>";
+		};
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+		74DB4A732152F3FB00E9B550 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		74DB4A742152F3FB00E9B550 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "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 = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.4;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		74DB4A762152F3FB00E9B550 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 74DB4A7F2152F49200E9B550 /* Debug.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = Host/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				DEVELOPMENT_TEAM = RW9CXS8BK2;
+				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.add2app.Host;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		74DB4A772152F3FB00E9B550 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 74DB4A802152F4A400E9B550 /* Release.xcconfig */;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				INFOPLIST_FILE = Host/Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				DEVELOPMENT_TEAM = RW9CXS8BK2;
+				PRODUCT_BUNDLE_IDENTIFIER = io.flutter.add2app.Host;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		74DB4A442152F3F900E9B550 /* Build configuration list for PBXProject "Host" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				74DB4A732152F3FB00E9B550 /* Debug */,
+				74DB4A742152F3FB00E9B550 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		74DB4A752152F3FB00E9B550 /* Build configuration list for PBXNativeTarget "Host" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				74DB4A762152F3FB00E9B550 /* Debug */,
+				74DB4A772152F3FB00E9B550 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 74DB4A412152F3F900E9B550 /* Project object */;
+}
diff --git a/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..8ee5ee9
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:Host.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host.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/dev/integration_tests/ios_host_app/Host.xcworkspace/contents.xcworkspacedata b/dev/integration_tests/ios_host_app/Host.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..8e65b67
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "group:Host.xcodeproj">
+   </FileRef>
+   <FileRef
+      location = "group:Pods/Pods.xcodeproj">
+   </FileRef>
+</Workspace>
diff --git a/dev/integration_tests/ios_host_app/Host.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/dev/integration_tests/ios_host_app/Host.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host.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/dev/integration_tests/ios_host_app/Host.xcworkspace/xcuserdata/mravn.xcuserdatad/UserInterfaceState.xcuserstate b/dev/integration_tests/ios_host_app/Host.xcworkspace/xcuserdata/mravn.xcuserdatad/UserInterfaceState.xcuserstate
new file mode 100644
index 0000000..40eb4bc
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host.xcworkspace/xcuserdata/mravn.xcuserdatad/UserInterfaceState.xcuserstate
Binary files differ
diff --git a/dev/integration_tests/ios_host_app/Host/AppDelegate.h b/dev/integration_tests/ios_host_app/Host/AppDelegate.h
new file mode 100644
index 0000000..d944abe
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/AppDelegate.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+#import <Flutter/Flutter.h>
+
+@interface AppDelegate : FlutterAppDelegate
+@end
diff --git a/dev/integration_tests/ios_host_app/Host/AppDelegate.m b/dev/integration_tests/ios_host_app/Host/AppDelegate.m
new file mode 100644
index 0000000..9432fc5
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/AppDelegate.m
@@ -0,0 +1,4 @@
+#import "AppDelegate.h"
+
+@implementation AppDelegate
+@end
diff --git a/dev/integration_tests/ios_host_app/Host/Assets.xcassets/AppIcon.appiconset/Contents.json b/dev/integration_tests/ios_host_app/Host/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..d8db8d6
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,98 @@
+{
+  "images" : [
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "20x20",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "29x29",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "40x40",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "iphone",
+      "size" : "60x60",
+      "scale" : "3x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "20x20",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "29x29",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "40x40",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "1x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "76x76",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ipad",
+      "size" : "83.5x83.5",
+      "scale" : "2x"
+    },
+    {
+      "idiom" : "ios-marketing",
+      "size" : "1024x1024",
+      "scale" : "1x"
+    }
+  ],
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/dev/integration_tests/ios_host_app/Host/Assets.xcassets/Contents.json b/dev/integration_tests/ios_host_app/Host/Assets.xcassets/Contents.json
new file mode 100644
index 0000000..da4a164
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "version" : 1,
+    "author" : "xcode"
+  }
+}
\ No newline at end of file
diff --git a/dev/integration_tests/ios_host_app/Host/Base.lproj/LaunchScreen.storyboard b/dev/integration_tests/ios_host_app/Host/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f83f6fd
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="EHf-IW-A2E">
+            <objects>
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
+            </objects>
+            <point key="canvasLocation" x="53" y="375"/>
+        </scene>
+    </scenes>
+</document>
diff --git a/dev/integration_tests/ios_host_app/Host/Base.lproj/Main.storyboard b/dev/integration_tests/ios_host_app/Host/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..d7c78a1
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/Base.lproj/Main.storyboard
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" systemVersion="17A277" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
+    <dependencies>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
+    </dependencies>
+    <scenes>
+        <!--View Controller-->
+        <scene sceneID="tne-QT-ifu">
+            <objects>
+                <viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="" sceneMemberID="viewController">
+                    <view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+                        <viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
+                    </view>
+                </viewController>
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+            </objects>
+        </scene>
+    </scenes>
+</document>
diff --git a/dev/integration_tests/ios_host_app/Host/Info.plist b/dev/integration_tests/ios_host_app/Host/Info.plist
new file mode 100644
index 0000000..16be3b6
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/Info.plist
@@ -0,0 +1,45 @@
+<?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>APPL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>UILaunchStoryboardName</key>
+	<string>LaunchScreen</string>
+	<key>UIMainStoryboardFile</key>
+	<string>Main</string>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<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>
+</dict>
+</plist>
diff --git a/dev/integration_tests/ios_host_app/Host/ViewController.h b/dev/integration_tests/ios_host_app/Host/ViewController.h
new file mode 100644
index 0000000..143825c
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/ViewController.h
@@ -0,0 +1,5 @@
+#import <UIKit/UIKit.h>
+
+@interface ViewController : UIViewController
+@end
+
diff --git a/dev/integration_tests/ios_host_app/Host/ViewController.m b/dev/integration_tests/ios_host_app/Host/ViewController.m
new file mode 100644
index 0000000..b8db22b
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/ViewController.m
@@ -0,0 +1,23 @@
+#import "ViewController.h"
+#import "Flutter/Flutter.h"
+#import "FlutterPluginRegistrant/GeneratedPluginRegistrant.h"
+
+@implementation ViewController
+- (void)viewDidLoad {
+    [super viewDidLoad];
+    UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
+    [button addTarget:self
+               action:@selector(handleButtonAction)
+     forControlEvents:UIControlEventTouchUpInside];
+    [button setTitle:@"Press me" forState:UIControlStateNormal];
+    [button setBackgroundColor:[UIColor blueColor]];
+    button.frame = CGRectMake(80.0, 210.0, 160.0, 40.0);
+    [self.view addSubview:button];
+}
+
+- (void)handleButtonAction {
+    FlutterViewController* flutterViewController = [[FlutterViewController alloc] init];
+    [GeneratedPluginRegistrant registerWithRegistry:flutterViewController];
+    [self presentViewController:flutterViewController animated:false completion:nil];
+}
+@end
diff --git a/dev/integration_tests/ios_host_app/Host/main.m b/dev/integration_tests/ios_host_app/Host/main.m
new file mode 100644
index 0000000..81e84cb
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Host/main.m
@@ -0,0 +1,8 @@
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char * argv[]) {
+    @autoreleasepool {
+        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
+    }
+}
diff --git a/dev/integration_tests/ios_host_app/Podfile b/dev/integration_tests/ios_host_app/Podfile
new file mode 100644
index 0000000..bac985d
--- /dev/null
+++ b/dev/integration_tests/ios_host_app/Podfile
@@ -0,0 +1,6 @@
+platform :ios, '9.0'
+
+target 'Host' do
+  flutter_application_path = '../hello'
+  eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
+end
diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh
index 100b158..655040a 100755
--- a/packages/flutter_tools/bin/xcode_backend.sh
+++ b/packages/flutter_tools/bin/xcode_backend.sh
@@ -80,6 +80,9 @@
   AssertExists "${project_path}"
 
   local derived_dir="${SOURCE_ROOT}/Flutter"
+  if [[ -e "${project_path}/.ios" ]]; then
+    derived_dir="${SOURCE_ROOT}/../.ios/Flutter"
+  fi
   RunCommand mkdir -p -- "$derived_dir"
   AssertExists "$derived_dir"
 
diff --git a/packages/flutter_tools/lib/src/ios/cocoapods.dart b/packages/flutter_tools/lib/src/ios/cocoapods.dart
index 96f7d32..184a24c 100644
--- a/packages/flutter_tools/lib/src/ios/cocoapods.dart
+++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart
@@ -156,7 +156,7 @@
       // Don't do anything for iOS when host platform doesn't support it.
       return;
     }
-    final Directory runnerProject = iosProject.directory.childDirectory('Runner.xcodeproj');
+    final Directory runnerProject = iosProject.xcodeProject;
     if (!runnerProject.existsSync()) {
       return;
     }
@@ -223,7 +223,7 @@
     final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true);
     final ProcessResult result = await processManager.run(
       <String>['pod', 'install', '--verbose'],
-      workingDirectory: iosProject.directory.path,
+      workingDirectory: iosProject.hostAppRoot.path,
       environment: <String, String>{
         // For backward compatibility with previously created Podfile only.
         'FLUTTER_FRAMEWORK_DIR': engineDirectory,
diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 53c822c..d86bf9b 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -291,7 +291,7 @@
     modern: false,
   );
 
-  final XcodeProjectInfo projectInfo = xcodeProjectInterpreter.getInfo(app.project.directory.path);
+  final XcodeProjectInfo projectInfo = xcodeProjectInterpreter.getInfo(app.project.hostAppRoot.path);
   if (!projectInfo.targets.contains('Runner')) {
     printError('The Xcode project does not define target "Runner" which is needed by Flutter tooling.');
     printError('Open Xcode to fix the problem:');
@@ -326,7 +326,7 @@
 
   // Before the build, all service definitions must be updated and the dylibs
   // copied over to a location that is suitable for Xcodebuild to find them.
-  await _addServicesToBundle(app.project.directory);
+  await _addServicesToBundle(app.project.hostAppRoot);
 
   final FlutterProject project = await FlutterProject.current();
   await updateGeneratedXcodeProperties(
@@ -334,8 +334,8 @@
     targetOverride: targetOverride,
     buildInfo: buildInfo,
   );
-
-  if (hasPlugins(project)) {
+  refreshPluginsList(project);
+  if (hasPlugins(project) || (project.isModule && project.ios.podfile.existsSync())) {
     // If the Xcode project, Podfile, or Generated.xcconfig have changed since
     // last run, pods should be updated.
     final Fingerprinter fingerprinter = Fingerprinter(
@@ -381,7 +381,7 @@
     buildCommands.add('-allowProvisioningDeviceRegistration');
   }
 
-  final List<FileSystemEntity> contents = app.project.directory.listSync();
+  final List<FileSystemEntity> contents = app.project.hostAppRoot.listSync();
   for (FileSystemEntity entity in contents) {
     if (fs.path.extension(entity.path) == '.xcworkspace') {
       buildCommands.addAll(<String>[
@@ -446,7 +446,7 @@
   initialBuildStatus = logger.startProgress('Starting Xcode build...');
   final RunResult buildResult = await runAsync(
     buildCommands,
-    workingDirectory: app.project.directory.path,
+    workingDirectory: app.project.hostAppRoot.path,
     allowReentrantFlutter: true
   );
   buildSubStatus?.stop();
@@ -473,7 +473,7 @@
             '-allowProvisioningDeviceRegistration',
           ].contains(buildCommand);
         }).toList(),
-    workingDirectory: app.project.directory.path,
+    workingDirectory: app.project.hostAppRoot.path,
   ));
 
   if (buildResult.exitCode != 0) {
@@ -492,7 +492,7 @@
       stderr: buildResult.stderr,
       xcodeBuildExecution: XcodeBuildExecution(
         buildCommands: buildCommands,
-        appDirectory: app.project.directory.path,
+        appDirectory: app.project.hostAppRoot.path,
         buildForPhysicalDevice: buildForDevice,
         buildSettings: buildSettings,
       ),
@@ -677,7 +677,7 @@
   assert(await xcodeProjectFile.exists());
   final List<String> lines = await xcodeProjectFile.readAsLines();
 
-  if (lines.any((String line) => line.contains('path = Flutter/flutter_assets')))
+  if (lines.any((String line) => line.contains('flutter_assets in Resources')))
     return true;
 
   const String l1 = '		3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };';
diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart
index ea5f798..d0906a4 100644
--- a/packages/flutter_tools/lib/src/plugins.dart
+++ b/packages/flutter_tools/lib/src/plugins.dart
@@ -228,6 +228,7 @@
   s.source_files =  "Classes", "Classes/**/*.{h,m}"
   s.source           = { :path => '.' }
   s.public_header_files = './Classes/**/*.h'
+  s.dependency 'Flutter'
   {{#plugins}}
   s.dependency '{{name}}'
   {{/plugins}}
@@ -296,7 +297,7 @@
   final List<Plugin> plugins = findPlugins(project);
   await _writeAndroidPluginRegistrant(project, plugins);
   await _writeIOSPluginRegistrant(project, plugins);
-  if (!project.isModule && project.ios.directory.existsSync()) {
+  if (!project.isModule && project.ios.hostAppRoot.existsSync()) {
     final CocoaPods cocoaPods = CocoaPods();
     if (plugins.isNotEmpty)
       cocoaPods.setupPodfile(project.ios);
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index ed8258c..9df371a 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -157,37 +157,53 @@
   /// The parent of this project.
   final FlutterProject parent;
 
-  /// The directory of this project.
-  Directory get directory => parent.directory.childDirectory(isModule ? '.ios' : 'ios');
+  Directory get _ephemeralDirectory => parent.directory.childDirectory('.ios');
+  Directory get _editableDirectory => parent.directory.childDirectory('ios');
 
+  /// This parent folder of `Runner.xcodeproj`.
+  Directory get hostAppRoot {
+    if (!isModule || _editableDirectory.existsSync())
+      return _editableDirectory;
+    return _ephemeralDirectory;
+  }
+
+  /// The root directory of the iOS wrapping of Flutter and plugins. This is the
+  /// parent of the `Flutter/` folder into which Flutter artifacts are written
+  /// during build.
+  ///
+  /// This is the same as [hostAppRoot] except when the project is
+  /// a Flutter module with an editable host app.
+  Directory get _flutterLibRoot => isModule ? _ephemeralDirectory : _editableDirectory;
+
+  /// The bundle name of the host app, `Runner.app`.
   String get hostAppBundleName => '$_hostAppBundleName.app';
 
   /// True, if the parent Flutter project is a module.
   bool get isModule => parent.isModule;
 
   /// The xcode config file for [mode].
-  File xcodeConfigFor(String mode) => directory.childDirectory('Flutter').childFile('$mode.xcconfig');
+  File xcodeConfigFor(String mode) => _flutterLibRoot.childDirectory('Flutter').childFile('$mode.xcconfig');
 
   /// The 'Podfile'.
-  File get podfile => directory.childFile('Podfile');
+  File get podfile => hostAppRoot.childFile('Podfile');
 
   /// The 'Podfile.lock'.
-  File get podfileLock => directory.childFile('Podfile.lock');
+  File get podfileLock => hostAppRoot.childFile('Podfile.lock');
 
   /// The 'Manifest.lock'.
-  File get podManifestLock => directory.childDirectory('Pods').childFile('Manifest.lock');
+  File get podManifestLock => hostAppRoot.childDirectory('Pods').childFile('Manifest.lock');
 
   /// The 'Info.plist' file of the host app.
-  File get hostInfoPlist => directory.childDirectory(_hostAppBundleName).childFile('Info.plist');
+  File get hostInfoPlist => hostAppRoot.childDirectory(_hostAppBundleName).childFile('Info.plist');
 
   /// '.xcodeproj' folder of the host app.
-  Directory get xcodeProject => directory.childDirectory('$_hostAppBundleName.xcodeproj');
+  Directory get xcodeProject => hostAppRoot.childDirectory('$_hostAppBundleName.xcodeproj');
 
   /// The '.pbxproj' file of the host app.
   File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj');
 
   /// Xcode workspace directory of the host app.
-  Directory get xcodeWorkspace => directory.childDirectory('$_hostAppBundleName.xcworkspace');
+  Directory get xcodeWorkspace => hostAppRoot.childDirectory('$_hostAppBundleName.xcworkspace');
 
   /// Xcode workspace shared data directory for the host app.
   Directory get xcodeWorkspaceSharedData => xcodeWorkspace.childDirectory('xcshareddata');
@@ -232,8 +248,12 @@
 
   Future<void> ensureReadyForPlatformSpecificTooling() async {
     _regenerateFromTemplateIfNeeded();
-    if (!directory.existsSync())
+    if (!_flutterLibRoot.existsSync())
       return;
+    await _updateGeneratedXcodeConfigIfNeeded();
+  }
+
+  Future<void> _updateGeneratedXcodeConfigIfNeeded() async {
     if (Cache.instance.isOlderThanToolsStamp(generatedXcodePropertiesFile)) {
       await xcode.updateGeneratedXcodeProperties(
         project: parent,
@@ -246,28 +266,40 @@
   void _regenerateFromTemplateIfNeeded() {
     if (!isModule)
       return;
-    final bool pubspecChanged = isOlderThanReference(entity: directory, referenceFile: parent.pubspecFile);
-    final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(directory);
+    final bool pubspecChanged = isOlderThanReference(entity: _ephemeralDirectory, referenceFile: parent.pubspecFile);
+    final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(_ephemeralDirectory);
     if (!pubspecChanged && !toolingChanged)
       return;
-    _deleteIfExistsSync(directory);
-    _overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), directory);
-    _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), directory);
-    if (hasPlugins(parent)) {
-      _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), directory);
+    _deleteIfExistsSync(_ephemeralDirectory);
+    _overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory);
+    // Add ephemeral host app, if a editable host app does not already exist.
+    if (!_editableDirectory.existsSync()) {
+      _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _ephemeralDirectory);
+      if (hasPlugins(parent)) {
+        _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _ephemeralDirectory);
+      }
     }
   }
 
   Future<void> makeHostAppEditable() async {
-    throwToolExit('making host app editable has not yet been implemented for iOS');
+    assert(isModule);
+    if (_editableDirectory.existsSync())
+      throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.');
+    _deleteIfExistsSync(_ephemeralDirectory);
+    _overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory);
+    _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _editableDirectory);
+    _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory);
+    _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_editable_cocoapods'), _editableDirectory);
+    await _updateGeneratedXcodeConfigIfNeeded();
+    await injectPlugins(parent);
   }
 
-  File get generatedXcodePropertiesFile => directory.childDirectory('Flutter').childFile('Generated.xcconfig');
+  File get generatedXcodePropertiesFile => _flutterLibRoot.childDirectory('Flutter').childFile('Generated.xcconfig');
 
   Directory get pluginRegistrantHost {
     return isModule
-        ? directory.childDirectory('Flutter').childDirectory('FlutterPluginRegistrant')
-        : directory.childDirectory(_hostAppBundleName);
+        ? _flutterLibRoot.childDirectory('Flutter').childDirectory('FlutterPluginRegistrant')
+        : hostAppRoot.childDirectory(_hostAppBundleName);
   }
 
   void _overwriteFromTemplate(String path, Directory target) {
diff --git a/packages/flutter_tools/templates/module/ios/host_app_editable_cocoapods/Config.tmpl/Flutter.xcconfig b/packages/flutter_tools/templates/module/ios/host_app_editable_cocoapods/Config.tmpl/Flutter.xcconfig
new file mode 100644
index 0000000..f6b0378
--- /dev/null
+++ b/packages/flutter_tools/templates/module/ios/host_app_editable_cocoapods/Config.tmpl/Flutter.xcconfig
@@ -0,0 +1,2 @@
+#include "../../.ios/Flutter/Generated.xcconfig"
+ENABLE_BITCODE=NO
diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl
index 7b0eeb1..8d4b827 100644
--- a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl
+++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl
@@ -36,15 +36,15 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
-		741F495E21355F27001E2961 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/engine/Flutter.framework; sourceTree = "<group>"; };
-		741F496521356807001E2961 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
+		2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../.ios/Flutter/flutter_assets; sourceTree = SOURCE_ROOT; };
+		741F495E21355F27001E2961 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = ../.ios/Flutter/engine/Flutter.framework; sourceTree = "<group>"; };
+		741F496521356807001E2961 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = ../.ios/Flutter/App.framework; sourceTree = "<group>"; };
 		74974046213559DB008C567A /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
 		74974047213559DB008C567A /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
 		7497404A213559E7008C567A /* Flutter.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = "<group>"; };
 		7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
 		7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
-		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
+		9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = ../.ios/Flutter/Generated.xcconfig; sourceTree = "<group>"; };
 		97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
 		97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl
index f11c13a..3457cfd 100644
--- a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl
+++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl
@@ -2,5 +2,5 @@
 
 target 'Runner' do
   flutter_application_path = '../'
-  eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')))
+  eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)
 end
diff --git a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb
index eec077a..0ac9d7c 100644
--- a/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb
+++ b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb
@@ -20,11 +20,6 @@
     return pods_array
 end
 
-# Prepare symlinks folder. We use symlinks to avoid having Podfile.lock
-# referring to absolute paths on developers' machines.
-system('rm -rf .symlinks')
-system('mkdir -p .symlinks/plugins')
-
 def flutter_root(f)
     generated_xcode_build_settings = parse_KV_file(File.join(f, File.join('.ios', 'Flutter', 'Generated.xcconfig')))
     if generated_xcode_build_settings.empty?
@@ -38,32 +33,26 @@
     }
 end
 
-framework_dir = File.join(File.expand_path(File.dirname(__FILE__)), 'Flutter')
+framework_dir = File.join(flutter_application_path, '.ios', 'Flutter')
+
 engine_dir = File.join(framework_dir, 'engine')
 if !File.exist?(engine_dir)
     # Copy the debug engine to have something to link against if the xcode backend script has not run yet.
     debug_framework_dir = File.join(flutter_root(flutter_application_path), 'bin', 'cache', 'artifacts', 'engine', 'ios')
-    FileUtils.mkdir(engine_dir)
+    FileUtils.mkdir_p(engine_dir)
     FileUtils.cp_r(File.join(debug_framework_dir, 'Flutter.framework'), engine_dir)
     FileUtils.cp(File.join(debug_framework_dir, 'Flutter.podspec'), engine_dir)
 end
 
-symlink = File.join('.symlinks', 'flutter')
+pod 'Flutter', :path => engine_dir
+pod 'FlutterPluginRegistrant', :path => File.join(framework_dir, 'FlutterPluginRegistrant')
 
-File.symlink(framework_dir, symlink)
-pod 'Flutter', :path => File.join(symlink, 'engine')
-
-
+symlinks_dir = File.join(framework_dir, '.symlinks')
+FileUtils.mkdir_p(symlinks_dir)
 plugin_pods = parse_KV_file(File.join(flutter_application_path, '.flutter-plugins'))
-
 plugin_pods.map { |r|
-    symlink = File.join('.symlinks', 'plugins', r[:name])
-
+    symlink = File.join(symlinks_dir, r[:name])
+    FileUtils.rm_f(symlink)
     File.symlink(r[:path], symlink)
     pod r[:name], :path => File.join(symlink, 'ios')
 }
-
-symlink = File.join('.symlinks', 'FlutterApp')
-File.symlink(File.absolute_path(flutter_application_path), symlink)
-
-pod 'FlutterPluginRegistrant', :path => File.join(symlink, '.ios', 'Flutter','FlutterPluginRegistrant')
diff --git a/packages/flutter_tools/test/ios/cocoapods_test.dart b/packages/flutter_tools/test/ios/cocoapods_test.dart
index 435bb96..a69b5ac 100644
--- a/packages/flutter_tools/test/ios/cocoapods_test.dart
+++ b/packages/flutter_tools/test/ios/cocoapods_test.dart
@@ -46,7 +46,7 @@
     mockProcessManager = MockProcessManager();
     mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
     projectUnderTest = await FlutterProject.fromDirectory(fs.directory('project'));
-    projectUnderTest.ios.directory.childDirectory('Runner.xcodeproj').createSync(recursive: true);
+    projectUnderTest.ios.xcodeProject.createSync(recursive: true);
     cocoaPodsUnderTest = CocoaPods();
     pretendPodVersionIs('1.5.0');
     fs.file(fs.path.join(
diff --git a/packages/flutter_tools/test/project_test.dart b/packages/flutter_tools/test/project_test.dart
index b0efc88..63e8057 100644
--- a/packages/flutter_tools/test/project_test.dart
+++ b/packages/flutter_tools/test/project_test.dart
@@ -148,20 +148,20 @@
       testInMemory('does nothing in plugin or package root project', () async {
         final FlutterProject project = await aPluginProject();
         await project.ensureReadyForPlatformSpecificTooling();
-        expectNotExists(project.ios.directory.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'));
+        expectNotExists(project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'));
         expectNotExists(androidPluginRegistrant(project.android.hostAppGradleRoot.childDirectory('app')));
-        expectNotExists(project.ios.directory.childDirectory('Flutter').childFile('Generated.xcconfig'));
+        expectNotExists(project.ios.hostAppRoot.childDirectory('Flutter').childFile('Generated.xcconfig'));
         expectNotExists(project.android.hostAppGradleRoot.childFile('local.properties'));
       });
       testInMemory('injects plugins for iOS', () async {
         final FlutterProject project = await someProject();
         await project.ensureReadyForPlatformSpecificTooling();
-        expectExists(project.ios.directory.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'));
+        expectExists(project.ios.hostAppRoot.childDirectory('Runner').childFile('GeneratedPluginRegistrant.h'));
       });
       testInMemory('generates Xcode configuration for iOS', () async {
         final FlutterProject project = await someProject();
         await project.ensureReadyForPlatformSpecificTooling();
-        expectExists(project.ios.directory.childDirectory('Flutter').childFile('Generated.xcconfig'));
+        expectExists(project.ios.hostAppRoot.childDirectory('Flutter').childFile('Generated.xcconfig'));
       });
       testInMemory('injects plugins for Android', () async {
         final FlutterProject project = await someProject();
@@ -183,7 +183,7 @@
       testInMemory('creates iOS pod in module', () async {
         final FlutterProject project = await aModuleProject();
         await project.ensureReadyForPlatformSpecificTooling();
-        final Directory flutter = project.ios.directory.childDirectory('Flutter');
+        final Directory flutter = project.ios.hostAppRoot.childDirectory('Flutter');
         expectExists(flutter.childFile('podhelper.rb'));
         expectExists(flutter.childFile('Generated.xcconfig'));
         final Directory pluginRegistrantClasses = flutter
@@ -201,7 +201,7 @@
         expect(project.android.isModule, isTrue);
         expect(project.ios.isModule, isTrue);
         expect(project.android.hostAppGradleRoot.basename, '.android');
-        expect(project.ios.directory.basename, '.ios');
+        expect(project.ios.hostAppRoot.basename, '.ios');
       });
       testInMemory('is known for non-module', () async {
         final FlutterProject project = await someProject();
@@ -209,7 +209,7 @@
         expect(project.android.isModule, isFalse);
         expect(project.ios.isModule, isFalse);
         expect(project.android.hostAppGradleRoot.basename, 'android');
-        expect(project.ios.directory.basename, 'ios');
+        expect(project.ios.hostAppRoot.basename, 'ios');
       });
     });