synchronizeDevSdkCollectionBuildPhase method
Synchronizes the existing dev-only phase without changing production inputs.
Implementation
Future<void> synchronizeDevSdkCollectionBuildPhase({
bool disableCollection = false,
}) async {
final projectFile = File("ios/Runner.xcodeproj/project.pbxproj");
if (!projectFile.existsSync()) {
throw Exception(
"Runner.xcodeproj does not exist in `ios/Runner.xcodeproj/project.pbxproj`.",
);
}
var source = await projectFile.readAsString();
var script = _firebaseMessagingAutoInitBuildScript;
if (disableCollection) {
script = script
.replaceAll("FirebaseMessagingAutoInitEnabled", r"$KEY")
.replaceFirst(
r'if [ "$FLAVOR" = "dev" ]; then',
"for KEY in FirebaseMessagingAutoInitEnabled FIREBASE_ANALYTICS_COLLECTION_ENABLED FirebaseCrashlyticsCollectionEnabled firebase_performance_collection_enabled; do\n"
r'if [ "$FLAVOR" = "dev" ]; then',
)
.replaceFirst(" exit 0\nfi", " continue\nfi");
script = "$script\ndone\n";
}
final escapedScript = script
.replaceAll(r"\", r"\\")
.replaceAll('"', r'\"')
.replaceAll("\n", r"\n");
final phasePattern = RegExp(
"$_firebaseMessagingAutoInitBuildPhaseId /\\* $_firebaseMessagingAutoInitBuildPhaseName \\*/ = \\{[\\s\\S]*?\\n[ \\t]*\\};",
);
final existingPhase = phasePattern.firstMatch(source);
if (existingPhase != null) {
final updatedPhase = existingPhase
.group(0)!
.replaceFirst(
RegExp(r'shellScript = "(?:\\.|[^"\\])*";'),
'shellScript = "$escapedScript";',
)
.replaceFirst(
RegExp(r"inputPaths = \([\s\S]*?\);"),
r'inputPaths = ("$(TARGET_BUILD_DIR)/$(INFOPLIST_PATH)");',
)
.replaceFirst(
RegExp(r"outputPaths = \([\s\S]*?\);"),
"outputPaths = ();",
);
source = source.replaceRange(
existingPhase.start,
existingPhase.end,
updatedPhase,
);
} else {
const sectionEnd = "/* End PBXShellScriptBuildPhase section */";
final sectionIndex = source.indexOf(sectionEnd);
if (sectionIndex < 0) {
throw const FormatException(
"PBXShellScriptBuildPhase section was not found.",
);
}
final phase = '''
\t\t$_firebaseMessagingAutoInitBuildPhaseId /* $_firebaseMessagingAutoInitBuildPhaseName */ = {
\t\t\tisa = PBXShellScriptBuildPhase;
\t\t\talwaysOutOfDate = 1;
\t\t\tbuildActionMask = 2147483647;
\t\t\tfiles = ();
\t\t\tinputFileListPaths = ();
\t\t\tinputPaths = ("\$(TARGET_BUILD_DIR)/\$(INFOPLIST_PATH)");
\t\t\tname = "$_firebaseMessagingAutoInitBuildPhaseName";
\t\t\toutputFileListPaths = ();
\t\t\toutputPaths = ();
\t\t\trunOnlyForDeploymentPostprocessing = 0;
\t\t\tshellPath = /bin/sh;
\t\t\tshellScript = "$escapedScript";
\t\t};
''';
source = source.replaceRange(sectionIndex, sectionIndex, phase);
}
const runnerTargetMarker = "/* Runner */ = {";
const nativeTargetSection = "/* Begin PBXNativeTarget section */";
final nativeTargetStart = source.indexOf(nativeTargetSection);
final runnerTargetStart = source.indexOf(
runnerTargetMarker,
nativeTargetStart,
);
final runnerTargetEnd = source.indexOf("\n\t\t};", runnerTargetStart);
if (runnerTargetStart < 0 || runnerTargetEnd < 0) {
throw const FormatException("The Runner Xcode target was not found.");
}
var runnerTarget = source.substring(runnerTargetStart, runnerTargetEnd);
const phaseReference =
"$_firebaseMessagingAutoInitBuildPhaseId /* $_firebaseMessagingAutoInitBuildPhaseName */,";
runnerTarget = runnerTarget
.replaceAll(
RegExp("^[ \t]*${RegExp.escape(phaseReference)}\n", multiLine: true),
"",
)
.replaceAll(RegExp(r"[ \t]+\n"), "\n");
final buildPhasesStart = runnerTarget.indexOf("buildPhases = (\n");
final buildPhasesEnd = runnerTarget.indexOf("\n\t\t\t);", buildPhasesStart);
if (buildPhasesStart < 0 || buildPhasesEnd < 0) {
throw const FormatException("The Runner build phases were not found.");
}
runnerTarget = runnerTarget.replaceRange(
buildPhasesEnd,
buildPhasesEnd,
"\n\t\t\t\t$phaseReference",
);
source = source.replaceRange(
runnerTargetStart,
runnerTargetEnd,
runnerTarget,
);
await projectFile.writeAsString(source);
}