Support for flutter run/build module on iOS (#21216)

diff --git a/packages/flutter_tools/test/application_package_test.dart b/packages/flutter_tools/test/application_package_test.dart
index bba01f3..bdd6792 100644
--- a/packages/flutter_tools/test/application_package_test.dart
+++ b/packages/flutter_tools/test/application_package_test.dart
@@ -41,17 +41,6 @@
     });
   });
 
-  group('BuildableIOSApp', () {
-    testUsingContext('check isSwift', () {
-      final BuildableIOSApp buildableIOSApp = new BuildableIOSApp(
-        projectBundleId: 'blah',
-        appDirectory: 'not/important',
-        buildSettings: _swiftBuildSettings,
-      );
-      expect(buildableIOSApp.isSwift, true);
-    });
-  });
-
   group('PrebuiltIOSApp', () {
     final Map<Type, Generator> overrides = <Type, Generator>{
       FileSystem: () => new MemoryFileSystem(),
@@ -165,19 +154,6 @@
   });
 }
 
-final Map<String, String> _swiftBuildSettings = <String, String>{
-  'ARCHS': 'arm64',
-  'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon',
-  'CLANG_ENABLE_MODULES': 'YES',
-  'ENABLE_BITCODE': 'NO',
-  'INFOPLIST_FILE': 'Runner/Info.plist',
-  'PRODUCT_BUNDLE_IDENTIFIER': 'com.example.test',
-  'PRODUCT_NAME': 'blah',
-  'SWIFT_OBJC_BRIDGING_HEADER': 'Runner/Runner-Bridging-Header.h',
-  'SWIFT_OPTIMIZATION_LEVEL': '-Onone',
-  'SWIFT_VERSION': '3.0',
-};
-
 const String _aaptDataWithExplicitEnabledActivity =
 '''N: android=http://schemas.android.com/apk/res/android
   E: manifest (line=7)
diff --git a/packages/flutter_tools/test/commands/create_test.dart b/packages/flutter_tools/test/commands/create_test.dart
index 18edcce..33530b6 100644
--- a/packages/flutter_tools/test/commands/create_test.dart
+++ b/packages/flutter_tools/test/commands/create_test.dart
@@ -340,7 +340,7 @@
       await _createProject(projectDir, <String>['--no-pub'], <String>[]);
       final FlutterProject project = await FlutterProject.fromDirectory(projectDir);
       expect(
-        await project.ios.productBundleIdentifier(),
+        project.ios.productBundleIdentifier,
         'com.bar.foo.flutterProject',
       );
     }, timeout: allowForCreateFlutterProject);
@@ -367,7 +367,7 @@
       );
       final FlutterProject project = await FlutterProject.fromDirectory(projectDir);
       expect(
-        await project.example.ios.productBundleIdentifier(),
+        project.example.ios.productBundleIdentifier,
         'com.bar.foo.flutterProjectExample',
       );
     }, timeout: allowForCreateFlutterProject);
diff --git a/packages/flutter_tools/test/ios/code_signing_test.dart b/packages/flutter_tools/test/ios/code_signing_test.dart
index dcf4600..38b6b09 100644
--- a/packages/flutter_tools/test/ios/code_signing_test.dart
+++ b/packages/flutter_tools/test/ios/code_signing_test.dart
@@ -4,6 +4,7 @@
 import 'dart:async';
 import 'dart:convert';
 
+import 'package:flutter_tools/src/project.dart';
 import 'package:mockito/mockito.dart';
 import 'package:flutter_tools/src/application_package.dart';
 import 'package:flutter_tools/src/base/common.dart';
@@ -16,39 +17,37 @@
 
 import '../src/common.dart';
 import '../src/context.dart';
+import '../src/mocks.dart';
 
 void main() {
   group('Auto signing', () {
     ProcessManager mockProcessManager;
     Config mockConfig;
+    IosProject mockIosProject;
     BuildableIOSApp app;
     AnsiTerminal testTerminal;
 
     setUp(() {
       mockProcessManager = new MockProcessManager();
       mockConfig = new MockConfig();
+      mockIosProject = new MockIosProject();
+      when(mockIosProject.buildSettings).thenReturn(<String, String>{
+        'For our purposes': 'a non-empty build settings map is valid',
+      });
       testTerminal = new TestTerminal();
-      app = new BuildableIOSApp(
-        projectBundleId: 'test.app',
-        buildSettings: <String, String>{
-          'For our purposes': 'a non-empty build settings map is valid',
-        },
-      );
+      app = new BuildableIOSApp(mockIosProject);
     });
 
     testUsingContext('No auto-sign if Xcode project settings are not available', () async {
-      app = new BuildableIOSApp(projectBundleId: 'test.app');
+      when(mockIosProject.buildSettings).thenReturn(null);
       final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
       expect(signingConfigs, isNull);
     });
 
     testUsingContext('No discovery if development team specified in Xcode project', () async {
-      app = new BuildableIOSApp(
-        projectBundleId: 'test.app',
-        buildSettings: <String, String>{
-          'DEVELOPMENT_TEAM': 'abc',
-        },
-      );
+      when(mockIosProject.buildSettings).thenReturn(<String, String>{
+        'DEVELOPMENT_TEAM': 'abc',
+      });
       final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
       expect(signingConfigs, isNull);
       expect(testLogger.statusText, equals(
diff --git a/packages/flutter_tools/test/ios/devices_test.dart b/packages/flutter_tools/test/ios/devices_test.dart
index 0751177..b2a1291 100644
--- a/packages/flutter_tools/test/ios/devices_test.dart
+++ b/packages/flutter_tools/test/ios/devices_test.dart
@@ -17,6 +17,7 @@
 
 import '../src/common.dart';
 import '../src/context.dart';
+import '../src/mocks.dart';
 
 class MockIMobileDevice extends Mock implements IMobileDevice {}
 class MockProcessManager extends Mock implements ProcessManager {}
@@ -91,9 +92,11 @@
   });
   group('logging', () {
     MockIMobileDevice mockIMobileDevice;
+    MockIosProject mockIosProject;
 
     setUp(() {
       mockIMobileDevice = new MockIMobileDevice();
+      mockIosProject = new MockIosProject();
     });
 
     testUsingContext('suppresses non-Flutter lines from output', () async {
@@ -117,7 +120,7 @@
 
       final IOSDevice device = new IOSDevice('123456');
       final DeviceLogReader logReader = device.getLogReader(
-        app: new BuildableIOSApp(projectBundleId: 'bundleId'),
+        app: new BuildableIOSApp(mockIosProject),
       );
 
       final List<String> lines = await logReader.logLines.toList();
@@ -147,7 +150,7 @@
 
       final IOSDevice device = new IOSDevice('123456');
       final DeviceLogReader logReader = device.getLogReader(
-        app: new BuildableIOSApp(projectBundleId: 'bundleId'),
+        app: new BuildableIOSApp(mockIosProject),
       );
 
       final List<String> lines = await logReader.logLines.toList();
diff --git a/packages/flutter_tools/test/ios/simulators_test.dart b/packages/flutter_tools/test/ios/simulators_test.dart
index e4edf4e..5517e35 100644
--- a/packages/flutter_tools/test/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/ios/simulators_test.dart
@@ -13,6 +13,7 @@
 
 import '../src/common.dart';
 import '../src/context.dart';
+import '../src/mocks.dart';
 
 class MockFile extends Mock implements File {}
 class MockIMobileDevice extends Mock implements IMobileDevice {}
@@ -291,9 +292,11 @@
 
   group('log reader', () {
     MockProcessManager mockProcessManager;
+    MockIosProject mockIosProject;
 
     setUp(() {
       mockProcessManager = new MockProcessManager();
+      mockIosProject = new MockIosProject();
     });
 
     testUsingContext('simulator can output `)`', () async {
@@ -316,7 +319,7 @@
 
       final IOSSimulator device = new IOSSimulator('123456', category: 'iOS 11.0');
       final DeviceLogReader logReader = device.getLogReader(
-        app: new BuildableIOSApp(projectBundleId: 'bundleId'),
+        app: new BuildableIOSApp(mockIosProject),
       );
 
       final List<String> lines = await logReader.logLines.toList();
diff --git a/packages/flutter_tools/test/project_test.dart b/packages/flutter_tools/test/project_test.dart
index bdde5d2..3e3ce86 100644
--- a/packages/flutter_tools/test/project_test.dart
+++ b/packages/flutter_tools/test/project_test.dart
@@ -224,50 +224,50 @@
     group('organization names set', () {
       testInMemory('is empty, if project not created', () async {
         final FlutterProject project = await someProject();
-        expect(await project.organizationNames(), isEmpty);
+        expect(project.organizationNames, isEmpty);
       });
       testInMemory('is empty, if no platform folders exist', () async {
         final FlutterProject project = await someProject();
         project.directory.createSync();
-        expect(await project.organizationNames(), isEmpty);
+        expect(project.organizationNames, isEmpty);
       });
       testInMemory('is populated from iOS bundle identifier', () async {
         final FlutterProject project = await someProject();
         addIosWithBundleId(project.directory, 'io.flutter.someProject');
-        expect(await project.organizationNames(), <String>['io.flutter']);
+        expect(project.organizationNames, <String>['io.flutter']);
       });
       testInMemory('is populated from Android application ID', () async {
         final FlutterProject project = await someProject();
         addAndroidWithApplicationId(project.directory, 'io.flutter.someproject');
-        expect(await project.organizationNames(), <String>['io.flutter']);
+        expect(project.organizationNames, <String>['io.flutter']);
       });
       testInMemory('is populated from iOS bundle identifier in plugin example', () async {
         final FlutterProject project = await someProject();
         addIosWithBundleId(project.example.directory, 'io.flutter.someProject');
-        expect(await project.organizationNames(), <String>['io.flutter']);
+        expect(project.organizationNames, <String>['io.flutter']);
       });
       testInMemory('is populated from Android application ID in plugin example', () async {
         final FlutterProject project = await someProject();
         addAndroidWithApplicationId(project.example.directory, 'io.flutter.someproject');
-        expect(await project.organizationNames(), <String>['io.flutter']);
+        expect(project.organizationNames, <String>['io.flutter']);
       });
       testInMemory('is populated from Android group in plugin', () async {
         final FlutterProject project = await someProject();
         addAndroidWithGroup(project.directory, 'io.flutter.someproject');
-        expect(await project.organizationNames(), <String>['io.flutter']);
+        expect(project.organizationNames, <String>['io.flutter']);
       });
       testInMemory('is singleton, if sources agree', () async {
         final FlutterProject project = await someProject();
         addIosWithBundleId(project.directory, 'io.flutter.someProject');
         addAndroidWithApplicationId(project.directory, 'io.flutter.someproject');
-        expect(await project.organizationNames(), <String>['io.flutter']);
+        expect(project.organizationNames, <String>['io.flutter']);
       });
       testInMemory('is non-singleton, if sources disagree', () async {
         final FlutterProject project = await someProject();
         addIosWithBundleId(project.directory, 'io.flutter.someProject');
         addAndroidWithApplicationId(project.directory, 'io.clutter.someproject');
         expect(
-          await project.organizationNames(),
+          project.organizationNames,
           <String>['io.flutter', 'io.clutter'],
         );
       });
diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart
index a4326e0..2ed3e0a 100644
--- a/packages/flutter_tools/test/src/mocks.dart
+++ b/packages/flutter_tools/test/src/mocks.dart
@@ -16,6 +16,7 @@
 import 'package:flutter_tools/src/device.dart';
 import 'package:flutter_tools/src/ios/devices.dart';
 import 'package:flutter_tools/src/ios/simulators.dart';
+import 'package:flutter_tools/src/project.dart';
 import 'package:flutter_tools/src/runner/flutter_command.dart';
 import 'package:mockito/mockito.dart';
 import 'package:process/process.dart';
@@ -29,10 +30,7 @@
       file: fs.file('/mock/path/to/android/SkyShell.apk'),
       launchActivity: 'io.flutter.android.mock.MockActivity'
     ),
-    iOS: new BuildableIOSApp(
-      appDirectory: '/mock/path/to/iOS/SkyShell.app',
-      projectBundleId: 'io.flutter.ios.mock'
-    )
+    iOS: new BuildableIOSApp(new MockIosProject())
   );
 }
 
@@ -335,6 +333,14 @@
   Stream<Device> get onRemoved => _onRemovedController.stream;
 }
 
+class MockIosProject extends Mock implements IosProject {
+  @override
+  String get productBundleIdentifier => 'com.example.test';
+
+  @override
+  String get hostAppBundleName => 'Runner.app';
+}
+
 class MockAndroidDevice extends Mock implements AndroidDevice {
   @override
   Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;