diff --git a/packages/flutter_tools/lib/src/android/apk.dart b/packages/flutter_tools/lib/src/android/apk.dart index 57e7f2952b..43b73c1567 100644 --- a/packages/flutter_tools/lib/src/android/apk.dart +++ b/packages/flutter_tools/lib/src/android/apk.dart @@ -19,7 +19,7 @@ Future buildApk({ @required String target, BuildInfo buildInfo = BuildInfo.debug }) async { - if (!project.android.isUsingGradle()) { + if (!project.android.isUsingGradle) { throwToolExit( 'The build process for Android has changed, and the current project configuration\n' 'is no longer valid. Please consult\n\n' diff --git a/packages/flutter_tools/lib/src/application_package.dart b/packages/flutter_tools/lib/src/application_package.dart index cbe569bb9b..58829a2128 100644 --- a/packages/flutter_tools/lib/src/application_package.dart +++ b/packages/flutter_tools/lib/src/application_package.dart @@ -17,7 +17,6 @@ import 'build_info.dart'; import 'globals.dart'; import 'ios/ios_workflow.dart'; import 'ios/plist_utils.dart' as plist; -import 'ios/xcodeproj.dart'; import 'project.dart'; import 'tester/flutter_tester.dart'; @@ -93,7 +92,7 @@ class AndroidApk extends ApplicationPackage { static Future fromAndroidProject(AndroidProject androidProject) async { File apkFile; - if (androidProject.isUsingGradle()) { + if (androidProject.isUsingGradle) { apkFile = await getGradleAppOut(androidProject); if (apkFile.existsSync()) { // Grab information from the .apk. The gradle build script might alter @@ -217,26 +216,10 @@ abstract class IOSApp extends ApplicationPackage { ); } - factory IOSApp.fromCurrentDirectory() { + factory IOSApp.fromIosProject(IosProject project) { if (getCurrentHostPlatform() != HostPlatform.darwin_x64) return null; - - final String plistPath = fs.path.join('ios', 'Runner', 'Info.plist'); - String id = iosWorkflow.getPlistValueFromFile( - plistPath, - plist.kCFBundleIdentifierKey, - ); - if (id == null || !xcodeProjectInterpreter.isInstalled) - return null; - final String projectPath = fs.path.join('ios', 'Runner.xcodeproj'); - final Map buildSettings = xcodeProjectInterpreter.getBuildSettings(projectPath, 'Runner'); - id = substituteXcodeVariables(id, buildSettings); - - return new BuildableIOSApp( - appDirectory: 'ios', - projectBundleId: id, - buildSettings: buildSettings, - ); + return new BuildableIOSApp(project); } @override @@ -248,25 +231,12 @@ abstract class IOSApp extends ApplicationPackage { } class BuildableIOSApp extends IOSApp { - static const String kBundleName = 'Runner.app'; + BuildableIOSApp(this.project) : super(projectBundleId: project.productBundleIdentifier); - BuildableIOSApp({ - this.appDirectory, - String projectBundleId, - this.buildSettings, - }) : super(projectBundleId: projectBundleId); - - final String appDirectory; - - /// Build settings of the app's Xcode project. - /// - /// These are the build settings as specified in the Xcode project files. - /// - /// Build settings may change depending on the parameters passed while building. - final Map buildSettings; + final IosProject project; @override - String get name => kBundleName; + String get name => project.hostAppBundleName; @override String get simulatorBundlePath => _buildAppPath('iphonesimulator'); @@ -274,11 +244,8 @@ class BuildableIOSApp extends IOSApp { @override String get deviceBundlePath => _buildAppPath('iphoneos'); - /// True if the app is built from a Swift project. Null if unknown. - bool get isSwift => buildSettings?.containsKey('SWIFT_VERSION'); - String _buildAppPath(String type) { - return fs.path.join(getIosBuildDirectory(), type, kBundleName); + return fs.path.join(getIosBuildDirectory(), type, name); } } @@ -317,7 +284,7 @@ Future getApplicationPackageForPlatform( : new AndroidApk.fromApk(applicationBinary); case TargetPlatform.ios: return applicationBinary == null - ? new IOSApp.fromCurrentDirectory() + ? new IOSApp.fromIosProject((await FlutterProject.current()).ios) : new IOSApp.fromPrebuiltApp(applicationBinary); case TargetPlatform.tester: return new FlutterTesterApp.fromCurrentDirectory(); @@ -346,7 +313,7 @@ class ApplicationPackageStore { android ??= await AndroidApk.fromAndroidProject((await FlutterProject.current()).android); return android; case TargetPlatform.ios: - iOS ??= new IOSApp.fromCurrentDirectory(); + iOS ??= IOSApp.fromIosProject((await FlutterProject.current()).ios); return iOS; case TargetPlatform.darwin_x64: case TargetPlatform.linux_x64: diff --git a/packages/flutter_tools/lib/src/base/file_system.dart b/packages/flutter_tools/lib/src/base/file_system.dart index 225dcb628e..b15e0412a6 100644 --- a/packages/flutter_tools/lib/src/base/file_system.dart +++ b/packages/flutter_tools/lib/src/base/file_system.dart @@ -6,6 +6,7 @@ import 'package:file/file.dart'; import 'package:file/local.dart'; import 'package:file/memory.dart'; import 'package:file/record_replay.dart'; +import 'package:meta/meta.dart'; import 'common.dart' show throwToolExit; import 'context.dart'; @@ -145,3 +146,16 @@ String canonicalizePath(String path) => fs.path.normalize(fs.path.absolute(path) /// On Windows it replaces all '\' with '\\'. On other platforms, it returns the /// path unchanged. String escapePath(String path) => platform.isWindows ? path.replaceAll('\\', '\\\\') : path; + +/// Returns true if the file system [entity] has not been modified since the +/// latest modification to [referenceFile]. +/// +/// Returns true, if [entity] does not exist. +/// +/// Returns false, if [entity] exists, but [referenceFile] does not. +bool isOlderThanReference({@required FileSystemEntity entity, @required File referenceFile}) { + if (!entity.existsSync()) + return true; + return referenceFile.existsSync() + && referenceFile.lastModifiedSync().isAfter(entity.statSync().modified); +} diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index 5b2ddbba9f..ffc4af5d07 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart @@ -169,17 +169,11 @@ class Cache { return fs.file(fs.path.join(getRoot().path, '$artifactName.stamp')); } - /// Returns `true` if either [file] is older than the tools stamp or if - /// [file] doesn't exist. - bool fileOlderThanToolsStamp(File file) { - if (!file.existsSync()) { - return true; - } + /// Returns `true` if either [entity] is older than the tools stamp or if + /// [entity] doesn't exist. + bool isOlderThanToolsStamp(FileSystemEntity entity) { final File flutterToolsStamp = getStampFileFor('flutter_tools'); - return flutterToolsStamp.existsSync() && - flutterToolsStamp - .lastModifiedSync() - .isAfter(file.lastModifiedSync()); + return isOlderThanReference(entity: entity, referenceFile: flutterToolsStamp); } bool isUpToDate() => _artifacts.every((CachedArtifact artifact) => artifact.isUpToDate()); diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index 8c25be61f4..4f9f440078 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -133,7 +133,7 @@ class CreateCommand extends FlutterCommand { String organization = argResults['org']; if (!argResults.wasParsed('org')) { final FlutterProject project = await FlutterProject.fromDirectory(projectDir); - final Set existingOrganizations = await project.organizationNames(); + final Set existingOrganizations = project.organizationNames; if (existingOrganizations.length == 1) { organization = existingOrganizations.first; } else if (1 < existingOrganizations.length) { diff --git a/packages/flutter_tools/lib/src/commands/inject_plugins.dart b/packages/flutter_tools/lib/src/commands/inject_plugins.dart index 21aa60a0a5..8c2ba4d804 100644 --- a/packages/flutter_tools/lib/src/commands/inject_plugins.dart +++ b/packages/flutter_tools/lib/src/commands/inject_plugins.dart @@ -26,6 +26,7 @@ class InjectPluginsCommand extends FlutterCommand { @override Future runCommand() async { final FlutterProject project = await FlutterProject.current(); + refreshPluginsList(project); await injectPlugins(project); final bool result = hasPlugins(project); if (result) { diff --git a/packages/flutter_tools/lib/src/flutter_manifest.dart b/packages/flutter_tools/lib/src/flutter_manifest.dart index 81a1e646d6..60b3fb3296 100644 --- a/packages/flutter_tools/lib/src/flutter_manifest.dart +++ b/packages/flutter_tools/lib/src/flutter_manifest.dart @@ -140,6 +140,14 @@ class FlutterManifest { return null; } + /// Returns the iOS bundle identifier declared by this manifest in its + /// module descriptor. Returns null, if there is no such declaration. + String get iosBundleIdentifier { + if (isModule) + return _flutterDescriptor['module']['iosBundleIdentifier']; + return null; + } + List> get fontsDescriptor { final List fontList = _flutterDescriptor['fonts']; return fontList == null diff --git a/packages/flutter_tools/lib/src/ios/code_signing.dart b/packages/flutter_tools/lib/src/ios/code_signing.dart index 6797a6e777..a337a75529 100644 --- a/packages/flutter_tools/lib/src/ios/code_signing.dart +++ b/packages/flutter_tools/lib/src/ios/code_signing.dart @@ -96,20 +96,21 @@ Future> getCodeSigningIdentityDevelopmentTeam({ BuildableIOSApp iosApp, bool usesTerminalUi = true }) async{ - if (iosApp.buildSettings == null) + final Map buildSettings = iosApp.project.buildSettings; + if (buildSettings == null) return null; // If the user already has it set in the project build settings itself, // continue with that. - if (isNotEmpty(iosApp.buildSettings['DEVELOPMENT_TEAM'])) { + if (isNotEmpty(buildSettings['DEVELOPMENT_TEAM'])) { printStatus( 'Automatically signing iOS for device deployment using specified development ' - 'team in Xcode project: ${iosApp.buildSettings['DEVELOPMENT_TEAM']}' + 'team in Xcode project: ${buildSettings['DEVELOPMENT_TEAM']}' ); return null; } - if (isNotEmpty(iosApp.buildSettings['PROVISIONING_PROFILE'])) + if (isNotEmpty(buildSettings['PROVISIONING_PROFILE'])) return null; // If the user's environment is missing the tools needed to find and read diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart index f9a1e7aba2..04549491ca 100644 --- a/packages/flutter_tools/lib/src/ios/mac.dart +++ b/packages/flutter_tools/lib/src/ios/mac.dart @@ -189,13 +189,13 @@ Future buildXcodeProject({ bool codesign = true, bool usesTerminalUi = true, }) async { - if (!await upgradePbxProjWithFlutterAssets(app.name, app.appDirectory)) + if (!await upgradePbxProjWithFlutterAssets(app.project)) return new XcodeBuildResult(success: false); if (!_checkXcodeVersion()) return new XcodeBuildResult(success: false); - final XcodeProjectInfo projectInfo = xcodeProjectInterpreter.getInfo(app.appDirectory); + final XcodeProjectInfo projectInfo = xcodeProjectInterpreter.getInfo(app.project.directory.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:'); @@ -230,8 +230,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. - final Directory appDirectory = fs.directory(app.appDirectory); - await _addServicesToBundle(appDirectory); + await _addServicesToBundle(app.project.directory); final FlutterProject project = await FlutterProject.current(); await updateGeneratedXcodeProperties( @@ -242,22 +241,21 @@ Future buildXcodeProject({ ); if (hasPlugins(project)) { - final String iosPath = fs.path.join(fs.currentDirectory.path, app.appDirectory); // If the Xcode project, Podfile, or Generated.xcconfig have changed since // last run, pods should be updated. final Fingerprinter fingerprinter = new Fingerprinter( fingerprintPath: fs.path.join(getIosBuildDirectory(), 'pod_inputs.fingerprint'), paths: [ - _getPbxProjPath(app.appDirectory), - fs.path.join(iosPath, 'Podfile'), - fs.path.join(iosPath, 'Flutter', 'Generated.xcconfig'), + app.project.xcodeProjectInfoFile.path, + app.project.podfile.path, + app.project.generatedXcodePropertiesFile.path, ], properties: {}, ); final bool didPodInstall = await cocoaPods.processPods( iosProject: project.ios, iosEngineDir: flutterFrameworkDir(buildInfo.mode), - isSwift: app.isSwift, + isSwift: project.ios.isSwift, dependenciesChanged: !await fingerprinter.doesFingerprintMatch() ); if (didPodInstall) @@ -288,7 +286,7 @@ Future buildXcodeProject({ buildCommands.add('-allowProvisioningDeviceRegistration'); } - final List contents = fs.directory(app.appDirectory).listSync(); + final List contents = app.project.directory.listSync(); for (FileSystemEntity entity in contents) { if (fs.path.extension(entity.path) == '.xcworkspace') { buildCommands.addAll([ @@ -353,7 +351,7 @@ Future buildXcodeProject({ initialBuildStatus = logger.startProgress('Starting Xcode build...'); final RunResult buildResult = await runAsync( buildCommands, - workingDirectory: app.appDirectory, + workingDirectory: app.project.directory.path, allowReentrantFlutter: true ); buildSubStatus?.stop(); @@ -381,7 +379,7 @@ Future buildXcodeProject({ '-allowProvisioningDeviceRegistration', ].contains(buildCommand); }).toList(), - workingDirectory: app.appDirectory, + workingDirectory: app.project.directory.path, )); if (buildResult.exitCode != 0) { @@ -400,7 +398,7 @@ Future buildXcodeProject({ stderr: buildResult.stderr, xcodeBuildExecution: new XcodeBuildExecution( buildCommands: buildCommands, - appDirectory: app.appDirectory, + appDirectory: app.project.directory.path, buildForPhysicalDevice: buildForDevice, buildSettings: buildSettings, ), @@ -568,9 +566,6 @@ Future _copyServiceFrameworks(List> services, Director } } -/// The path of the Xcode project file. -String _getPbxProjPath(String appPath) => fs.path.join(fs.currentDirectory.path, appPath, 'Runner.xcodeproj', 'project.pbxproj'); - void _copyServiceDefinitionsManifest(List> services, File manifest) { printTrace("Creating service definitions manifest at '${manifest.path}'"); final List> jsonServices = services.map((Map service) => { @@ -583,8 +578,8 @@ void _copyServiceDefinitionsManifest(List> services, File ma manifest.writeAsStringSync(json.encode(jsonObject), mode: FileMode.write, flush: true); } -Future upgradePbxProjWithFlutterAssets(String app, String appPath) async { - final File xcodeProjectFile = fs.file(_getPbxProjPath(appPath)); +Future upgradePbxProjWithFlutterAssets(IosProject project) async { + final File xcodeProjectFile = project.xcodeProjectInfoFile; assert(await xcodeProjectFile.exists()); final List lines = await xcodeProjectFile.readAsLines(); @@ -601,7 +596,7 @@ Future upgradePbxProjWithFlutterAssets(String app, String appPath) async { const String l8 = ' 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */,'; - printStatus("Upgrading project.pbxproj of $app' to include the " + printStatus("Upgrading project.pbxproj of ${project.hostAppBundleName}' to include the " "'flutter_assets' directory"); if (!lines.contains(l1) || !lines.contains(l3) || diff --git a/packages/flutter_tools/lib/src/plugins.dart b/packages/flutter_tools/lib/src/plugins.dart index 9a6afe80e0..288d87a35c 100644 --- a/packages/flutter_tools/lib/src/plugins.dart +++ b/packages/flutter_tools/lib/src/plugins.dart @@ -241,8 +241,7 @@ Future _writeIOSPluginRegistrant(FlutterProject project, List plug 'name': p.name, 'prefix': p.iosPrefix, 'class': p.pluginClass, - }). - toList(); + }).toList(); final Map context = { 'plugins': iosPlugins, }; @@ -279,23 +278,34 @@ Future _writeIOSPluginRegistrant(FlutterProject project, List plug } } -/// Injects plugins found in `pubspec.yaml` into the platform-specific projects. -Future injectPlugins(FlutterProject project) async { +/// Rewrites the `.flutter-plugins` file of [project] based on the plugin +/// dependencies declared in `pubspec.yaml`. +/// +/// Assumes `pub get` has been executed since last change to `pubspec.yaml`. +void refreshPluginsList(FlutterProject project) { final List plugins = findPlugins(project); final bool changed = _writeFlutterPluginsList(project, plugins); + if (changed) + cocoaPods.invalidatePodInstallOutput(project.ios); +} + +/// Injects plugins found in `pubspec.yaml` into the platform-specific projects. +/// +/// Assumes [refreshPluginsList] has been called since last change to `pubspec.yaml`. +Future injectPlugins(FlutterProject project) async { + final List plugins = findPlugins(project); await _writeAndroidPluginRegistrant(project, plugins); await _writeIOSPluginRegistrant(project, plugins); - - if (project.ios.directory.existsSync()) { + if (!project.isModule && project.ios.directory.existsSync()) { final CocoaPods cocoaPods = new CocoaPods(); if (plugins.isNotEmpty) cocoaPods.setupPodfile(project.ios); - if (changed) - cocoaPods.invalidatePodInstallOutput(project.ios); } } /// Returns whether the specified Flutter [project] has any plugin dependencies. +/// +/// Assumes [refreshPluginsList] has been called since last change to `pubspec.yaml`. bool hasPlugins(FlutterProject project) { return _readFlutterPluginsList(project) != null; } diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart index d54d8e952f..b1fc04de34 100644 --- a/packages/flutter_tools/lib/src/project.dart +++ b/packages/flutter_tools/lib/src/project.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:convert'; import 'package:meta/meta.dart'; @@ -64,17 +63,17 @@ class FlutterProject { /// The manifest of the example sub-project of this project. final FlutterManifest _exampleManifest; - /// Asynchronously returns the organization names found in this project as + /// The set of organization names found in this project as /// part of iOS product bundle identifier, Android application ID, or /// Gradle group ID. - Future> organizationNames() async { - final List candidates = await Future.wait(>[ - ios.productBundleIdentifier(), - android.applicationId(), - android.group(), - example.android.applicationId(), - example.ios.productBundleIdentifier(), - ]); + Set get organizationNames { + final List candidates = [ + ios.productBundleIdentifier, + android.applicationId, + android.group, + example.android.applicationId, + example.ios.productBundleIdentifier, + ]; return new Set.from(candidates .map(_organizationNameFromPackageName) .where((String name) => name != null)); @@ -93,6 +92,13 @@ class FlutterProject { /// The Android sub project of this project. AndroidProject get android => new AndroidProject._(this); + /// The `pubspec.yaml` file of this project. + File get pubspecFile => directory.childFile('pubspec.yaml'); + + /// The `.packages` file of this project. + File get packagesFile => directory.childFile('.packages'); + + /// The `.flutter-plugins` file of this project. File get flutterPluginsFile => directory.childFile('.flutter-plugins'); /// The example sub-project of this project. @@ -128,6 +134,7 @@ class FlutterProject { Future ensureReadyForPlatformSpecificTooling() async { if (!directory.existsSync() || hasExampleApp) return; + refreshPluginsList(this); await android.ensureReadyForPlatformSpecificTooling(); await ios.ensureReadyForPlatformSpecificTooling(); await injectPlugins(this); @@ -140,6 +147,7 @@ class FlutterProject { /// Flutter applications and the `.ios/` sub-folder of Flutter modules. class IosProject { static final RegExp _productBundleIdPattern = new RegExp(r'^\s*PRODUCT_BUNDLE_IDENTIFIER\s*=\s*(.*);\s*$'); + static const String _hostAppBundleName = 'Runner'; IosProject._(this.parent); @@ -149,6 +157,8 @@ class IosProject { /// The directory of this project. Directory get directory => parent.directory.childDirectory(isModule ? '.ios' : 'ios'); + String get hostAppBundleName => '$_hostAppBundleName.app'; + /// True, if the parent Flutter project is a module. bool get isModule => parent.isModule; @@ -164,19 +174,30 @@ class IosProject { /// The 'Manifest.lock'. File get podManifestLock => directory.childDirectory('Pods').childFile('Manifest.lock'); - Future productBundleIdentifier() { - final File projectFile = directory.childDirectory('Runner.xcodeproj').childFile('project.pbxproj'); - return _firstMatchInFile(projectFile, _productBundleIdPattern).then((Match match) => match?.group(1)); + /// '.xcodeproj' folder of the host app. + Directory get xcodeProject => directory.childDirectory('$_hostAppBundleName.xcodeproj'); + + /// The '.pbxproj' file of the host app. + File get xcodeProjectInfoFile => xcodeProject.childFile('project.pbxproj'); + + /// The product bundle identifier of the host app. + String get productBundleIdentifier { + return _firstMatchInFile(xcodeProjectInfoFile, _productBundleIdPattern)?.group(1); + } + + /// True, if the host app project is using Swift. + bool get isSwift => buildSettings?.containsKey('SWIFT_VERSION'); + + /// The build settings for the host app of this project, as a detached map. + Map get buildSettings { + return xcode.xcodeProjectInterpreter.getBuildSettings(xcodeProject.path, _hostAppBundleName); } Future ensureReadyForPlatformSpecificTooling() async { - if (isModule && _shouldRegenerateFromTemplate()) { - final Template template = new Template.fromName(fs.path.join('module', 'ios')); - template.render(directory, {}, printStatusWhenWriting: false); - } + _regenerateFromTemplateIfNeeded(); if (!directory.existsSync()) return; - if (Cache.instance.fileOlderThanToolsStamp(generatedXcodePropertiesFile)) { + if (Cache.instance.isOlderThanToolsStamp(generatedXcodePropertiesFile)) { await xcode.updateGeneratedXcodeProperties( project: parent, buildInfo: BuildInfo.debug, @@ -186,12 +207,23 @@ class IosProject { } } - Future materialize() async { - throwToolExit('flutter materialize has not yet been implemented for iOS'); + void _regenerateFromTemplateIfNeeded() { + if (!isModule) + return; + final bool pubspecChanged = isOlderThanReference(entity: directory, referenceFile: parent.pubspecFile); + final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(directory); + 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); + } } - bool _shouldRegenerateFromTemplate() { - return Cache.instance.fileOlderThanToolsStamp(directory.childFile('podhelper.rb')); + Future materialize() async { + throwToolExit('flutter materialize has not yet been implemented for iOS'); } File get generatedXcodePropertiesFile => directory.childDirectory('Flutter').childFile('Generated.xcconfig'); @@ -199,7 +231,20 @@ class IosProject { Directory get pluginRegistrantHost { return isModule ? directory.childDirectory('Flutter').childDirectory('FlutterPluginRegistrant') - : directory.childDirectory('Runner'); + : directory.childDirectory(_hostAppBundleName); + } + + void _overwriteFromTemplate(String path, Directory target) { + final Template template = new Template.fromName(path); + template.render( + target, + { + 'projectName': parent.manifest.appName, + 'iosIdentifier': parent.manifest.iosBundleIdentifier + }, + printStatusWhenWriting: false, + overwriteExisting: true, + ); } } @@ -237,7 +282,7 @@ class AndroidProject { bool get isModule => parent.isModule; File get appManifestFile { - return isUsingGradle() + return isUsingGradle ? fs.file(fs.path.join(hostAppGradleRoot.path, 'app', 'src', 'main', 'AndroidManifest.xml')) : hostAppGradleRoot.childFile('AndroidManifest.xml'); } @@ -248,18 +293,18 @@ class AndroidProject { return fs.directory(fs.path.join(hostAppGradleRoot.path, 'app', 'build', 'outputs', 'apk')); } - bool isUsingGradle() { + bool get isUsingGradle { return hostAppGradleRoot.childFile('build.gradle').existsSync(); } - Future applicationId() { + String get applicationId { final File gradleFile = hostAppGradleRoot.childDirectory('app').childFile('build.gradle'); - return _firstMatchInFile(gradleFile, _applicationIdPattern).then((Match match) => match?.group(1)); + return _firstMatchInFile(gradleFile, _applicationIdPattern)?.group(1); } - Future group() { + String get group { final File gradleFile = hostAppGradleRoot.childFile('build.gradle'); - return _firstMatchInFile(gradleFile, _groupPattern).then((Match match) => match?.group(1)); + return _firstMatchInFile(gradleFile, _groupPattern)?.group(1); } Future ensureReadyForPlatformSpecificTooling() async { @@ -278,7 +323,8 @@ class AndroidProject { } bool _shouldRegenerateFromTemplate() { - return Cache.instance.fileOlderThanToolsStamp(_ephemeralDirectory.childFile('build.gradle')); + return isOlderThanReference(entity: _ephemeralDirectory, referenceFile: parent.pubspecFile) + || Cache.instance.isOlderThanToolsStamp(_ephemeralDirectory); } Future materialize() async { @@ -305,11 +351,6 @@ class AndroidProject { gradle.injectGradleWrapper(_ephemeralDirectory); } - void _deleteIfExistsSync(Directory directory) { - if (directory.existsSync()) - directory.deleteSync(recursive: true); - } - void _overwriteFromTemplate(String path, Directory target) { final Template template = new Template.fromName(path); template.render( @@ -324,17 +365,25 @@ class AndroidProject { } } -/// Asynchronously returns the first line-based match for [regExp] in [file]. +/// Deletes [directory] with all content. +void _deleteIfExistsSync(Directory directory) { + if (directory.existsSync()) + directory.deleteSync(recursive: true); +} + + +/// Returns the first line-based match for [regExp] in [file]. /// /// Assumes UTF8 encoding. -Future _firstMatchInFile(File file, RegExp regExp) async { - if (!await file.exists()) { +Match _firstMatchInFile(File file, RegExp regExp) { + if (!file.existsSync()) { return null; } - return file - .openRead() - .transform(utf8.decoder) - .transform(const LineSplitter()) - .map(regExp.firstMatch) - .firstWhere((Match match) => match != null, orElse: () => null); + for (String line in file.readAsLinesSync()) { + final Match match = regExp.firstMatch(line); + if (match != null) { + return match; + } + } + return null; } diff --git a/packages/flutter_tools/schema/pubspec_yaml.json b/packages/flutter_tools/schema/pubspec_yaml.json index deafe91427..0a4b56e97d 100644 --- a/packages/flutter_tools/schema/pubspec_yaml.json +++ b/packages/flutter_tools/schema/pubspec_yaml.json @@ -47,7 +47,8 @@ "type": "object", "additionalProperties": false, "properties": { - "androidPackage": { "type": "string" } + "androidPackage": { "type": "string" }, + "iosBundleIdentifier": { "type": "string" } } }, "plugin": { diff --git a/packages/flutter_tools/templates/module/README.md b/packages/flutter_tools/templates/module/README.md index f5dc3b0937..8e976a0298 100644 --- a/packages/flutter_tools/templates/module/README.md +++ b/packages/flutter_tools/templates/module/README.md @@ -25,9 +25,7 @@ Flutter views. Written to `.android/` or `android/`. -Mixin for adding Gradle boilerplate to Android projects. The `build.gradle` -file is a template file so that it is created, not copied, on instantiation. -That way, its timestamp reflects template instantiation time. +Mixin for adding Gradle boilerplate to Android projects. #### host_app_common @@ -59,9 +57,31 @@ under app author control) Android host app with a dependency on the ## ios -Written to the `.ios/` hidden folder. +#### library -Contents wraps Flutter/Dart code as a CocoaPods pod. +Written to the `.ios/Flutter` hidden folder. -iOS host apps can set up a dependency to this project to consume +Contents wraps Flutter/Dart code for consumption by an Xcode project. + +iOS host apps can set up a dependency to this contents to consume Flutter views. + +#### host_app_ephemeral + +Written to `.ios/` outside the `Flutter/` sub-folder. + +Combined contents define an *ephemeral* (hidden, auto-generated, +under Flutter tooling control) iOS host app with a dependency on the +`.ios/Flutter` folder contents. + +The host app does not make use of CocoaPods, and is therefore +suitable only when the Flutter part declares no plugin dependencies. + +#### host_app_ephemeral_cocoapods + +Written to `.ios/` on top of `host_app_ephemeral`. + +Adds CocoaPods support. + +Combined contents define an ephemeral host app suitable for when the +Flutter part declares plugin dependencies. diff --git a/packages/flutter_tools/templates/module/android/gradle/build.gradle.tmpl b/packages/flutter_tools/templates/module/android/gradle/build.gradle.copy.tmpl similarity index 100% rename from packages/flutter_tools/templates/module/android/gradle/build.gradle.tmpl rename to packages/flutter_tools/templates/module/android/gradle/build.gradle.copy.tmpl diff --git a/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl b/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl index c2576b5657..f1db0cf6a2 100644 --- a/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl +++ b/packages/flutter_tools/templates/module/common/pubspec.yaml.tmpl @@ -17,3 +17,4 @@ flutter: uses-material-design: true module: androidPackage: {{androidIdentifier}} + iosBundleIdentifier: {{iosIdentifier}} diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Debug.xcconfig b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Debug.xcconfig new file mode 100644 index 0000000000..49ebc7585f --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Debug.xcconfig @@ -0,0 +1 @@ +#include "Flutter.xcconfig" diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Flutter.xcconfig b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Flutter.xcconfig new file mode 100644 index 0000000000..7d679015de --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Flutter.xcconfig @@ -0,0 +1,2 @@ +#include "../Flutter/Generated.xcconfig" +ENABLE_BITCODE=NO diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Release.xcconfig b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Release.xcconfig new file mode 100644 index 0000000000..0a56c0c679 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Config.tmpl/Release.xcconfig @@ -0,0 +1,2 @@ +#include "Flutter.xcconfig" +FLUTTER_BUILD_MODE=release diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/AppDelegate.h b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/AppDelegate.h new file mode 100644 index 0000000000..36e21bbf9c --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : FlutterAppDelegate + +@end diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/AppDelegate.m b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/AppDelegate.m new file mode 100644 index 0000000000..9b9ce33276 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/AppDelegate.m @@ -0,0 +1,10 @@ +#include "AppDelegate.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..d36b1fab2d --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 0000000000..3d43d11e66 Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 0000000000..28c6bf0301 Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 0000000000..2ccbfd967d Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 0000000000..f091b6b0bc Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 0000000000..4cde12118d Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 0000000000..d0ef06e7ed Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 0000000000..dcdc2306c2 Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 0000000000..2ccbfd967d Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 0000000000..c8f9ed8f5c Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 0000000000..a6d6b8609d Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 0000000000..a6d6b8609d Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 0000000000..75b2d164a5 Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 0000000000..c4df70d39d Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 0000000000..6a84f41e14 Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 0000000000..d0e1f58536 Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/Contents.json new file mode 100644 index 0000000000..0bedcf2fd4 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "LaunchImage.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "LaunchImage@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png new file mode 100644 index 0000000000..9da19eacad Binary files /dev/null and b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/README.md b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/README.md new file mode 100644 index 0000000000..89c2725b70 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Assets.xcassets/LaunchImage.imageset/README.md @@ -0,0 +1,5 @@ +# Launch Screen Assets + +You can customize the launch screen with your own desired assets by replacing the image files in this directory. + +You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. \ No newline at end of file diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Base.lproj/LaunchScreen.storyboard b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000..f2e259c7c9 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Base.lproj/Main.storyboard b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Base.lproj/Main.storyboard new file mode 100644 index 0000000000..f3c28516fb --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Base.lproj/Main.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Info.plist.tmpl b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Info.plist.tmpl new file mode 100644 index 0000000000..3a4012f89a --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/Info.plist.tmpl @@ -0,0 +1,45 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + {{projectName}} + CFBundlePackageType + APPL + CFBundleShortVersionString + $(FLUTTER_BUILD_NAME) + CFBundleSignature + ???? + CFBundleVersion + $(FLUTTER_BUILD_NUMBER) + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/main.m b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/main.m new file mode 100644 index 0000000000..dff6597e45 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.tmpl/main.m @@ -0,0 +1,9 @@ +#import +#import +#import "AppDelegate.h" + +int main(int argc, char* argv[]) { + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} 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 new file mode 100644 index 0000000000..7b0eeb1f88 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.pbxproj.tmpl @@ -0,0 +1,435 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; + 741F496221355F47001E2961 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 741F495E21355F27001E2961 /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 741F496821356857001E2961 /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 741F496521356807001E2961 /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 74974048213559DB008C567A /* Release.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 74974046213559DB008C567A /* Release.xcconfig */; }; + 74974049213559DB008C567A /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 74974047213559DB008C567A /* Debug.xcconfig */; }; + 7497404B213559E7008C567A /* Flutter.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 7497404A213559E7008C567A /* Flutter.xcconfig */; }; + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; + 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 741F496321355F47001E2961 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 741F496821356857001E2961 /* App.framework in Embed Frameworks */, + 741F496221355F47001E2961 /* Flutter.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* 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 = ""; }; + 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 = ""; }; + 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 = ""; }; + 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 97C146EB1CF9000F007C117D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 741F49642135620F001E2961 /* Config */ = { + isa = PBXGroup; + children = ( + 74974047213559DB008C567A /* Debug.xcconfig */, + 74974046213559DB008C567A /* Release.xcconfig */, + 7497404A213559E7008C567A /* Flutter.xcconfig */, + ); + path = Config; + sourceTree = ""; + }; + 9740EEB11CF90186004384FC /* Flutter */ = { + isa = PBXGroup; + children = ( + 741F496521356807001E2961 /* App.framework */, + 741F495E21355F27001E2961 /* Flutter.framework */, + 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, + 9740EEB31CF90195004384FC /* Generated.xcconfig */, + ); + name = Flutter; + sourceTree = ""; + }; + 97C146E51CF9000F007C117D = { + isa = PBXGroup; + children = ( + 741F49642135620F001E2961 /* Config */, + 9740EEB11CF90186004384FC /* Flutter */, + 97C146F01CF9000F007C117D /* Runner */, + 97C146EF1CF9000F007C117D /* Products */, + ); + sourceTree = ""; + }; + 97C146EF1CF9000F007C117D /* Products */ = { + isa = PBXGroup; + children = ( + 97C146EE1CF9000F007C117D /* Runner.app */, + ); + name = Products; + sourceTree = ""; + }; + 97C146F01CF9000F007C117D /* Runner */ = { + isa = PBXGroup; + children = ( + 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, + 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, + 97C146FA1CF9000F007C117D /* Main.storyboard */, + 97C146FD1CF9000F007C117D /* Assets.xcassets */, + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, + 97C147021CF9000F007C117D /* Info.plist */, + 97C146F11CF9000F007C117D /* Supporting Files */, + ); + path = Runner; + sourceTree = ""; + }; + 97C146F11CF9000F007C117D /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 97C146F21CF9000F007C117D /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 97C146ED1CF9000F007C117D /* Runner */ = { + isa = PBXNativeTarget; + buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; + buildPhases = ( + 9740EEB61CF901F6004384FC /* Run Script */, + 97C146EA1CF9000F007C117D /* Sources */, + 97C146EB1CF9000F007C117D /* Frameworks */, + 97C146EC1CF9000F007C117D /* Resources */, + 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 741F496321355F47001E2961 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Runner; + productName = Runner; + productReference = 97C146EE1CF9000F007C117D /* Runner.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 97C146E61CF9000F007C117D /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0910; + ORGANIZATIONNAME = "The Chromium Authors"; + TargetAttributes = { + 97C146ED1CF9000F007C117D = { + CreatedOnToolsVersion = 7.3.1; + }; + }; + }; + buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 97C146E51CF9000F007C117D; + productRefGroup = 97C146EF1CF9000F007C117D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 97C146ED1CF9000F007C117D /* Runner */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 97C146EC1CF9000F007C117D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, + 74974049213559DB008C567A /* Debug.xcconfig in Resources */, + 74974048213559DB008C567A /* Release.xcconfig in Resources */, + 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, + 7497404B213559E7008C567A /* Flutter.xcconfig in Resources */, + 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, + 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Thin Binary"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; + }; + 9740EEB61CF901F6004384FC /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 97C146EA1CF9000F007C117D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, + 97C146F31CF9000F007C117D /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 97C146FA1CF9000F007C117D /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C146FB1CF9000F007C117D /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 97C147001CF9000F007C117D /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 97C147031CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 97C147041CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = iphoneos; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 97C147061CF9000F007C117D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 74974047213559DB008C567A /* Debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/Flutter/engine", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}}; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 97C147071CF9000F007C117D /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 74974046213559DB008C567A /* Release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + ENABLE_BITCODE = NO; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + "$(PROJECT_DIR)/Flutter/engine", + ); + INFOPLIST_FILE = Runner/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Flutter", + ); + PRODUCT_BUNDLE_IDENTIFIER = {{iosIdentifier}}; + PRODUCT_NAME = "$(TARGET_NAME)"; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147031CF9000F007C117D /* Debug */, + 97C147041CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 97C147061CF9000F007C117D /* Debug */, + 97C147071CF9000F007C117D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 97C146E61CF9000F007C117D /* Project object */; +} diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.xcworkspace/contents.xcworkspacedata b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..1d526a16ed --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/xcshareddata/xcschemes/Runner.xcscheme new file mode 100644 index 0000000000..1263ac84b1 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcodeproj.tmpl/xcshareddata/xcschemes/Runner.xcscheme @@ -0,0 +1,93 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcworkspace.tmpl/contents.xcworkspacedata b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcworkspace.tmpl/contents.xcworkspacedata new file mode 100644 index 0000000000..1d526a16ed --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral/Runner.xcworkspace.tmpl/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Config.tmpl/Debug.xcconfig b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Config.tmpl/Debug.xcconfig new file mode 100644 index 0000000000..da820d112b --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Config.tmpl/Debug.xcconfig @@ -0,0 +1,2 @@ +#include "Flutter.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Config.tmpl/Release.xcconfig b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Config.tmpl/Release.xcconfig new file mode 100644 index 0000000000..4fc6f7fe10 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Config.tmpl/Release.xcconfig @@ -0,0 +1,3 @@ +#include "Flutter.xcconfig" +#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" +FLUTTER_BUILD_MODE=release 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 new file mode 100644 index 0000000000..f11c13a1ad --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Podfile.copy.tmpl @@ -0,0 +1,6 @@ +platform :ios, '8.0' + +target 'Runner' do + flutter_application_path = '../' + eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb'))) +end diff --git a/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Runner.tmpl/AppDelegate.m b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Runner.tmpl/AppDelegate.m new file mode 100644 index 0000000000..cb3bdff863 --- /dev/null +++ b/packages/flutter_tools/templates/module/ios/host_app_ephemeral_cocoapods/Runner.tmpl/AppDelegate.m @@ -0,0 +1,12 @@ +#include "AppDelegate.h" +#import "FlutterPluginRegistrant/GeneratedPluginRegistrant.h" + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application + didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + [GeneratedPluginRegistrant registerWithRegistry:self]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +@end diff --git a/packages/flutter_tools/templates/module/ios/Flutter.tmpl/AppFrameworkInfo.plist.copy.tmpl b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/AppFrameworkInfo.plist similarity index 100% rename from packages/flutter_tools/templates/module/ios/Flutter.tmpl/AppFrameworkInfo.plist.copy.tmpl rename to packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/AppFrameworkInfo.plist diff --git a/packages/flutter_tools/templates/module/ios/Flutter.tmpl/README.md.copy.tmpl b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/README.md similarity index 100% rename from packages/flutter_tools/templates/module/ios/Flutter.tmpl/README.md.copy.tmpl rename to packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/README.md diff --git a/packages/flutter_tools/templates/module/ios/Flutter.tmpl/podhelper.rb.copy.tmpl b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb similarity index 87% rename from packages/flutter_tools/templates/module/ios/Flutter.tmpl/podhelper.rb.copy.tmpl rename to packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb index e9a3eb63ab..eec077a7a4 100644 --- a/packages/flutter_tools/templates/module/ios/Flutter.tmpl/podhelper.rb.copy.tmpl +++ b/packages/flutter_tools/templates/module/ios/library/Flutter.tmpl/podhelper.rb @@ -1,11 +1,3 @@ -# This file should be used from the target section of the host-app's Podfile like this: -# ``` -# target 'host' do -# flutter_application_path = /"(.*)\/.ios\/Flutter\/Generated.xcconfig"/.match(File.read("./Flutter/FlutterConfig.xcconfig"))[1] -# eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb'))) -# end -# ``` - def parse_KV_file(file, separator='=') file_abs_path = File.expand_path(file) if !File.exists? file_abs_path diff --git a/packages/flutter_tools/test/application_package_test.dart b/packages/flutter_tools/test/application_package_test.dart index bba01f314f..bdd679216d 100644 --- a/packages/flutter_tools/test/application_package_test.dart +++ b/packages/flutter_tools/test/application_package_test.dart @@ -41,17 +41,6 @@ void main() { }); }); - 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 overrides = { FileSystem: () => new MemoryFileSystem(), @@ -165,19 +154,6 @@ void main() { }); } -final Map _swiftBuildSettings = { - '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 18edccef06..33530b6bd6 100644 --- a/packages/flutter_tools/test/commands/create_test.dart +++ b/packages/flutter_tools/test/commands/create_test.dart @@ -340,7 +340,7 @@ void main() { await _createProject(projectDir, ['--no-pub'], []); 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 @@ void main() { ); 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 dcf4600ec6..38b6b09685 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 'package:process/process.dart'; 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({ + 'For our purposes': 'a non-empty build settings map is valid', + }); testTerminal = new TestTerminal(); - app = new BuildableIOSApp( - projectBundleId: 'test.app', - buildSettings: { - '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 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: { - 'DEVELOPMENT_TEAM': 'abc', - }, - ); + when(mockIosProject.buildSettings).thenReturn({ + 'DEVELOPMENT_TEAM': 'abc', + }); final Map 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 0751177a27..b2a129122f 100644 --- a/packages/flutter_tools/test/ios/devices_test.dart +++ b/packages/flutter_tools/test/ios/devices_test.dart @@ -17,6 +17,7 @@ import 'package:process/process.dart'; 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 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4 }); 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 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4 final IOSDevice device = new IOSDevice('123456'); final DeviceLogReader logReader = device.getLogReader( - app: new BuildableIOSApp(projectBundleId: 'bundleId'), + app: new BuildableIOSApp(mockIosProject), ); final List lines = await logReader.logLines.toList(); @@ -147,7 +150,7 @@ f577a7903cc54959be2e34bc4f7f80b7009efcf4 final IOSDevice device = new IOSDevice('123456'); final DeviceLogReader logReader = device.getLogReader( - app: new BuildableIOSApp(projectBundleId: 'bundleId'), + app: new BuildableIOSApp(mockIosProject), ); final List 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 e4edf4ef36..5517e359fc 100644 --- a/packages/flutter_tools/test/ios/simulators_test.dart +++ b/packages/flutter_tools/test/ios/simulators_test.dart @@ -13,6 +13,7 @@ import 'package:process/process.dart'; 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 @@ void main() { group('log reader', () { MockProcessManager mockProcessManager; + MockIosProject mockIosProject; setUp(() { mockProcessManager = new MockProcessManager(); + mockIosProject = new MockIosProject(); }); testUsingContext('simulator can output `)`', () async { @@ -316,7 +319,7 @@ void main() { 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 lines = await logReader.logLines.toList(); diff --git a/packages/flutter_tools/test/project_test.dart b/packages/flutter_tools/test/project_test.dart index bdde5d22b6..3e3ce86466 100644 --- a/packages/flutter_tools/test/project_test.dart +++ b/packages/flutter_tools/test/project_test.dart @@ -224,50 +224,50 @@ void main() { 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(), ['io.flutter']); + expect(project.organizationNames, ['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(), ['io.flutter']); + expect(project.organizationNames, ['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(), ['io.flutter']); + expect(project.organizationNames, ['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(), ['io.flutter']); + expect(project.organizationNames, ['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(), ['io.flutter']); + expect(project.organizationNames, ['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(), ['io.flutter']); + expect(project.organizationNames, ['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, ['io.flutter', 'io.clutter'], ); }); diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index a4326e0db6..2ed3e0a87b 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/devfs.dart'; 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 @@ class MockApplicationPackageStore extends ApplicationPackageStore { 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 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { Stream 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 get targetPlatform async => TargetPlatform.android_arm;