synchronizeDevSdkCollectionBuildPhase method

Future<void> synchronizeDevSdkCollectionBuildPhase({
  1. bool disableCollection = false,
})

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