flutter start initializes the Xcode project if the user has not already done so.
This commit is contained in:
@@ -13,7 +13,7 @@ import '../android/android.dart' as android;
|
||||
import '../artifacts.dart';
|
||||
import '../base/globals.dart';
|
||||
import '../dart/pub.dart';
|
||||
import 'ios.dart';
|
||||
import '../ios/initialize_xcode.dart';
|
||||
|
||||
class CreateCommand extends Command {
|
||||
final String name = 'create';
|
||||
|
||||
@@ -5,20 +5,9 @@
|
||||
import "dart:async";
|
||||
import "dart:io";
|
||||
|
||||
import "package:path/path.dart" as path;
|
||||
|
||||
import "../artifacts.dart";
|
||||
import "../base/globals.dart";
|
||||
import "../base/process.dart";
|
||||
import "../runner/flutter_command.dart";
|
||||
import "../runner/flutter_command_runner.dart";
|
||||
|
||||
/// A map from file path to file contents.
|
||||
final Map<String, String> iosTemplateFiles = <String, String>{
|
||||
'ios/Info.plist': _infoPlistInitialContents,
|
||||
'ios/LaunchScreen.storyboard': _launchScreenInitialContents,
|
||||
'ios/Assets.xcassets/AppIcon.appiconset/Contents.json': _iconAssetInitialContents
|
||||
};
|
||||
import "../ios/initialize_xcode.dart";
|
||||
|
||||
class IOSCommand extends FlutterCommand {
|
||||
final String name = "ios";
|
||||
@@ -28,148 +17,6 @@ class IOSCommand extends FlutterCommand {
|
||||
argParser.addFlag('init', help: 'Initialize the Xcode project for building the iOS application');
|
||||
}
|
||||
|
||||
static Uri _xcodeProjectUri(String revision) {
|
||||
String uriString = "https://storage.googleapis.com/flutter_infra/flutter/$revision/ios/FlutterXcode.zip";
|
||||
return Uri.parse(uriString);
|
||||
}
|
||||
|
||||
Future<List<int>> _fetchXcodeArchive() async {
|
||||
printStatus("Fetching the Xcode project archive from the cloud...");
|
||||
|
||||
HttpClient client = new HttpClient();
|
||||
|
||||
Uri xcodeProjectUri = _xcodeProjectUri(ArtifactStore.engineRevision);
|
||||
printStatus("Downloading $xcodeProjectUri...");
|
||||
HttpClientRequest request = await client.getUrl(xcodeProjectUri);
|
||||
HttpClientResponse response = await request.close();
|
||||
|
||||
if (response.statusCode != 200)
|
||||
throw new Exception(response.reasonPhrase);
|
||||
|
||||
BytesBuilder bytesBuilder = new BytesBuilder(copy: false);
|
||||
await for (List<int> chunk in response)
|
||||
bytesBuilder.add(chunk);
|
||||
|
||||
return bytesBuilder.takeBytes();
|
||||
}
|
||||
|
||||
Future<bool> _inflateXcodeArchive(String directory, List<int> archiveBytes) async {
|
||||
printStatus("Unzipping Xcode project to local directory...");
|
||||
|
||||
// We cannot use ArchiveFile because this archive contains files that are exectuable
|
||||
// and there is currently no provision to modify file permissions during
|
||||
// or after creation. See https://github.com/dart-lang/sdk/issues/15078.
|
||||
// So we depend on the platform to unzip the archive for us.
|
||||
|
||||
Directory tempDir = await Directory.systemTemp.create();
|
||||
File tempFile = new File(path.join(tempDir.path, "FlutterXcode.zip"))..createSync();
|
||||
tempFile.writeAsBytesSync(archiveBytes);
|
||||
|
||||
try {
|
||||
// Remove the old generated project if one is present
|
||||
runCheckedSync(['/bin/rm', '-rf', directory]);
|
||||
// Create the directory so unzip can write to it
|
||||
runCheckedSync(['/bin/mkdir', '-p', directory]);
|
||||
// Unzip the Xcode project into the new empty directory
|
||||
runCheckedSync(['/usr/bin/unzip', tempFile.path, '-d', directory]);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup the temp directory after unzipping
|
||||
runSync(['/bin/rm', '-rf', tempDir.path]);
|
||||
|
||||
Directory flutterDir = new Directory(path.join(directory, 'Flutter'));
|
||||
bool flutterDirExists = await flutterDir.exists();
|
||||
if (!flutterDirExists)
|
||||
return false;
|
||||
|
||||
// Move contents of the Flutter directory one level up
|
||||
// There is no dart:io API to do this. See https://github.com/dart-lang/sdk/issues/8148
|
||||
|
||||
for (FileSystemEntity file in flutterDir.listSync()) {
|
||||
try {
|
||||
runCheckedSync(['/bin/mv', file.path, directory]);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
runSync(['/bin/rm', '-rf', flutterDir.path]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _writeUserEditableFilesIfNecessary(String directory) {
|
||||
iosTemplateFiles.forEach((String filePath, String contents) {
|
||||
File file = new File(filePath);
|
||||
|
||||
if (!file.existsSync()) {
|
||||
file.parent.createSync(recursive: true);
|
||||
file.writeAsStringSync(contents);
|
||||
printStatus('Created $filePath.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _setupXcodeProjXcconfig(String filePath) {
|
||||
StringBuffer localsBuffer = new StringBuffer();
|
||||
|
||||
localsBuffer.writeln("// This is a generated file; do not edit or check into version control.");
|
||||
localsBuffer.writeln("// Recreate using `flutter ios --init`.");
|
||||
|
||||
String flutterRoot = path.normalize(Platform.environment[kFlutterRootEnvironmentVariableName]);
|
||||
localsBuffer.writeln("FLUTTER_ROOT=$flutterRoot");
|
||||
|
||||
// This holds because requiresProjectRoot is true for this command
|
||||
String applicationRoot = path.normalize(Directory.current.path);
|
||||
localsBuffer.writeln("FLUTTER_APPLICATION_PATH=$applicationRoot");
|
||||
|
||||
String dartSDKPath = path.normalize(path.join(Platform.resolvedExecutable, "..", ".."));
|
||||
localsBuffer.writeln("DART_SDK_PATH=$dartSDKPath");
|
||||
|
||||
File localsFile = new File(filePath);
|
||||
localsFile.createSync(recursive: true);
|
||||
localsFile.writeAsStringSync(localsBuffer.toString());
|
||||
}
|
||||
|
||||
Future<int> _runInitCommand() async {
|
||||
// Step 1: Fetch the archive from the cloud
|
||||
String iosFilesPath = path.join(Directory.current.path, "ios");
|
||||
String xcodeprojPath = path.join(iosFilesPath, ".generated");
|
||||
List<int> archiveBytes = await _fetchXcodeArchive();
|
||||
|
||||
if (archiveBytes.isEmpty) {
|
||||
printError("Error: No archive bytes received.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 2: Inflate the archive into the user project directory
|
||||
bool result = await _inflateXcodeArchive(xcodeprojPath, archiveBytes);
|
||||
if (!result) {
|
||||
printError("Could not inflate the Xcode project archive.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 3: Setup default user editable files if this is the first run of
|
||||
// the init command.
|
||||
_writeUserEditableFilesIfNecessary(iosFilesPath);
|
||||
|
||||
// Step 4: Populate the Local.xcconfig with project specific paths
|
||||
_setupXcodeProjXcconfig(path.join(xcodeprojPath, "Local.xcconfig"));
|
||||
|
||||
// Step 5: Write the REVISION file
|
||||
File revisionFile = new File(path.join(xcodeprojPath, "REVISION"));
|
||||
revisionFile.createSync();
|
||||
revisionFile.writeAsStringSync(ArtifactStore.engineRevision);
|
||||
|
||||
// Step 6: Tell the user the location of the generated project.
|
||||
printStatus("Xcode project created at $xcodeprojPath/.");
|
||||
printStatus("User editable settings are in $iosFilesPath/.");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<int> runInProject() async {
|
||||
if (!Platform.isMacOS) {
|
||||
@@ -178,165 +25,9 @@ class IOSCommand extends FlutterCommand {
|
||||
}
|
||||
|
||||
if (argResults['init'])
|
||||
return await _runInitCommand();
|
||||
return await initializeXcodeProjectHarness();
|
||||
|
||||
printError("No flags specified.");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
final String _infoPlistInitialContents = '''
|
||||
<?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>Runner</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.runner.Runner</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Flutter</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
''';
|
||||
|
||||
final String _launchScreenInitialContents = '''
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<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="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
''';
|
||||
|
||||
final String _iconAssetInitialContents = '''
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
''';
|
||||
|
||||
@@ -17,6 +17,7 @@ import '../build_configuration.dart';
|
||||
import '../device.dart';
|
||||
import '../services.dart';
|
||||
import '../toolchain.dart';
|
||||
import '../ios/initialize_xcode.dart';
|
||||
import 'simulator.dart';
|
||||
|
||||
const String _ideviceinstallerInstructions =
|
||||
@@ -560,8 +561,11 @@ String _getIOSEngineRevision(ApplicationPackage app) {
|
||||
|
||||
Future<bool> _buildIOSXcodeProject(ApplicationPackage app, { bool buildForDevice }) async {
|
||||
if (!FileSystemEntity.isDirectorySync(app.localPath)) {
|
||||
printError('Path "${path.absolute(app.localPath)}" does not exist.\nDid you run `flutter ios --init`?');
|
||||
return false;
|
||||
printTrace('Path "${path.absolute(app.localPath)}" does not exist. Initializing the Xcode project.');
|
||||
if ((await initializeXcodeProjectHarness()) != 0) {
|
||||
printError('Could not initialize the Xcode project.');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!_validateEngineRevision(app))
|
||||
|
||||
318
packages/flutter_tools/lib/src/ios/initialize_xcode.dart
Normal file
318
packages/flutter_tools/lib/src/ios/initialize_xcode.dart
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
import '../artifacts.dart';
|
||||
import '../base/globals.dart';
|
||||
import '../base/process.dart';
|
||||
import '../runner/flutter_command_runner.dart';
|
||||
|
||||
/// A map from file path to file contents.
|
||||
final Map<String, String> iosTemplateFiles = <String, String>{
|
||||
'ios/Info.plist': _infoPlistInitialContents,
|
||||
'ios/LaunchScreen.storyboard': _launchScreenInitialContents,
|
||||
'ios/Assets.xcassets/AppIcon.appiconset/Contents.json': _iconAssetInitialContents
|
||||
};
|
||||
|
||||
Uri _xcodeProjectUri(String revision) {
|
||||
String uriString = 'https://storage.googleapis.com/flutter_infra/flutter/$revision/ios/FlutterXcode.zip';
|
||||
return Uri.parse(uriString);
|
||||
}
|
||||
|
||||
Future<List<int>> _fetchXcodeArchive() async {
|
||||
printStatus('Fetching the Xcode project archive from the cloud...');
|
||||
|
||||
HttpClient client = new HttpClient();
|
||||
|
||||
Uri xcodeProjectUri = _xcodeProjectUri(ArtifactStore.engineRevision);
|
||||
printStatus('Downloading $xcodeProjectUri...');
|
||||
HttpClientRequest request = await client.getUrl(xcodeProjectUri);
|
||||
HttpClientResponse response = await request.close();
|
||||
|
||||
if (response.statusCode != 200)
|
||||
throw new Exception(response.reasonPhrase);
|
||||
|
||||
BytesBuilder bytesBuilder = new BytesBuilder(copy: false);
|
||||
await for (List<int> chunk in response)
|
||||
bytesBuilder.add(chunk);
|
||||
|
||||
return bytesBuilder.takeBytes();
|
||||
}
|
||||
|
||||
Future<bool> _inflateXcodeArchive(String directory, List<int> archiveBytes) async {
|
||||
printStatus('Unzipping Xcode project to local directory...');
|
||||
|
||||
// We cannot use ArchiveFile because this archive contains files that are exectuable
|
||||
// and there is currently no provision to modify file permissions during
|
||||
// or after creation. See https://github.com/dart-lang/sdk/issues/15078.
|
||||
// So we depend on the platform to unzip the archive for us.
|
||||
|
||||
Directory tempDir = await Directory.systemTemp.create();
|
||||
File tempFile = new File(path.join(tempDir.path, 'FlutterXcode.zip'))..createSync();
|
||||
tempFile.writeAsBytesSync(archiveBytes);
|
||||
|
||||
try {
|
||||
// Remove the old generated project if one is present
|
||||
runCheckedSync(['/bin/rm', '-rf', directory]);
|
||||
// Create the directory so unzip can write to it
|
||||
runCheckedSync(['/bin/mkdir', '-p', directory]);
|
||||
// Unzip the Xcode project into the new empty directory
|
||||
runCheckedSync(['/usr/bin/unzip', tempFile.path, '-d', directory]);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Cleanup the temp directory after unzipping
|
||||
runSync(['/bin/rm', '-rf', tempDir.path]);
|
||||
|
||||
Directory flutterDir = new Directory(path.join(directory, 'Flutter'));
|
||||
bool flutterDirExists = await flutterDir.exists();
|
||||
if (!flutterDirExists)
|
||||
return false;
|
||||
|
||||
// Move contents of the Flutter directory one level up
|
||||
// There is no dart:io API to do this. See https://github.com/dart-lang/sdk/issues/8148
|
||||
|
||||
for (FileSystemEntity file in flutterDir.listSync()) {
|
||||
try {
|
||||
runCheckedSync(['/bin/mv', file.path, directory]);
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
runSync(['/bin/rm', '-rf', flutterDir.path]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void _writeUserEditableFilesIfNecessary(String directory) {
|
||||
iosTemplateFiles.forEach((String filePath, String contents) {
|
||||
File file = new File(filePath);
|
||||
|
||||
if (!file.existsSync()) {
|
||||
file.parent.createSync(recursive: true);
|
||||
file.writeAsStringSync(contents);
|
||||
printStatus('Created $filePath.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _setupXcodeProjXcconfig(String filePath) {
|
||||
StringBuffer localsBuffer = new StringBuffer();
|
||||
|
||||
localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
|
||||
localsBuffer.writeln('// Recreate using `flutter ios --init`.');
|
||||
|
||||
String flutterRoot = path.normalize(Platform.environment[kFlutterRootEnvironmentVariableName]);
|
||||
localsBuffer.writeln('FLUTTER_ROOT=$flutterRoot');
|
||||
|
||||
// This holds because requiresProjectRoot is true for this command
|
||||
String applicationRoot = path.normalize(Directory.current.path);
|
||||
localsBuffer.writeln('FLUTTER_APPLICATION_PATH=$applicationRoot');
|
||||
|
||||
String dartSDKPath = path.normalize(path.join(Platform.resolvedExecutable, '..', '..'));
|
||||
localsBuffer.writeln('DART_SDK_PATH=$dartSDKPath');
|
||||
|
||||
File localsFile = new File(filePath);
|
||||
localsFile.createSync(recursive: true);
|
||||
localsFile.writeAsStringSync(localsBuffer.toString());
|
||||
}
|
||||
|
||||
Future<int> initializeXcodeProjectHarness() async {
|
||||
// Step 1: Fetch the archive from the cloud
|
||||
String iosFilesPath = path.join(Directory.current.path, 'ios');
|
||||
String xcodeprojPath = path.join(iosFilesPath, '.generated');
|
||||
List<int> archiveBytes = await _fetchXcodeArchive();
|
||||
|
||||
if (archiveBytes.isEmpty) {
|
||||
printError('Error: No archive bytes received.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 2: Inflate the archive into the user project directory
|
||||
bool result = await _inflateXcodeArchive(xcodeprojPath, archiveBytes);
|
||||
if (!result) {
|
||||
printError('Could not inflate the Xcode project archive.');
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Step 3: Setup default user editable files if this is the first run of
|
||||
// the init command.
|
||||
_writeUserEditableFilesIfNecessary(iosFilesPath);
|
||||
|
||||
// Step 4: Populate the Local.xcconfig with project specific paths
|
||||
_setupXcodeProjXcconfig(path.join(xcodeprojPath, 'Local.xcconfig'));
|
||||
|
||||
// Step 5: Write the REVISION file
|
||||
File revisionFile = new File(path.join(xcodeprojPath, 'REVISION'));
|
||||
revisionFile.createSync();
|
||||
revisionFile.writeAsStringSync(ArtifactStore.engineRevision);
|
||||
|
||||
// Step 6: Tell the user the location of the generated project.
|
||||
printStatus('Xcode project created at $xcodeprojPath/.');
|
||||
printStatus('User editable settings are in $iosFilesPath/.');
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
final String _infoPlistInitialContents = '''
|
||||
<?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>Runner</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>io.flutter.runner.Runner</string>
|
||||
<key>CFBundleInfoDictionaryVersion</key>
|
||||
<string>6.0</string>
|
||||
<key>CFBundleName</key>
|
||||
<string>Flutter</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIRequiredDeviceCapabilities</key>
|
||||
<array>
|
||||
<string>arm64</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||
<false/>
|
||||
</dict>
|
||||
</plist>
|
||||
''';
|
||||
|
||||
final String _launchScreenInitialContents = '''
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="9531" systemVersion="15C50" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" initialViewController="01J-lp-oVM">
|
||||
<dependencies>
|
||||
<deployment identifier="iOS"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="9529"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Llm-lL-Icb"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xb3-aO-Qok"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<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="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="53" y="375"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
</document>
|
||||
''';
|
||||
|
||||
final String _iconAssetInitialContents = '''
|
||||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "29x29",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "40x40",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "iphone",
|
||||
"size" : "60x60",
|
||||
"scale" : "3x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "29x29",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "40x40",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "76x76",
|
||||
"scale" : "2x"
|
||||
},
|
||||
{
|
||||
"idiom" : "ipad",
|
||||
"size" : "83.5x83.5",
|
||||
"scale" : "2x"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
''';
|
||||
Reference in New Issue
Block a user