From a600fe7f1304cf9f8b1bfbe7b72e40437cb56680 Mon Sep 17 00:00:00 2001 From: Mikkel Nygaard Ravn Date: Tue, 25 Sep 2018 21:21:13 +0200 Subject: [PATCH] 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 --- dev/devicelab/bin/tasks/module_test.dart | 40 +- dev/devicelab/bin/tasks/module_test_ios.dart | 137 ++++++ dev/devicelab/manifest.yaml | 7 + .../ios_host_app/Config/Debug.xcconfig | 2 + .../ios_host_app/Config/Flutter.xcconfig | 2 + .../ios_host_app/Config/Release.xcconfig | 3 + .../Host.xcodeproj/project.pbxproj | 449 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../Host.xcworkspace/contents.xcworkspacedata | 10 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../UserInterfaceState.xcuserstate | Bin 0 -> 26773 bytes .../ios_host_app/Host/AppDelegate.h | 5 + .../ios_host_app/Host/AppDelegate.m | 4 + .../AppIcon.appiconset/Contents.json | 98 ++++ .../Host/Assets.xcassets/Contents.json | 6 + .../Host/Base.lproj/LaunchScreen.storyboard | 25 + .../Host/Base.lproj/Main.storyboard | 24 + .../ios_host_app/Host/Info.plist | 45 ++ .../ios_host_app/Host/ViewController.h | 5 + .../ios_host_app/Host/ViewController.m | 23 + .../ios_host_app/Host/main.m | 8 + dev/integration_tests/ios_host_app/Podfile | 6 + packages/flutter_tools/bin/xcode_backend.sh | 3 + .../flutter_tools/lib/src/ios/cocoapods.dart | 4 +- packages/flutter_tools/lib/src/ios/mac.dart | 18 +- packages/flutter_tools/lib/src/plugins.dart | 3 +- packages/flutter_tools/lib/src/project.dart | 74 ++- .../Config.tmpl/Flutter.xcconfig | 2 + .../project.pbxproj.tmpl | 8 +- .../Podfile.copy.tmpl | 2 +- .../ios/library/Flutter.tmpl/podhelper.rb | 29 +- .../test/ios/cocoapods_test.dart | 2 +- packages/flutter_tools/test/project_test.dart | 14 +- 34 files changed, 994 insertions(+), 87 deletions(-) create mode 100644 dev/devicelab/bin/tasks/module_test_ios.dart create mode 100644 dev/integration_tests/ios_host_app/Config/Debug.xcconfig create mode 100644 dev/integration_tests/ios_host_app/Config/Flutter.xcconfig create mode 100644 dev/integration_tests/ios_host_app/Config/Release.xcconfig create mode 100644 dev/integration_tests/ios_host_app/Host.xcodeproj/project.pbxproj create mode 100644 dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 dev/integration_tests/ios_host_app/Host.xcworkspace/contents.xcworkspacedata create mode 100644 dev/integration_tests/ios_host_app/Host.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 dev/integration_tests/ios_host_app/Host.xcworkspace/xcuserdata/mravn.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 dev/integration_tests/ios_host_app/Host/AppDelegate.h create mode 100644 dev/integration_tests/ios_host_app/Host/AppDelegate.m create mode 100644 dev/integration_tests/ios_host_app/Host/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 dev/integration_tests/ios_host_app/Host/Assets.xcassets/Contents.json create mode 100644 dev/integration_tests/ios_host_app/Host/Base.lproj/LaunchScreen.storyboard create mode 100644 dev/integration_tests/ios_host_app/Host/Base.lproj/Main.storyboard create mode 100644 dev/integration_tests/ios_host_app/Host/Info.plist create mode 100644 dev/integration_tests/ios_host_app/Host/ViewController.h create mode 100644 dev/integration_tests/ios_host_app/Host/ViewController.m create mode 100644 dev/integration_tests/ios_host_app/Host/main.m create mode 100644 dev/integration_tests/ios_host_app/Podfile create mode 100644 packages/flutter_tools/templates/module/ios/host_app_editable_cocoapods/Config.tmpl/Flutter.xcconfig diff --git a/dev/devicelab/bin/tasks/module_test.dart b/dev/devicelab/bin/tasks/module_test.dart index 0014939f6c..45e48b2e55 100644 --- a/dev/devicelab/bin/tasks/module_test.dart +++ b/dev/devicelab/bin/tasks/module_test.dart @@ -24,6 +24,7 @@ Future main() 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( @@ -34,14 +35,14 @@ Future main() async { 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: ['get'], @@ -50,7 +51,7 @@ Future main() async { 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', ['flutter:assembleDebug'], @@ -59,8 +60,7 @@ Future main() async { }); final bool aarBuilt = exists(File(path.join( - tempDir.path, - 'hello', + projectDir.path, '.android', 'Flutter', 'build', @@ -75,7 +75,7 @@ Future main() async { section('Build ephemeral host app'); - await inDirectory(Directory(path.join(tempDir.path, 'hello')), () async { + await inDirectory(projectDir, () async { await flutter( 'build', options: ['apk'], @@ -83,8 +83,7 @@ Future main() async { }); final bool ephemeralHostApkBuilt = exists(File(path.join( - tempDir.path, - 'hello', + projectDir.path, 'build', 'host', 'outputs', @@ -99,31 +98,30 @@ Future main() async { 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: ['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: ['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 @@ Future main() async { '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 @@ Future main() async { 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 0000000000..b69db061ae --- /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 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: ['--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: ['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: ['get'], + ); + }); + + section('Build ephemeral host app with CocoaPods'); + + await inDirectory(projectDir, () async { + await flutter( + 'build', + options: ['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: ['ios'], + ); + }); + + section('Build editable host app'); + + await inDirectory(projectDir, () async { + await flutter( + 'build', + options: ['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 793421dde1..0078092fd6 100644 --- a/dev/devicelab/manifest.yaml +++ b/dev/devicelab/manifest.yaml @@ -297,6 +297,13 @@ tasks: 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 0000000000..55d2bd85f8 --- /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 0000000000..ccfdd63404 --- /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 0000000000..a0a0197543 --- /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 0000000000..c6086b7cc2 --- /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 = ""; }; + 74DB4A4D2152F3F900E9B550 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 74DB4A4F2152F3F900E9B550 /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 74DB4A502152F3F900E9B550 /* ViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = ""; }; + 74DB4A532152F3F900E9B550 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 74DB4A552152F3FB00E9B550 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 74DB4A582152F3FB00E9B550 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 74DB4A5A2152F3FB00E9B550 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 74DB4A5B2152F3FB00E9B550 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 74DB4A7F2152F49200E9B550 /* Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 74DB4A802152F4A400E9B550 /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; + 74DB4A8221541FEE00E9B550 /* Flutter.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = ""; }; + 74DB4A842154201200E9B550 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = ../../hello/.ios/Flutter/App.framework; sourceTree = ""; }; + 74DB4A862154203700E9B550 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = ../../hello/.ios/Flutter/flutter_assets; sourceTree = ""; }; + 74DB4A882154204700E9B550 /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = ../../hello/.ios/Flutter/engine/Flutter.framework; sourceTree = ""; }; + 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 = ""; + }; + 74DB4A4A2152F3F900E9B550 /* Products */ = { + isa = PBXGroup; + children = ( + 74DB4A492152F3F900E9B550 /* Host.app */, + ); + name = Products; + sourceTree = ""; + }; + 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 = ""; + }; + 74DB4A7E2152F47500E9B550 /* Config */ = { + isa = PBXGroup; + children = ( + 74DB4A8221541FEE00E9B550 /* Flutter.xcconfig */, + 74DB4A7F2152F49200E9B550 /* Debug.xcconfig */, + 74DB4A802152F4A400E9B550 /* Release.xcconfig */, + ); + path = Config; + sourceTree = ""; + }; + 74DB4A8121541FDF00E9B550 /* Flutter */ = { + isa = PBXGroup; + children = ( + 74DB4A882154204700E9B550 /* Flutter.framework */, + 74DB4A862154203700E9B550 /* flutter_assets */, + 74DB4A842154201200E9B550 /* App.framework */, + ); + path = Flutter; + sourceTree = ""; + }; + 76443A8345AFB0A4BBAA0AC0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + A7845ED770D25CF67B243D1A /* libPods-Host.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + D22B5E2B5577AC172019DDE2 /* Pods */ = { + isa = PBXGroup; + children = ( + ); + name = Pods; + sourceTree = ""; + }; +/* 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 = ""; + }; + 74DB4A572152F3FB00E9B550 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 74DB4A582152F3FB00E9B550 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* 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 0000000000..8ee5ee9ff6 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + 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 0000000000..18d981003d --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + 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 0000000000..8e65b678a3 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + 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 0000000000..18d981003d --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + 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 0000000000000000000000000000000000000000..40eb4bcb278d93199c9eb48c97cf44fab874d88d GIT binary patch literal 26773 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 YS{2;yWMBgAjAme9U|SC1!^miL07~CKM*si- literal 0 HcmV?d00001 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 0000000000..d944abe46e --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host/AppDelegate.h @@ -0,0 +1,5 @@ +#import +#import + +@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 0000000000..9432fc57e0 --- /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 0000000000..d8db8d65fd --- /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 0000000000..da4a164c91 --- /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 0000000000..f83f6fd581 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000..d7c78a1255 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + 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 0000000000..16be3b6811 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host/Info.plist @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + 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 0000000000..143825c2d5 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host/ViewController.h @@ -0,0 +1,5 @@ +#import + +@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 0000000000..b8db22bc3e --- /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 0000000000..81e84cbb78 --- /dev/null +++ b/dev/integration_tests/ios_host_app/Host/main.m @@ -0,0 +1,8 @@ +#import +#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 0000000000..bac985d148 --- /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 100b158ffc..655040ab27 100755 --- a/packages/flutter_tools/bin/xcode_backend.sh +++ b/packages/flutter_tools/bin/xcode_backend.sh @@ -80,6 +80,9 @@ BuildApp() { 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 96f7d324f4..184a24c62b 100644 --- a/packages/flutter_tools/lib/src/ios/cocoapods.dart +++ b/packages/flutter_tools/lib/src/ios/cocoapods.dart @@ -156,7 +156,7 @@ class CocoaPods { // 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 @@ class CocoaPods { final Status status = logger.startProgress('Running pod install...', expectSlowOperation: true); final ProcessResult result = await processManager.run( ['pod', 'install', '--verbose'], - workingDirectory: iosProject.directory.path, + workingDirectory: iosProject.hostAppRoot.path, environment: { // 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 53c822cdb8..d86bf9ba2b 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart @@ -291,7 +291,7 @@ Future buildXcodeProject({ 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 @@ Future buildXcodeProject({ // 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 @@ Future buildXcodeProject({ 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 @@ Future buildXcodeProject({ buildCommands.add('-allowProvisioningDeviceRegistration'); } - final List contents = app.project.directory.listSync(); + final List contents = app.project.hostAppRoot.listSync(); for (FileSystemEntity entity in contents) { if (fs.path.extension(entity.path) == '.xcworkspace') { buildCommands.addAll([ @@ -446,7 +446,7 @@ Future buildXcodeProject({ 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 @@ Future buildXcodeProject({ '-allowProvisioningDeviceRegistration', ].contains(buildCommand); }).toList(), - workingDirectory: app.project.directory.path, + workingDirectory: app.project.hostAppRoot.path, )); if (buildResult.exitCode != 0) { @@ -492,7 +492,7 @@ Future buildXcodeProject({ 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 @@ Future upgradePbxProjWithFlutterAssets(IosProject project) async { assert(await xcodeProjectFile.exists()); final List 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 ea5f79829e..d0906a4ad0 100644 --- a/packages/flutter_tools/lib/src/plugins.dart +++ b/packages/flutter_tools/lib/src/plugins.dart @@ -228,6 +228,7 @@ Depends on all your plugins, and provides a function to register them. 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 @@ Future injectPlugins(FlutterProject project) async { final List 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 ed8258cd6f..9df371afdd 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -157,37 +157,53 @@ class IosProject { /// 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 @@ class IosProject { Future ensureReadyForPlatformSpecificTooling() async { _regenerateFromTemplateIfNeeded(); - if (!directory.existsSync()) + if (!_flutterLibRoot.existsSync()) return; + await _updateGeneratedXcodeConfigIfNeeded(); + } + + Future _updateGeneratedXcodeConfigIfNeeded() async { if (Cache.instance.isOlderThanToolsStamp(generatedXcodePropertiesFile)) { await xcode.updateGeneratedXcodeProperties( project: parent, @@ -246,28 +266,40 @@ class IosProject { 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 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 0000000000..f6b0378f5b --- /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 7b0eeb1f88..8d4b827eba 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 = ""; }; - 741F496521356807001E2961 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 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 = ""; }; + 741F496521356807001E2961 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = ../.ios/Flutter/App.framework; sourceTree = ""; }; 74974046213559DB008C567A /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; 74974047213559DB008C567A /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; 7497404A213559E7008C567A /* Flutter.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = ""; }; 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; - 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = ../.ios/Flutter/Generated.xcconfig; sourceTree = ""; }; 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 = ""; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 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 f11c13a1ad..3457cfd81b 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 @@ platform :ios, '8.0' 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 eec077a7a4..0ac9d7c9fc 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 @@ def parse_KV_file(file, separator='=') 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 @@ def flutter_root(f) } 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') - -File.symlink(framework_dir, symlink) -pod 'Flutter', :path => File.join(symlink, 'engine') - +pod 'Flutter', :path => engine_dir +pod 'FlutterPluginRegistrant', :path => File.join(framework_dir, 'FlutterPluginRegistrant') +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 435bb963ad..a69b5ac6f2 100644 --- a/packages/flutter_tools/test/ios/cocoapods_test.dart +++ b/packages/flutter_tools/test/ios/cocoapods_test.dart @@ -46,7 +46,7 @@ void main() { 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 b0efc88c5d..63e80571da 100644 --- a/packages/flutter_tools/test/project_test.dart +++ b/packages/flutter_tools/test/project_test.dart @@ -148,20 +148,20 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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 @@ void main() { 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'); }); });