[macOS] Remigrate principal class to NSApplication (#124173)

In #122336 we migrated the principal class from NSApplication to
FlutterApplication in the app Info.plist. We removed the need for
FlutterApplication in https://github.com/flutter/engine/pull/40929. This
reverses the migration for anyone who previously upgraded on the Flutter
master branch.

Issue: https://github.com/flutter/flutter/issues/30735

## Pre-launch Checklist

- [X] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [X] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [X] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [X] I signed the [CLA].
- [X] I listed at least one issue that this PR fixes in the description
above.
- [X] I updated/added relevant documentation (doc comments with `///`).
- [X] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [X] All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel
on [Discord].

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#overview
[Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene
[test-exempt]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[flutter/tests]: https://github.com/flutter/tests
[breaking change policy]:
https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes
[Discord]: https://github.com/flutter/flutter/wiki/Chat
diff --git a/dev/benchmarks/complex_layout/macos/Runner/Info.plist b/dev/benchmarks/complex_layout/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/dev/benchmarks/complex_layout/macos/Runner/Info.plist
+++ b/dev/benchmarks/complex_layout/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/dev/benchmarks/macrobenchmarks/macos/Runner/Info.plist b/dev/benchmarks/macrobenchmarks/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/dev/benchmarks/macrobenchmarks/macos/Runner/Info.plist
+++ b/dev/benchmarks/macrobenchmarks/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/dev/integration_tests/channels/macos/Runner/Info.plist b/dev/integration_tests/channels/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/dev/integration_tests/channels/macos/Runner/Info.plist
+++ b/dev/integration_tests/channels/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/dev/integration_tests/flavors/macos/Runner/Info.plist b/dev/integration_tests/flavors/macos/Runner/Info.plist
index eba8f24..e6e728b 100644
--- a/dev/integration_tests/flavors/macos/Runner/Info.plist
+++ b/dev/integration_tests/flavors/macos/Runner/Info.plist
@@ -29,6 +29,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/dev/integration_tests/flutter_gallery/macos/Runner/Info.plist b/dev/integration_tests/flutter_gallery/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/dev/integration_tests/flutter_gallery/macos/Runner/Info.plist
+++ b/dev/integration_tests/flutter_gallery/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/dev/integration_tests/ui/macos/Runner/Info.plist b/dev/integration_tests/ui/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/dev/integration_tests/ui/macos/Runner/Info.plist
+++ b/dev/integration_tests/ui/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/dev/manual_tests/macos/Runner/Info.plist b/dev/manual_tests/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/dev/manual_tests/macos/Runner/Info.plist
+++ b/dev/manual_tests/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/api/macos/Runner/Info.plist b/examples/api/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/api/macos/Runner/Info.plist
+++ b/examples/api/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/flutter_view/macos/Runner/Info.plist b/examples/flutter_view/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/flutter_view/macos/Runner/Info.plist
+++ b/examples/flutter_view/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/hello_world/macos/Runner/Info.plist b/examples/hello_world/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/hello_world/macos/Runner/Info.plist
+++ b/examples/hello_world/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/image_list/macos/Runner/Info.plist b/examples/image_list/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/image_list/macos/Runner/Info.plist
+++ b/examples/image_list/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/layers/macos/Runner/Info.plist b/examples/layers/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/layers/macos/Runner/Info.plist
+++ b/examples/layers/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/platform_channel/macos/Runner/Info.plist b/examples/platform_channel/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/platform_channel/macos/Runner/Info.plist
+++ b/examples/platform_channel/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/examples/platform_view/macos/Runner/Info.plist b/examples/platform_view/macos/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/examples/platform_view/macos/Runner/Info.plist
+++ b/examples/platform_view/macos/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/packages/flutter_tools/lib/src/macos/migrations/flutter_application_migration.dart b/packages/flutter_tools/lib/src/macos/migrations/flutter_application_migration.dart
index 14ea4d6..ac0bef6 100644
--- a/packages/flutter_tools/lib/src/macos/migrations/flutter_application_migration.dart
+++ b/packages/flutter_tools/lib/src/macos/migrations/flutter_application_migration.dart
@@ -8,7 +8,13 @@
 import '../../ios/plist_parser.dart';
 import '../../xcode_project.dart';
 
-/// Update the minimum macOS deployment version to the minimum allowed by Xcode without causing a warning.
+/// Migrate principle class from FlutterApplication to NSApplication.
+///
+/// For several weeks, we required macOS apps to use FlutterApplication as the
+/// app's NSPrincipalClass rather than NSApplication. During that time an
+/// automated migration migrated the NSPrincipalClass in the Info.plist from
+/// NSApplication to FlutterApplication. Now that this is no longer necessary,
+/// we apply the reverse migration for anyone who was previously migrated.
 class FlutterApplicationMigration extends ProjectMigrator {
   FlutterApplicationMigration(
     MacOSProject project,
@@ -20,29 +26,23 @@
   @override
   void migrate() {
     if (_infoPlistFile.existsSync()) {
-      final String? principleClass =
+      final String? principalClass =
           globals.plistParser.getStringValueFromFile(_infoPlistFile.path, PlistParser.kNSPrincipalClassKey);
-      if (principleClass == null || principleClass == 'FlutterApplication') {
-        // No NSPrincipalClass defined, or already converted, so no migration
+      if (principalClass == null || principalClass == 'NSApplication') {
+        // No NSPrincipalClass defined, or already converted. No migration
         // needed.
         return;
       }
-      if (principleClass != 'NSApplication') {
-        // Only replace NSApplication values, since we don't know why they might
-        // have substituted something else.
-        logger.printTrace('${_infoPlistFile.basename} has an '
-          '${PlistParser.kNSPrincipalClassKey} of $principleClass, not '
-          'NSApplication, skipping FlutterApplication migration.\nYou will need '
-          'to modify your application class to derive from FlutterApplication.');
+      if (principalClass != 'FlutterApplication') {
+        // If the principal class wasn't already migrated to
+        // FlutterApplication, there's no need to revert the migration.
         return;
       }
-      logger.printStatus('Updating ${_infoPlistFile.basename} to use FlutterApplication instead of NSApplication.');
-      final bool success = globals.plistParser.replaceKey(_infoPlistFile.path, key: PlistParser.kNSPrincipalClassKey, value: 'FlutterApplication');
+      logger.printStatus('Updating ${_infoPlistFile.basename} to use NSApplication instead of FlutterApplication.');
+      final bool success = globals.plistParser.replaceKey(_infoPlistFile.path, key: PlistParser.kNSPrincipalClassKey, value: 'NSApplication');
       if (!success) {
         logger.printError('Updating ${_infoPlistFile.basename} failed.');
       }
-    } else {
-      logger.printTrace('${_infoPlistFile.basename} not found, skipping FlutterApplication migration.');
     }
   }
 }
diff --git a/packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Info.plist b/packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Info.plist
index 55c6534..4789daa 100644
--- a/packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Info.plist
+++ b/packages/flutter_tools/templates/app_shared/macos.tmpl/Runner/Info.plist
@@ -27,6 +27,6 @@
 	<key>NSMainNibFile</key>
 	<string>MainMenu</string>
 	<key>NSPrincipalClass</key>
-	<string>FlutterApplication</string>
+	<string>NSApplication</string>
 </dict>
 </plist>
diff --git a/packages/flutter_tools/test/general.shard/macos/macos_project_migration_test.dart b/packages/flutter_tools/test/general.shard/macos/macos_project_migration_test.dart
index cfcd32e..5abb987 100644
--- a/packages/flutter_tools/test/general.shard/macos/macos_project_migration_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/macos_project_migration_test.dart
@@ -280,7 +280,7 @@
     });
   });
 
-  group('update NSPrincipalClass to FlutterApplication', () {
+  group('update NSPrincipalClass from FlutterApplication to NSApplication', () {
     late MemoryFileSystem memoryFileSystem;
     late BufferLogger testLogger;
     late FakeMacOSProject project;
@@ -318,7 +318,7 @@
       macOSProjectMigration.migrate();
       expect(infoPlistFile.existsSync(), isFalse);
 
-      expect(testLogger.traceText, contains('${infoPlistFile.basename} not found, skipping FlutterApplication migration.'));
+      expect(testLogger.traceText, isEmpty);
       expect(testLogger.statusText, isEmpty);
     });
 
@@ -333,19 +333,7 @@
       expect(testLogger.statusText, isEmpty);
     });
 
-    testWithMocks('skipped if already upgraded', () async {
-      fakePlistParser.setProperty(PlistParser.kNSPrincipalClassKey, 'FlutterApplication');
-      final FlutterApplicationMigration macOSProjectMigration = FlutterApplicationMigration(
-        project,
-        testLogger,
-      );
-      infoPlistFile.writeAsStringSync('contents'); // Just so it exists: parser is a fake.
-      macOSProjectMigration.migrate();
-      expect(fakePlistParser.getStringValueFromFile(infoPlistFile.path, PlistParser.kNSPrincipalClassKey), 'FlutterApplication');
-      expect(testLogger.statusText, isEmpty);
-    });
-
-    testWithMocks('Info.plist migrated to use FlutterApplication', () async {
+    testWithMocks('skipped if already de-upgraded (or never migrated)', () async {
       fakePlistParser.setProperty(PlistParser.kNSPrincipalClassKey, 'NSApplication');
       final FlutterApplicationMigration macOSProjectMigration = FlutterApplicationMigration(
         project,
@@ -353,9 +341,21 @@
       );
       infoPlistFile.writeAsStringSync('contents'); // Just so it exists: parser is a fake.
       macOSProjectMigration.migrate();
-      expect(fakePlistParser.getStringValueFromFile(infoPlistFile.path, PlistParser.kNSPrincipalClassKey), 'FlutterApplication');
+      expect(fakePlistParser.getStringValueFromFile(infoPlistFile.path, PlistParser.kNSPrincipalClassKey), 'NSApplication');
+      expect(testLogger.statusText, isEmpty);
+    });
+
+    testWithMocks('Info.plist migrated to use NSApplication', () async {
+      fakePlistParser.setProperty(PlistParser.kNSPrincipalClassKey, 'FlutterApplication');
+      final FlutterApplicationMigration macOSProjectMigration = FlutterApplicationMigration(
+        project,
+        testLogger,
+      );
+      infoPlistFile.writeAsStringSync('contents'); // Just so it exists: parser is a fake.
+      macOSProjectMigration.migrate();
+      expect(fakePlistParser.getStringValueFromFile(infoPlistFile.path, PlistParser.kNSPrincipalClassKey), 'NSApplication');
       // Only print once.
-      expect('Updating ${infoPlistFile.basename} to use FlutterApplication instead of NSApplication.'.allMatches(testLogger.statusText).length, 1);
+      expect('Updating ${infoPlistFile.basename} to use NSApplication instead of FlutterApplication.'.allMatches(testLogger.statusText).length, 1);
     });
 
     testWithMocks('Skip if NSPrincipalClass is not NSApplication', () async {
@@ -368,7 +368,7 @@
       infoPlistFile.writeAsStringSync('contents'); // Just so it exists: parser is a fake.
       macOSProjectMigration.migrate();
       expect(fakePlistParser.getStringValueFromFile(infoPlistFile.path, PlistParser.kNSPrincipalClassKey), differentApp);
-      expect(testLogger.traceText, contains('${infoPlistFile.basename} has an ${PlistParser.kNSPrincipalClassKey} of $differentApp, not NSApplication, skipping FlutterApplication migration'));
+      expect(testLogger.traceText, isEmpty);
     });
   });
 }