Turn off bitcode in existing iOS Xcode projects (#112828)

diff --git a/packages/flutter_tools/lib/src/ios/mac.dart b/packages/flutter_tools/lib/src/ios/mac.dart
index 3fcf176..15fe0a3 100644
--- a/packages/flutter_tools/lib/src/ios/mac.dart
+++ b/packages/flutter_tools/lib/src/ios/mac.dart
@@ -31,6 +31,7 @@
 import 'migrations/ios_deployment_target_migration.dart';
 import 'migrations/project_base_configuration_migration.dart';
 import 'migrations/project_build_location_migration.dart';
+import 'migrations/remove_bitcode_migration.dart';
 import 'migrations/remove_framework_link_and_embedding_migration.dart';
 import 'migrations/xcode_build_system_migration.dart';
 import 'xcode_build_settings.dart';
@@ -128,6 +129,7 @@
     XcodeProjectObjectVersionMigration(app.project, globals.logger),
     HostAppInfoPlistMigration(app.project, globals.logger),
     XcodeScriptBuildPhaseMigration(app.project, globals.logger),
+    RemoveBitcodeMigration(app.project, globals.logger),
   ];
 
   final ProjectMigration migration = ProjectMigration(migrators);
diff --git a/packages/flutter_tools/lib/src/ios/migrations/remove_bitcode_migration.dart b/packages/flutter_tools/lib/src/ios/migrations/remove_bitcode_migration.dart
new file mode 100644
index 0000000..4042113
--- /dev/null
+++ b/packages/flutter_tools/lib/src/ios/migrations/remove_bitcode_migration.dart
@@ -0,0 +1,41 @@
+// Copyright 2014 The Flutter 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 '../../base/file_system.dart';
+import '../../base/project_migrator.dart';
+import '../../xcode_project.dart';
+
+/// Remove deprecated bitcode build setting.
+class RemoveBitcodeMigration extends ProjectMigrator {
+  RemoveBitcodeMigration(
+    IosProject project,
+    super.logger,
+  )   : _xcodeProjectInfoFile = project.xcodeProjectInfoFile;
+
+  final File _xcodeProjectInfoFile;
+
+  @override
+  bool migrate() {
+    if (_xcodeProjectInfoFile.existsSync()) {
+      processFileLines(_xcodeProjectInfoFile);
+    } else {
+      logger.printTrace('Xcode project not found, skipping removing bitcode migration.');
+    }
+
+    return true;
+  }
+
+  @override
+  String? migrateLine(String line) {
+    if (line.contains('ENABLE_BITCODE = YES;')) {
+      if (!migrationRequired) {
+        // Only print for the first discovered change found.
+        logger.printWarning('Disabling deprecated bitcode Xcode build setting. See https://github.com/flutter/flutter/issues/107887 for additional details.');
+      }
+      return line.replaceAll('ENABLE_BITCODE = YES', 'ENABLE_BITCODE = NO');
+    }
+
+    return line;
+  }
+}
diff --git a/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart
index 89b6ebf..cecb705 100644
--- a/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/ios_project_migration_test.dart
@@ -10,6 +10,7 @@
 import 'package:flutter_tools/src/ios/migrations/ios_deployment_target_migration.dart';
 import 'package:flutter_tools/src/ios/migrations/project_base_configuration_migration.dart';
 import 'package:flutter_tools/src/ios/migrations/project_build_location_migration.dart';
+import 'package:flutter_tools/src/ios/migrations/remove_bitcode_migration.dart';
 import 'package:flutter_tools/src/ios/migrations/remove_framework_link_and_embedding_migration.dart';
 import 'package:flutter_tools/src/ios/migrations/xcode_build_system_migration.dart';
 import 'package:flutter_tools/src/migrations/xcode_project_object_version_migration.dart';
@@ -821,6 +822,76 @@
 '''));
       });
     });
+
+    group('remove bitcode build setting', () {
+      late MemoryFileSystem memoryFileSystem;
+      late BufferLogger testLogger;
+      late FakeIosProject project;
+      late File xcodeProjectInfoFile;
+
+      setUp(() {
+        memoryFileSystem = MemoryFileSystem();
+        testLogger = BufferLogger.test();
+        project = FakeIosProject();
+        xcodeProjectInfoFile = memoryFileSystem.file('project.pbxproj');
+        project.xcodeProjectInfoFile = xcodeProjectInfoFile;
+      });
+
+      testWithoutContext('skipped if files are missing', () {
+        final RemoveBitcodeMigration migration = RemoveBitcodeMigration(
+          project,
+          testLogger,
+        );
+        expect(migration.migrate(), isTrue);
+        expect(xcodeProjectInfoFile.existsSync(), isFalse);
+
+        expect(testLogger.traceText, contains('Xcode project not found, skipping removing bitcode migration'));
+        expect(testLogger.statusText, isEmpty);
+      });
+
+      testWithoutContext('skipped if nothing to upgrade', () {
+        const String xcodeProjectInfoFileContents = 'IPHONEOS_DEPLOYMENT_TARGET = 11.0;';
+        xcodeProjectInfoFile.writeAsStringSync(xcodeProjectInfoFileContents);
+        final DateTime projectLastModified = xcodeProjectInfoFile.lastModifiedSync();
+
+        final RemoveBitcodeMigration migration = RemoveBitcodeMigration(
+          project,
+          testLogger,
+        );
+        expect(migration.migrate(), isTrue);
+
+        expect(xcodeProjectInfoFile.lastModifiedSync(), projectLastModified);
+        expect(xcodeProjectInfoFile.readAsStringSync(), xcodeProjectInfoFileContents);
+
+        expect(testLogger.statusText, isEmpty);
+      });
+
+      testWithoutContext('bitcode build setting is removed', () {
+        xcodeProjectInfoFile.writeAsStringSync('''
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ENABLE_BITCODE = YES;
+				INFOPLIST_FILE = Runner/Info.plist;
+
+				ENABLE_BITCODE = YES;
+''');
+
+        final RemoveBitcodeMigration migration = RemoveBitcodeMigration(
+          project,
+          testLogger,
+        );
+        expect(migration.migrate(), isTrue);
+
+        expect(xcodeProjectInfoFile.readAsStringSync(), '''
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				ENABLE_BITCODE = NO;
+				INFOPLIST_FILE = Runner/Info.plist;
+
+				ENABLE_BITCODE = NO;
+''');
+        // Only print once even though 2 lines were changed.
+        expect('Disabling deprecated bitcode Xcode build setting'.allMatches(testLogger.warningText).length, 1);
+      });
+    });
   });
 
   group('update Xcode script build phase', () {