From c8a96302f0002dadf9a12fbb3184c35d2f1312d5 Mon Sep 17 00:00:00 2001 From: louisehsu Date: Wed, 2 Sep 2026 12:23:12 -0700 Subject: [PATCH] Migrate custom standalone samples and app clip to UIScene lifecycle --- google_maps/ios/Runner/AppDelegate.swift | 12 +- google_maps/ios/Runner/Info.plist | 21 ++ ios_app_clip/ios/AppClip/Info.plist | 21 ++ ios_app_clip/ios/Runner/AppDelegate.swift | 9 +- ios_app_clip/ios/Runner/Info.plist | 21 ++ .../ios/Runner/AppDelegate.swift | 179 ++++++++++-------- platform_channels/ios/Runner/Info.plist | 21 ++ .../ios/Runner/AppDelegate.swift | 70 ++++--- platform_view_swift/ios/Runner/Info.plist | 25 +++ 9 files changed, 265 insertions(+), 114 deletions(-) diff --git a/google_maps/ios/Runner/AppDelegate.swift b/google_maps/ios/Runner/AppDelegate.swift index 6861f5a96..31ef6679d 100644 --- a/google_maps/ios/Runner/AppDelegate.swift +++ b/google_maps/ios/Runner/AppDelegate.swift @@ -1,18 +1,20 @@ -import UIKit import Flutter import GoogleMaps +import UIKit -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - // TODO: Add your Google Maps API key GMSServices.provideAPIKey("YOUR-API-KEY") return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } } diff --git a/google_maps/ios/Runner/Info.plist b/google_maps/ios/Runner/Info.plist index 3d7ffa12c..38aa622b3 100644 --- a/google_maps/ios/Runner/Info.plist +++ b/google_maps/ios/Runner/Info.plist @@ -24,6 +24,27 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/ios_app_clip/ios/AppClip/Info.plist b/ios_app_clip/ios/AppClip/Info.plist index 9d1cf2299..6c9cf1857 100644 --- a/ios_app_clip/ios/AppClip/Info.plist +++ b/ios_app_clip/ios/AppClip/Info.plist @@ -22,6 +22,27 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + NSAppClip NSAppClipRequestEphemeralUserNotification diff --git a/ios_app_clip/ios/Runner/AppDelegate.swift b/ios_app_clip/ios/Runner/AppDelegate.swift index b63630348..c30b367ec 100644 --- a/ios_app_clip/ios/Runner/AppDelegate.swift +++ b/ios_app_clip/ios/Runner/AppDelegate.swift @@ -1,13 +1,16 @@ -import UIKit import Flutter +import UIKit @main -@objc class AppDelegate: FlutterAppDelegate { +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + } } diff --git a/ios_app_clip/ios/Runner/Info.plist b/ios_app_clip/ios/Runner/Info.plist index 6ba20555e..5b591c710 100644 --- a/ios_app_clip/ios/Runner/Info.plist +++ b/ios_app_clip/ios/Runner/Info.plist @@ -24,6 +24,27 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + UIApplicationSupportsIndirectInputEvents UILaunchStoryboardName diff --git a/platform_channels/ios/Runner/AppDelegate.swift b/platform_channels/ios/Runner/AppDelegate.swift index 131fd18b7..f40bb6430 100644 --- a/platform_channels/ios/Runner/AppDelegate.swift +++ b/platform_channels/ios/Runner/AppDelegate.swift @@ -2,88 +2,111 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import UIKit import Flutter +import UIKit + +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + + let messenger = engineBridge.applicationRegistrar.messenger() -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate { - override func application( - _ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? - ) -> Bool { - let flutterViewController = window.rootViewController as! FlutterViewController - FlutterMethodChannel(name: "methodChannelDemo", binaryMessenger: flutterViewController.binaryMessenger).setMethodCallHandler({ - (call: FlutterMethodCall, result: FlutterResult) -> Void in - - guard let count = (call.arguments as? NSDictionary)?["count"] as? Int else { - result(FlutterError(code: "INVALID_ARGUMENT", message: "Value of count cannot be null", details: nil)) - return - } - - switch call.method { - case "increment": - result(count + 1) - case "decrement": - result(count - 1) - default: - result(FlutterMethodNotImplemented) - } - }) - - FlutterBasicMessageChannel(name: "platformImageDemo", binaryMessenger: flutterViewController.binaryMessenger, codec: FlutterStandardMessageCodec.sharedInstance()).setMessageHandler{ - (message: Any?, reply: FlutterReply) -> Void in - - if(message as! String == "getImage") { - guard let image = UIImage(named: "eat_new_orleans.jpg") else { - reply(nil) - return - } - - reply(FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 1)!)) - } + FlutterMethodChannel(name: "methodChannelDemo", binaryMessenger: messenger) + .setMethodCallHandler({ (call: FlutterMethodCall, result: FlutterResult) -> Void in + guard let count = (call.arguments as? NSDictionary)?["count"] as? Int else { + result( + FlutterError( + code: "INVALID_ARGUMENT", + message: "Value of count cannot be null", + details: nil + ) + ) + return } - - FlutterEventChannel(name: "eventChannelDemo", binaryMessenger: flutterViewController.binaryMessenger).setStreamHandler(AccelerometerStreamHandler()) - - var petList : [[String:String]] = [] - - // A FlutterBasicMessageChannel for sending petList to Dart. - let stringCodecChannel = FlutterBasicMessageChannel(name: "stringCodecDemo", binaryMessenger: flutterViewController.binaryMessenger, codec: FlutterStringCodec.sharedInstance()) - - // Registers a MessageHandler for FlutterBasicMessageChannel to receive pet details. - FlutterBasicMessageChannel(name: "jsonMessageCodecDemo", binaryMessenger: flutterViewController.binaryMessenger, codec: FlutterJSONMessageCodec.sharedInstance()) - .setMessageHandler{(message: Any?, reply: FlutterReply) -> Void in - petList.insert(message! as! [String: String], at: 0) - stringCodecChannel.sendMessage(self.convertPetListToJson(petList: petList)) - } - - // Registers a MessageHandler for FlutterBasicMessageHandler to receive indices of detail records to remove from the petList. - FlutterBasicMessageChannel(name: "binaryCodecDemo", binaryMessenger: flutterViewController.binaryMessenger, codec: FlutterBinaryCodec.sharedInstance()).setMessageHandler{ - (message: Any?, reply: FlutterReply) -> Void in - - guard let index = Int.init(String.init(data: message! as! Data, encoding: String.Encoding.utf8)!) else { - reply(nil) - return - } - - if (index >= 0 && index < petList.count) { - petList.remove(at: index) - reply("Removed Successfully".data(using: .utf8)!) - stringCodecChannel.sendMessage(self.convertPetListToJson(petList: petList)) - } else { - reply(nil) - } + + switch call.method { + case "increment": + result(count + 1) + case "decrement": + result(count - 1) + default: + result(FlutterMethodNotImplemented) } - - GeneratedPluginRegistrant.register(with: self) - return super.application(application, didFinishLaunchingWithOptions: launchOptions) - } - - // Function to convert petList to json string. - func convertPetListToJson(petList: [[String: String]]) -> String? { - guard let data = try? JSONSerialization.data(withJSONObject: ["petList": petList], options: .prettyPrinted) else { - return nil + }) + + FlutterBasicMessageChannel( + name: "platformImageDemo", + binaryMessenger: messenger, + codec: FlutterStandardMessageCodec.sharedInstance() + ).setMessageHandler { (message: Any?, reply: FlutterReply) -> Void in + if message as! String == "getImage" { + guard let image = UIImage(named: "eat_new_orleans.jpg") else { + reply(nil) + return } - return String(data: data, encoding: String.Encoding.utf8) + + reply(FlutterStandardTypedData(bytes: image.jpegData(compressionQuality: 1)!)) + } + } + + FlutterEventChannel( + name: "eventChannelDemo", + binaryMessenger: messenger + ).setStreamHandler(AccelerometerStreamHandler()) + + var petList: [[String: String]] = [] + + // A FlutterBasicMessageChannel for sending petList to Dart. + let stringCodecChannel = FlutterBasicMessageChannel( + name: "stringCodecDemo", + binaryMessenger: messenger, + codec: FlutterStringCodec.sharedInstance() + ) + + // Registers a MessageHandler for FlutterBasicMessageChannel to receive pet details. + FlutterBasicMessageChannel( + name: "jsonMessageCodecDemo", + binaryMessenger: messenger, + codec: FlutterJSONMessageCodec.sharedInstance() + ).setMessageHandler { (message: Any?, reply: FlutterReply) -> Void in + petList.insert(message! as! [String: String], at: 0) + stringCodecChannel.sendMessage(self.convertPetListToJson(petList: petList)) + } + + // Registers a MessageHandler for FlutterBasicMessageHandler to receive indices of detail records to remove from the petList. + FlutterBasicMessageChannel( + name: "binaryCodecDemo", + binaryMessenger: messenger, + codec: FlutterBinaryCodec.sharedInstance() + ).setMessageHandler { (message: Any?, reply: FlutterReply) -> Void in + guard let index = Int(String(data: message! as! Data, encoding: String.Encoding.utf8)!) else { + reply(nil) + return + } + + if index >= 0 && index < petList.count { + petList.remove(at: index) + reply("Removed Successfully".data(using: .utf8)!) + stringCodecChannel.sendMessage(self.convertPetListToJson(petList: petList)) + } else { + reply(nil) + } + } + } + + // Function to convert petList to json string. + func convertPetListToJson(petList: [[String: String]]) -> String? { + guard let data = try? JSONSerialization.data(withJSONObject: ["petList": petList], options: .prettyPrinted) else { + return nil } + return String(data: data, encoding: String.Encoding.utf8) + } } diff --git a/platform_channels/ios/Runner/Info.plist b/platform_channels/ios/Runner/Info.plist index 2557472fd..fd3fb7f48 100644 --- a/platform_channels/ios/Runner/Info.plist +++ b/platform_channels/ios/Runner/Info.plist @@ -24,6 +24,27 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/platform_view_swift/ios/Runner/AppDelegate.swift b/platform_view_swift/ios/Runner/AppDelegate.swift index 630e34447..f3028e4b3 100644 --- a/platform_view_swift/ios/Runner/AppDelegate.swift +++ b/platform_view_swift/ios/Runner/AppDelegate.swift @@ -2,42 +2,56 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import UIKit import Flutter +import UIKit -@UIApplicationMain -@objc class AppDelegate: FlutterAppDelegate, PlatformViewControllerDelegate { +@main +@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate, PlatformViewControllerDelegate { var flutterResult: FlutterResult? override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { - GeneratedPluginRegistrant.register(with: self) - let controller: FlutterViewController = window?.rootViewController as! FlutterViewController - let channel = FlutterMethodChannel.init(name: "dev.flutter.sample/platform_view_swift", binaryMessenger: controller.binaryMessenger) - - channel.setMethodCallHandler({ - (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in - if ("switchView" == call.method) { - self.flutterResult = result - - let platformViewController = PlatformViewController(nibName: "PlatformViewController", bundle: nil) - platformViewController.counter = call.arguments as! Int - platformViewController.delegate = self - - let navigationController = UINavigationController(rootViewController: platformViewController) - navigationController.navigationBar.topItem?.title = "Platform View" - controller.present(navigationController, animated: true, completion: nil) - } else { - result(FlutterMethodNotImplemented) - } - }); - return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - - func didUpdateCounter(counter: Int) { - flutterResult?(counter) - } + + func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) { + GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry) + + let registrar = engineBridge.pluginRegistry.registrar(forPlugin: "PlatformViewSwift") + let channel = FlutterMethodChannel( + name: "dev.flutter.sample/platform_view_swift", + binaryMessenger: engineBridge.applicationRegistrar.messenger() + ) + + channel.setMethodCallHandler({ + [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in + guard let self = self else { return } + if "switchView" == call.method { + self.flutterResult = result + + let platformViewController = PlatformViewController(nibName: "PlatformViewController", bundle: nil) + platformViewController.counter = call.arguments as! Int + platformViewController.delegate = self + + let navigationController = UINavigationController(rootViewController: platformViewController) + navigationController.navigationBar.topItem?.title = "Platform View" + + let presenter = registrar?.viewController ?? UIApplication.shared.connectedScenes + .compactMap { $0 as? UIWindowScene } + .flatMap { $0.windows } + .first { $0.isKeyWindow }? + .rootViewController + + presenter?.present(navigationController, animated: true, completion: nil) + } else { + result(FlutterMethodNotImplemented) + } + }) + } + + func didUpdateCounter(counter: Int) { + flutterResult?(counter) + } } diff --git a/platform_view_swift/ios/Runner/Info.plist b/platform_view_swift/ios/Runner/Info.plist index 83cff7fe1..e15b917b9 100644 --- a/platform_view_swift/ios/Runner/Info.plist +++ b/platform_view_swift/ios/Runner/Info.plist @@ -22,6 +22,27 @@ $(FLUTTER_BUILD_NUMBER) LSRequiresIPhoneOS + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneClassName + UIWindowScene + UISceneConfigurationName + flutter + UISceneDelegateClassName + FlutterSceneDelegate + UISceneStoryboardFile + Main + + + + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -41,5 +62,9 @@ UIViewControllerBasedStatusBarAppearance + CADisableMinimumFrameDurationOnPhone + + UIApplicationSupportsIndirectInputEvents +