Skip to content

Commit

Permalink
Add iOS module template (flutter#18830)
Browse files Browse the repository at this point in the history
Add iOS module template

This will enable integration of flutter-views into existing iOS project.
  • Loading branch information
sigurdm authored Jun 28, 2018
1 parent 3158d85 commit 8be198d
Show file tree
Hide file tree
Showing 17 changed files with 395 additions and 114 deletions.
10 changes: 10 additions & 0 deletions examples/flutter_gallery/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = EQHXZ8M8AV;
ProvisioningStyle = Manual;
};
};
};
Expand Down Expand Up @@ -427,6 +429,8 @@
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = EQHXZ8M8AV;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -440,6 +444,8 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "89aa7622-6c03-475d-baed-b7ebcc3c63b2";
PROVISIONING_PROFILE_SPECIFIER = "Google Development";
};
name = Debug;
};
Expand All @@ -448,6 +454,8 @@
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CODE_SIGN_STYLE = Manual;
DEVELOPMENT_TEAM = EQHXZ8M8AV;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
Expand All @@ -461,6 +469,8 @@
);
PRODUCT_BUNDLE_IDENTIFIER = io.flutter.examples.gallery;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE = "89aa7622-6c03-475d-baed-b7ebcc3c63b2";
PROVISIONING_PROFILE_SPECIFIER = "Google Development";
};
name = Release;
};
Expand Down
24 changes: 20 additions & 4 deletions packages/flutter_tools/bin/xcode_backend.sh
Original file line number Diff line number Diff line change
Expand Up @@ -83,10 +83,20 @@ BuildApp() {
RunCommand mkdir -p -- "$derived_dir"
AssertExists "$derived_dir"

RunCommand rm -rf -- "${derived_dir}/Flutter.framework"
RunCommand rm -rf -- "${derived_dir}/App.framework"
RunCommand cp -r -- "${framework_path}/Flutter.framework" "${derived_dir}"
RunCommand find "${derived_dir}/Flutter.framework" -type f -exec chmod a-w "{}" \;

if [[ -e "${project_path}/.ios" ]]; then
RunCommand rm -rf -- "${derived_dir}/engine"
mkdir "${derived_dir}/engine"
RunCommand cp -r -- "${framework_path}/Flutter.podspec" "${derived_dir}/engine"
RunCommand cp -r -- "${framework_path}/Flutter.framework" "${derived_dir}/engine"
RunCommand find "${derived_dir}/engine/Flutter.framework" -type f -exec chmod a-w "{}" \;
else
RunCommand rm -rf -- "${derived_dir}/Flutter.framework"
RunCommand cp -r -- "${framework_path}/Flutter.framework" "${derived_dir}"
RunCommand find "${derived_dir}/Flutter.framework" -type f -exec chmod a-w "{}" \;
fi

RunCommand pushd "${project_path}" > /dev/null

AssertExists "${target_path}"
Expand Down Expand Up @@ -155,7 +165,13 @@ BuildApp() {
-install_name '@rpath/App.framework/App' \
-o "${derived_dir}/App.framework/App" -)"
fi
RunCommand cp -- "${project_path}/ios/Flutter/AppFrameworkInfo.plist" "${derived_dir}/App.framework/Info.plist"

local plistPath="${project_path}/ios/Flutter/AppFrameworkInfo.plist"
if [[ -e "${project_path}/.ios" ]]; then
plistPath="${project_path}/.ios/Flutter/AppFrameworkInfo.plist"
fi

RunCommand cp -- "$plistPath" "${derived_dir}/App.framework/Info.plist"

local precompilation_flag=""
if [[ "$CURRENT_ARCH" != "x86_64" ]] && [[ "$build_mode" != "debug" ]]; then
Expand Down
13 changes: 13 additions & 0 deletions packages/flutter_tools/lib/src/cache.dart
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,19 @@ 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;
}
final File flutterToolsStamp = getStampFileFor('flutter_tools');
return flutterToolsStamp.existsSync() &&
flutterToolsStamp
.lastModifiedSync()
.isAfter(file.lastModifiedSync());
}

bool isUpToDate() => _artifacts.every((CachedArtifact artifact) => artifact.isUpToDate());

Future<String> getThirdPartyFile(String urlStr, String serviceName) async {
Expand Down
22 changes: 11 additions & 11 deletions packages/flutter_tools/lib/src/commands/create.dart
Original file line number Diff line number Diff line change
Expand Up @@ -233,20 +233,20 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
}
}

