Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev/a11y_assessments/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
2 changes: 1 addition & 1 deletion dev/benchmarks/complex_layout/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import UIKit

@UIApplicationMain
@main
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't a Flutter application. I updated this by hand.

class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
2 changes: 1 addition & 1 deletion dev/manual_tests/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
2 changes: 1 addition & 1 deletion examples/api/ios/Runner/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import UIKit
import Flutter

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ enum MyFlutterErrorCode {
static let unavailable = "UNAVAILABLE"
}

@UIApplicationMain
@main
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains significant edits from the starter app template. The migration is flexible enough to be able to update this correctly.

@objc class AppDelegate: FlutterAppDelegate, FlutterStreamHandler {
private var eventSink: FlutterEventSink?

Expand Down
2 changes: 2 additions & 0 deletions packages/flutter_tools/lib/src/ios/mac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ 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/uiapplicationmain_deprecation_migration.dart';
import 'migrations/xcode_build_system_migration.dart';
import 'xcode_build_settings.dart';
import 'xcodeproj.dart';
Expand Down Expand Up @@ -158,6 +159,7 @@ Future<XcodeBuildResult> buildXcodeProject({
XcodeScriptBuildPhaseMigration(app.project, globals.logger),
RemoveBitcodeMigration(app.project, globals.logger),
XcodeThinBinaryBuildPhaseInputPathsMigration(app.project, globals.logger),
UIApplicationMainDeprecationMigration(app.project, globals.logger),
];

final ProjectMigration migration = ProjectMigration(migrators);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,12 @@ class RemoveBitcodeMigration extends ProjectMigrator {
final File _xcodeProjectInfoFile;

@override
Future<bool> migrate() async {
Future<void> migrate() async {
if (_xcodeProjectInfoFile.existsSync()) {
processFileLines(_xcodeProjectInfoFile);
} else {
logger.printTrace('Xcode project not found, skipping removing bitcode migration.');
}

return true;
}

@override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// 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';

const String _appDelegateFileBefore = r'''
@UIApplicationMain
@objc class AppDelegate''';

const String _appDelegateFileAfter = r'''
@main
@objc class AppDelegate''';

/// Replace the deprecated `@UIApplicationMain` attribute with `@main`.
///
/// See:
/// https://github.com/apple/swift-evolution/blob/main/proposals/0383-deprecate-uiapplicationmain-and-nsapplicationmain.md
class UIApplicationMainDeprecationMigration extends ProjectMigrator {
UIApplicationMainDeprecationMigration(
IosProject project,
super.logger,
) : _appDelegateSwift = project.appDelegateSwift;

final File _appDelegateSwift;

@override
Future<void> migrate() async {
// Skip this migration if the project uses Objective-C.
if (!_appDelegateSwift.existsSync()) {
logger.printTrace(
'ios/Runner/AppDelegate.swift not found, skipping @main migration.',
);
return;
}

// Migrate the ios/Runner/AppDelegate.swift file.
final String original = _appDelegateSwift.readAsStringSync();
final String migrated = original.replaceFirst(_appDelegateFileBefore, _appDelegateFileAfter);
if (original == migrated) {
return;
}

logger.printWarning(
'ios/Runner/AppDelegate.swift uses the deprecated @UIApplicationMain attribute, updating.',
);
_appDelegateSwift.writeAsStringSync(migrated);
}
}
10 changes: 5 additions & 5 deletions packages/flutter_tools/lib/src/xcode_project.dart
Original file line number Diff line number Diff line change
Expand Up @@ -178,15 +178,15 @@ class IosProject extends XcodeBasedProject {

File get appFrameworkInfoPlist => _flutterLibRoot.childDirectory('Flutter').childFile('AppFrameworkInfo.plist');

/// The 'AppDelegate.swift' file of the host app. This file might not exist if the app project uses Objective-C.
File get appDelegateSwift => _editableDirectory.childDirectory('Runner').childFile('AppDelegate.swift');

File get infoPlist => _editableDirectory.childDirectory('Runner').childFile('Info.plist');

Directory get symlinks => _flutterLibRoot.childDirectory('.symlinks');

/// True, if the app project is using swift.
bool get isSwift {
final File appDelegateSwift = _editableDirectory.childDirectory('Runner').childFile('AppDelegate.swift');
return appDelegateSwift.existsSync();
}
/// True if the app project uses Swift.
bool get isSwift => appDelegateSwift.existsSync();

/// Do all plugins support arm64 simulators to run natively on an ARM Mac?
Future<bool> pluginsSupportArmSimulator() async {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Flutter
import UIKit

@UIApplicationMain
@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:flutter_tools/src/ios/migrations/project_base_configuration_migr
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/uiapplicationmain_deprecation_migration.dart';
import 'package:flutter_tools/src/ios/migrations/xcode_build_system_migration.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart';
import 'package:flutter_tools/src/migrations/cocoapods_script_symlink.dart';
Expand Down Expand Up @@ -906,7 +907,7 @@ platform :ios, '12.0'
project,
testLogger,
);
expect(await migration.migrate(), isTrue);
await migration.migrate();
expect(xcodeProjectInfoFile.existsSync(), isFalse);

expect(testLogger.traceText, contains('Xcode project not found, skipping removing bitcode migration'));
Expand All @@ -922,7 +923,7 @@ platform :ios, '12.0'
project,
testLogger,
);
expect(await migration.migrate(), isTrue);
await migration.migrate();

expect(xcodeProjectInfoFile.lastModifiedSync(), projectLastModified);
expect(xcodeProjectInfoFile.readAsStringSync(), xcodeProjectInfoFileContents);
Expand All @@ -943,7 +944,7 @@ platform :ios, '12.0'
project,
testLogger,
);
expect(await migration.migrate(), isTrue);
await migration.migrate();

expect(xcodeProjectInfoFile.readAsStringSync(), '''
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
Expand Down Expand Up @@ -1408,6 +1409,104 @@ LD_RUNPATH_SEARCH_PATHS = $(inherited) /usr/lib/swift '@executable_path/../Frame
expect(testLogger.statusText, contains('Adding input path to Thin Binary build phase.'));
});
});

group('migrate @UIApplicationMain attribute to @main', () {
late MemoryFileSystem memoryFileSystem;
late BufferLogger testLogger;
late FakeIosProject project;
late File appDelegateFile;

setUp(() {
memoryFileSystem = MemoryFileSystem();
testLogger = BufferLogger.test();
project = FakeIosProject();
appDelegateFile = memoryFileSystem.file('AppDelegate.swift');
project.appDelegateSwift = appDelegateFile;
});

testWithoutContext('skipped if files are missing', () async {
final UIApplicationMainDeprecationMigration migration = UIApplicationMainDeprecationMigration(
project,
testLogger,
);
await migration.migrate();
expect(appDelegateFile.existsSync(), isFalse);

expect(testLogger.statusText, isEmpty);
});

testWithoutContext('skipped if nothing to upgrade', () async {
const String appDelegateContents = '''
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
''';
appDelegateFile.writeAsStringSync(appDelegateContents);
final DateTime lastModified = appDelegateFile.lastModifiedSync();

final UIApplicationMainDeprecationMigration migration = UIApplicationMainDeprecationMigration(
project,
testLogger,
);
await migration.migrate();

expect(appDelegateFile.lastModifiedSync(), lastModified);
expect(appDelegateFile.readAsStringSync(), appDelegateContents);

expect(testLogger.statusText, isEmpty);
});

testWithoutContext('updates AppDelegate.swift', () async {
appDelegateFile.writeAsStringSync('''
import Flutter
import UIKit

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
''');

final UIApplicationMainDeprecationMigration migration = UIApplicationMainDeprecationMigration(
project,
testLogger,
);
await migration.migrate();

expect(appDelegateFile.readAsStringSync(), '''
import Flutter
import UIKit

@main
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
''');
expect(testLogger.warningText, contains('uses the deprecated @UIApplicationMain attribute, updating'));
});
});
}

class FakeIosProject extends Fake implements IosProject {
Expand Down Expand Up @@ -1439,6 +1538,9 @@ class FakeIosProject extends Fake implements IosProject {

@override
Directory podRunnerTargetSupportFiles = MemoryFileSystem.test().directory('Pods-Runner');

@override
File appDelegateSwift = MemoryFileSystem.test().file('AppDelegate.swift');
}

class FakeIOSMigrator extends ProjectMigrator {
Expand Down