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

This commit is contained in:
Mikkel Nygaard Ravn 2018-08-30 16:18:44 +02:00 committed by GitHub
parent 19c9628256
commit 22832d3634
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 1092 additions and 199 deletions

View File

@ -19,7 +19,7 @@ Future<Null> 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'

View File

@ -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<AndroidApk> 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<String, String> 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<String, String> 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<ApplicationPackage> 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:

View File

@ -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);
}

View File

@ -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());

View File

@ -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<String> existingOrganizations = await project.organizationNames();
final Set<String> existingOrganizations = project.organizationNames;
if (existingOrganizations.length == 1) {
organization = existingOrganizations.first;
} else if (1 < existingOrganizations.length) {

View File

@ -26,6 +26,7 @@ class InjectPluginsCommand extends FlutterCommand {
@override
Future<Null> runCommand() async {
final FlutterProject project = await FlutterProject.current();
refreshPluginsList(project);
await injectPlugins(project);
final bool result = hasPlugins(project);
if (result) {

View File

@ -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<Map<String, dynamic>> get fontsDescriptor {
final List<dynamic> fontList = _flutterDescriptor['fonts'];
return fontList == null

View File

@ -96,20 +96,21 @@ Future<Map<String, String>> getCodeSigningIdentityDevelopmentTeam({
BuildableIOSApp iosApp,
bool usesTerminalUi = true
}) async{
if (iosApp.buildSettings == null)
final Map<String, String> 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

View File

@ -189,13 +189,13 @@ Future<XcodeBuildResult> 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<XcodeBuildResult> 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<XcodeBuildResult> 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: <String>[
_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: <String, String>{},
);
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<XcodeBuildResult> buildXcodeProject({
buildCommands.add('-allowProvisioningDeviceRegistration');
}
final List<FileSystemEntity> contents = fs.directory(app.appDirectory).listSync();
final List<FileSystemEntity> contents = app.project.directory.listSync();
for (FileSystemEntity entity in contents) {
if (fs.path.extension(entity.path) == '.xcworkspace') {
buildCommands.addAll(<String>[
@ -353,7 +351,7 @@ Future<XcodeBuildResult> 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<XcodeBuildResult> buildXcodeProject({
'-allowProvisioningDeviceRegistration',
].contains(buildCommand);
}).toList(),
workingDirectory: app.appDirectory,
workingDirectory: app.project.directory.path,
));
if (buildResult.exitCode != 0) {
@ -400,7 +398,7 @@ Future<XcodeBuildResult> 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<Null> _copyServiceFrameworks(List<Map<String, String>> 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<Map<String, String>> services, File manifest) {
printTrace("Creating service definitions manifest at '${manifest.path}'");
final List<Map<String, String>> jsonServices = services.map((Map<String, String> service) => <String, String>{
@ -583,8 +578,8 @@ void _copyServiceDefinitionsManifest(List<Map<String, String>> services, File ma
manifest.writeAsStringSync(json.encode(jsonObject), mode: FileMode.write, flush: true);
}
Future<bool> upgradePbxProjWithFlutterAssets(String app, String appPath) async {
final File xcodeProjectFile = fs.file(_getPbxProjPath(appPath));
Future<bool> upgradePbxProjWithFlutterAssets(IosProject project) async {
final File xcodeProjectFile = project.xcodeProjectInfoFile;
assert(await xcodeProjectFile.exists());
final List<String> lines = await xcodeProjectFile.readAsLines();
@ -601,7 +596,7 @@ Future<bool> 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) ||

View File

@ -241,8 +241,7 @@ Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plug
'name': p.name,
'prefix': p.iosPrefix,
'class': p.pluginClass,
}).
toList();
}).toList();
final Map<String, dynamic> context = <String, dynamic>{
'plugins': iosPlugins,
};
@ -279,23 +278,34 @@ Future<void> _writeIOSPluginRegistrant(FlutterProject project, List<Plugin> plug
}
}
/// Injects plugins found in `pubspec.yaml` into the platform-specific projects.
Future<void> 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<Plugin> 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<void> injectPlugins(FlutterProject project) async {
final List<Plugin> 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;
}

View File

@ -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<Set<String>> organizationNames() async {
final List<String> candidates = await Future.wait(<Future<String>>[
ios.productBundleIdentifier(),
android.applicationId(),
android.group(),
example.android.applicationId(),
example.ios.productBundleIdentifier(),
]);
Set<String> get organizationNames {
final List<String> candidates = <String>[
ios.productBundleIdentifier,
android.applicationId,
android.group,
example.android.applicationId,
example.ios.productBundleIdentifier,
];
return new Set<String>.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<void> 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<String> 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<String, String> get buildSettings {
return xcode.xcodeProjectInterpreter.getBuildSettings(xcodeProject.path, _hostAppBundleName);
}
Future<void> ensureReadyForPlatformSpecificTooling() async {
if (isModule && _shouldRegenerateFromTemplate()) {
final Template template = new Template.fromName(fs.path.join('module', 'ios'));
template.render(directory, <String, dynamic>{}, 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<void> 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<void> 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,
<String, dynamic>{
'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<String> 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<String> 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<void> 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<void> 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<Match> _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;
}

View File

@ -47,7 +47,8 @@
"type": "object",
"additionalProperties": false,
"properties": {
"androidPackage": { "type": "string" }
"androidPackage": { "type": "string" },
"iosBundleIdentifier": { "type": "string" }
}
},
"plugin": {

View File

@ -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.

View File

@ -17,3 +17,4 @@ flutter:
uses-material-design: true
module:
androidPackage: {{androidIdentifier}}
iosBundleIdentifier: {{iosIdentifier}}

View File

@ -0,0 +1 @@
#include "Flutter.xcconfig"

View File

@ -0,0 +1,2 @@
#include "../Flutter/Generated.xcconfig"
ENABLE_BITCODE=NO

View File

@ -0,0 +1,2 @@
#include "Flutter.xcconfig"
FLUTTER_BUILD_MODE=release

View File

@ -0,0 +1,6 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
@interface AppDelegate : FlutterAppDelegate
@end

View File

@ -0,0 +1,10 @@
#include "AppDelegate.h"
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
return [super application:application didFinishLaunchingWithOptions:launchOptions];
}
@end

View File

@ -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"
}
}

View File

@ -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"
}
}

View File

@ -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.

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>{{projectName}}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
</dict>
</plist>

View File

@ -0,0 +1,9 @@
#import <Flutter/Flutter.h>
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char* argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}

View File

@ -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 = "<group>"; };
741F496521356807001E2961 /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
74974046213559DB008C567A /* Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
74974047213559DB008C567A /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
7497404A213559E7008C567A /* Flutter.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Flutter.xcconfig; sourceTree = "<group>"; };
7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
/* 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 = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
741F496521356807001E2961 /* App.framework */,
741F495E21355F27001E2961 /* Flutter.framework */,
2D5378251FAA1A9400D5DBA9 /* flutter_assets */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
741F49642135620F001E2961 /* Config */,
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
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 = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
97C146F21CF9000F007C117D /* main.m */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* 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 = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* 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 */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0910"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
</Workspace>

View File

@ -0,0 +1,2 @@
#include "Flutter.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"

View File

@ -0,0 +1,3 @@
#include "Flutter.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
FLUTTER_BUILD_MODE=release

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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<Type, Generator> overrides = <Type, Generator>{
FileSystem: () => new MemoryFileSystem(),
@ -165,19 +154,6 @@ void main() {
});
}
final Map<String, String> _swiftBuildSettings = <String, String>{
'ARCHS': 'arm64',
'ASSETCATALOG_COMPILER_APPICON_NAME': 'AppIcon',
'CLANG_ENABLE_MODULES': 'YES',
'ENABLE_BITCODE': 'NO',
'INFOPLIST_FILE': 'Runner/Info.plist',
'PRODUCT_BUNDLE_IDENTIFIER': 'com.example.test',
'PRODUCT_NAME': 'blah',
'SWIFT_OBJC_BRIDGING_HEADER': 'Runner/Runner-Bridging-Header.h',
'SWIFT_OPTIMIZATION_LEVEL': '-Onone',
'SWIFT_VERSION': '3.0',
};
const String _aaptDataWithExplicitEnabledActivity =
'''N: android=http://schemas.android.com/apk/res/android
E: manifest (line=7)

View File

@ -340,7 +340,7 @@ void main() {
await _createProject(projectDir, <String>['--no-pub'], <String>[]);
final FlutterProject project = await FlutterProject.fromDirectory(projectDir);
expect(
await project.ios.productBundleIdentifier(),
project.ios.productBundleIdentifier,
'com.bar.foo.flutterProject',
);
}, timeout: allowForCreateFlutterProject);
@ -367,7 +367,7 @@ 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);

View File

@ -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(<String, String>{
'For our purposes': 'a non-empty build settings map is valid',
});
testTerminal = new TestTerminal();
app = new BuildableIOSApp(
projectBundleId: 'test.app',
buildSettings: <String, String>{
'For our purposes': 'a non-empty build settings map is valid',
},
);
app = new BuildableIOSApp(mockIosProject);
});
testUsingContext('No auto-sign if Xcode project settings are not available', () async {
app = new BuildableIOSApp(projectBundleId: 'test.app');
when(mockIosProject.buildSettings).thenReturn(null);
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(signingConfigs, isNull);
});
testUsingContext('No discovery if development team specified in Xcode project', () async {
app = new BuildableIOSApp(
projectBundleId: 'test.app',
buildSettings: <String, String>{
'DEVELOPMENT_TEAM': 'abc',
},
);
when(mockIosProject.buildSettings).thenReturn(<String, String>{
'DEVELOPMENT_TEAM': 'abc',
});
final Map<String, String> signingConfigs = await getCodeSigningIdentityDevelopmentTeam(iosApp: app);
expect(signingConfigs, isNull);
expect(testLogger.statusText, equals(

View File

@ -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<String> 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<String> lines = await logReader.logLines.toList();

View File

@ -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<String> lines = await logReader.logLines.toList();

View File

@ -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(), <String>['io.flutter']);
expect(project.organizationNames, <String>['io.flutter']);
});
testInMemory('is populated from Android application ID', () async {
final FlutterProject project = await someProject();
addAndroidWithApplicationId(project.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
expect(project.organizationNames, <String>['io.flutter']);
});
testInMemory('is populated from iOS bundle identifier in plugin example', () async {
final FlutterProject project = await someProject();
addIosWithBundleId(project.example.directory, 'io.flutter.someProject');
expect(await project.organizationNames(), <String>['io.flutter']);
expect(project.organizationNames, <String>['io.flutter']);
});
testInMemory('is populated from Android application ID in plugin example', () async {
final FlutterProject project = await someProject();
addAndroidWithApplicationId(project.example.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
expect(project.organizationNames, <String>['io.flutter']);
});
testInMemory('is populated from Android group in plugin', () async {
final FlutterProject project = await someProject();
addAndroidWithGroup(project.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
expect(project.organizationNames, <String>['io.flutter']);
});
testInMemory('is singleton, if sources agree', () async {
final FlutterProject project = await someProject();
addIosWithBundleId(project.directory, 'io.flutter.someProject');
addAndroidWithApplicationId(project.directory, 'io.flutter.someproject');
expect(await project.organizationNames(), <String>['io.flutter']);
expect(project.organizationNames, <String>['io.flutter']);
});
testInMemory('is non-singleton, if sources disagree', () async {
final FlutterProject project = await someProject();
addIosWithBundleId(project.directory, 'io.flutter.someProject');
addAndroidWithApplicationId(project.directory, 'io.clutter.someproject');
expect(
await project.organizationNames(),
project.organizationNames,
<String>['io.flutter', 'io.clutter'],
);
});

View File

@ -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<Device> get onRemoved => _onRemovedController.stream;
}
class MockIosProject extends Mock implements IosProject {
@override
String get productBundleIdentifier => 'com.example.test';
@override
String get hostAppBundleName => 'Runner.app';
}
class MockAndroidDevice extends Mock implements AndroidDevice {
@override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;