Future<int> _generateModule(String dirPath, Map<String, dynamic> templateContext) async {
Future<int> _generateModule(String path, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
final String description = argResults.wasParsed('description')
? argResults['description']
: 'A new flutter module project.';
templateContext['description'] = description;
generatedCount += _renderTemplate(fs.path.join('module', 'common'), dirPath, templateContext);
generatedCount += _renderTemplate(fs.path.join('module', 'common'), path, templateContext);
if (argResults['pub']) {
await pubGet(
context: PubContext.create,
directory: dirPath,
directory: path,
offline: argResults['offline'],
);
final FlutterProject project = new FlutterProject(fs.directory(dirPath));
final FlutterProject project = new FlutterProject.fromPath(path);
await project.ensureReadyForPlatformSpecificTooling();
}
return generatedCount;
Expand Down Expand Up @@ -303,23 +303,23 @@ To edit platform code in an IDE see https://flutter.io/developing-packages/#edit
return generatedCount;
}

Future<int> _generateApp(String appPath, Map<String, dynamic> templateContext) async {
Future<int> _generateApp(String projectPath, Map<String, dynamic> templateContext) async {
int generatedCount = 0;
generatedCount += _renderTemplate('create', appPath, templateContext);
generatedCount += _injectGradleWrapper(appPath);
generatedCount += _renderTemplate('create', projectPath, templateContext);
generatedCount += _injectGradleWrapper(projectPath);

if (argResults['with-driver-test']) {
final String testPath = fs.path.join(appPath, 'test_driver');
final String testPath = fs.path.join(projectPath, 'test_driver');
generatedCount += _renderTemplate('driver', testPath, templateContext);
}

if (argResults['pub']) {
await pubGet(context: PubContext.create, directory: appPath, offline: argResults['offline']);
await new FlutterProject(fs.directory(appPath)).ensureReadyForPlatformSpecificTooling();
await pubGet(context: PubContext.create, directory: projectPath, offline: argResults['offline']);
await new FlutterProject.fromPath(projectPath).ensureReadyForPlatformSpecificTooling();
}

if (android_sdk.androidSdk != null)
await gradle.updateLocalProperties(projectPath: appPath);
await gradle.updateLocalProperties(projectPath: projectPath);

return generatedCount;
}
Expand Down
6 changes: 5 additions & 1 deletion packages/flutter_tools/lib/src/commands/inject_plugins.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import 'dart:async';

import '../base/file_system.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../plugins.dart';
import '../runner/flutter_command.dart';
Expand All @@ -24,7 +26,9 @@ class InjectPluginsCommand extends FlutterCommand {

@override
Future<Null> runCommand() async {
injectPlugins();
final String projectPath = fs.currentDirectory.path;
final FlutterManifest manifest = await FlutterManifest.createFromPath(projectPath);
injectPlugins(projectPath: projectPath, manifest: manifest);
final bool result = hasPlugins();
if (result) {
printStatus('GeneratedPluginRegistrants successfully written.');
Expand Down
3 changes: 1 addition & 2 deletions packages/flutter_tools/lib/src/commands/packages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import 'dart:async';

import '../base/common.dart';
import '../base/file_system.dart';
import '../base/os.dart';
import '../dart/pub.dart';
import '../project.dart';
Expand Down Expand Up @@ -81,7 +80,7 @@ class PackagesGetCommand extends FlutterCommand {
}

await _runPubGet(target);
final FlutterProject rootProject = new FlutterProject(fs.directory(target));
final FlutterProject rootProject = new FlutterProject.fromPath(target);
await rootProject.ensureReadyForPlatformSpecificTooling();

// Get/upgrade packages in example app as well
Expand Down
7 changes: 6 additions & 1 deletion packages/flutter_tools/lib/src/ios/cocoapods.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import '../base/process.dart';
import '../base/process_manager.dart';
import '../base/version.dart';
import '../cache.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import 'xcodeproj.dart';

Expand Down Expand Up @@ -150,11 +151,14 @@ class CocoaPods {
/// Ensures the `ios` sub-project of the Flutter project at [appDirectory]
/// contains a suitable `Podfile` and that its `Flutter/Xxx.xcconfig` files
/// include pods configuration.
void setupPodfile(String appDirectory) {
void setupPodfile(String appDirectory, FlutterManifest manifest) {
if (!xcodeProjectInterpreter.isInstalled) {
// Don't do anything for iOS when host platform doesn't support it.
return;
}
if (!fs.directory(fs.path.join(appDirectory, 'ios')).existsSync()) {
return;
}
final String podfilePath = fs.path.join(appDirectory, 'ios', 'Podfile');
if (!fs.file(podfilePath).existsSync()) {
final bool isSwift = xcodeProjectInterpreter.getBuildSettings(
Expand All @@ -171,6 +175,7 @@ class CocoaPods {
));
podfileTemplate.copySync(podfilePath);
}

_addPodsDependencyToFlutterXcconfig(appDirectory, 'Debug');
_addPodsDependencyToFlutterXcconfig(appDirectory, 'Release');
}
Expand Down
7 changes: 6 additions & 1 deletion packages/flutter_tools/lib/src/ios/mac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import '../base/process.dart';
import '../base/process_manager.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../flutter_manifest.dart';
import '../globals.dart';
import '../plugins.dart';
import '../services.dart';
Expand Down Expand Up @@ -242,11 +243,15 @@ Future<XcodeBuildResult> buildXcodeProject({
final Directory appDirectory = fs.directory(app.appDirectory);
await _addServicesToBundle(appDirectory);

await updateGeneratedXcodeProperties(
final FlutterManifest manifest = await FlutterManifest.createFromPath(
fs.currentDirectory.childFile('pubspec.yaml').path,
);
updateGeneratedXcodeProperties(
projectPath: fs.currentDirectory.path,
buildInfo: buildInfo,
targetOverride: targetOverride,
previewDart2: buildInfo.previewDart2,
manifest: manifest,
);

if (hasPlugins()) {
Expand Down
50 changes: 26 additions & 24 deletions packages/flutter_tools/lib/src/ios/xcodeproj.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'dart:async';

import 'package:meta/meta.dart';

import '../artifacts.dart';
import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart';
import '../base/io.dart';
Expand All @@ -28,21 +25,30 @@ String flutterFrameworkDir(BuildMode mode) {
return fs.path.normalize(fs.path.dirname(artifacts.getArtifactPath(Artifact.flutterFramework, TargetPlatform.ios, mode)));
}

String _generatedXcodePropertiesPath(String projectPath) {
return fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig');
String _generatedXcodePropertiesPath({@required String projectPath, @required FlutterManifest manifest}) {
if (manifest.isModule) {
return fs.path.join(projectPath, '.ios', 'Flutter', 'Generated.xcconfig');
} else {
return fs.path.join(projectPath, 'ios', 'Flutter', 'Generated.xcconfig');
}
}

/// Writes default Xcode properties files in the Flutter project at [projectPath],
/// if project is an iOS project and such files do not already exist.
Future<void> generateXcodeProperties(String projectPath) async {
if (fs.isDirectorySync(fs.path.join(projectPath, 'ios'))) {
if (fs.file(_generatedXcodePropertiesPath(projectPath)).existsSync())
/// if project is an iOS project and such files are out of date or do not
/// already exist.
void generateXcodeProperties({String projectPath, FlutterManifest manifest}) {
if (manifest.isModule || fs.isDirectorySync(fs.path.join(projectPath, 'ios'))) {
final File propertiesFile = fs.file(_generatedXcodePropertiesPath(projectPath: projectPath, manifest: manifest));
if (!Cache.instance.fileOlderThanToolsStamp(propertiesFile)) {
return;
await updateGeneratedXcodeProperties(
}

updateGeneratedXcodeProperties(
projectPath: projectPath,
manifest: manifest,
buildInfo: BuildInfo.debug,
targetOverride: bundle.defaultMainPath,
previewDart2: false,
previewDart2: true,
);
}
}
Expand All @@ -51,14 +57,13 @@ Future<void> generateXcodeProperties(String projectPath) async {
///
/// targetOverride: Optional parameter, if null or unspecified the default value
/// from xcode_backend.sh is used 'lib/main.dart'.
///
/// Returns the number of files written.
Future<void> updateGeneratedXcodeProperties({
void updateGeneratedXcodeProperties({
@required String projectPath,
@required FlutterManifest manifest,
@required BuildInfo buildInfo,
String targetOverride,
@required bool previewDart2,
}) async {
}) {
final StringBuffer localsBuffer = new StringBuffer();

localsBuffer.writeln('// This is a generated file; do not edit or check into version control.');
Expand All @@ -81,14 +86,11 @@ Future<void> updateGeneratedXcodeProperties({

localsBuffer.writeln('SYMROOT=\${SOURCE_ROOT}/../${getIosBuildDirectory()}');

localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');

final String flutterManifest = fs.path.join(projectPath, bundle.defaultManifestPath);
FlutterManifest manifest;
try {
manifest = await FlutterManifest.createFromPath(flutterManifest);
} catch (error) {
throwToolExit('Failed to load pubspec.yaml: $error');
if (!manifest.isModule) {
// For module projects we do not want to write the FLUTTER_FRAMEWORK_DIR
// explicitly. Rather we rely on the xcode backend script and the Podfile
// logic to derive it from FLUTTER_ROOT and FLUTTER_BUILD_MODE.
localsBuffer.writeln('FLUTTER_FRAMEWORK_DIR=${flutterFrameworkDir(buildInfo.mode)}');
}

final String buildName = buildInfo?.buildName ?? manifest.buildName;
Expand Down Expand Up @@ -123,7 +125,7 @@ Future<void> updateGeneratedXcodeProperties({
localsBuffer.writeln('TRACK_WIDGET_CREATION=true');
}

final File localsFile = fs.file(_generatedXcodePropertiesPath(projectPath));
final File localsFile = fs.file(_generatedXcodePropertiesPath(projectPath: projectPath, manifest: manifest));
localsFile.createSync(recursive: true);
localsFile.writeAsStringSync(localsBuffer.toString());
}
Expand Down
Loading

0 comments on commit 8be198d

Please sign in to comment.