UIScene Migration documentation updates#13045
Conversation
Summary of ChangesHello @LouiseHsu, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request updates the Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Changelog
Activity
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
There was a problem hiding this comment.
Code Review
The pull request updates the UIScene migration guidelines, adding a new table for App Delegate methods and their Scene Delegate equivalents, and providing guidance on migrating deprecated APIs for multi-scene support. The changes improve the clarity and completeness of the migration documentation for Flutter iOS developers.
| | [`applicationDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive) | [`sceneDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197915-scenedidbecomeactive) | | ||
| | [`applicationWillResignActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive) | [`sceneWillResignActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197919-scenewillresignactive) | | ||
| | [`applicationWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground) | [`sceneWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197918-scenewillenterforeground) | | ||
| | [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) | |
There was a problem hiding this comment.
The applicationDidEnterBackground method's Scene Delegate Equivalent has a colon at the end (sceneDidEnterBackground:). This is inconsistent with the other entries in the table and should be removed for clarity and uniformity.
| | [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) | | |
| | [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) | |
There was a problem hiding this comment.
I agree with Gemini, make the colons consistent
| + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { | ||
| - YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] init]; | ||
| + YourFlutterViewProvider *viewProvider = [[YourFlutterViewProvider alloc] initWithRegistrar:registrar]; | ||
| +. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider]; |
There was a problem hiding this comment.
There is an extra period at the beginning of this line. This appears to be a typo and should be removed.
| +. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider]; | |
| YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider]; |
sfshaza2
left a comment
There was a problem hiding this comment.
@LouiseHsu, lgtm, but are you ok with the. minor changes that the bot is requesting? I leave them to you.
|
Visit the preview URL for this PR (updated for commit 6afaaae): https://flutter-docs-prod--pr13045-uiscenedelegate-updates-ubc3v0i3.web.app |
| | [`UIScreen mainScreen`](https://developer.apple.com/documentation/uikit/uiscreen/1617815-mainscreen) | `self.pluginRegistrar.viewController.view.window.windowScene.screen` | | ||
| | [`UIApplication keyWindow`](https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | | ||
| | [`UIApplication windows`](https://developer.apple.com/documentation/uikit/uiapplication/1622975-windows) | `self.pluginRegistrar.viewController.view.window.windowScene.windows` | | ||
| | `UIApplicationDelegate window` | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | |
There was a problem hiding this comment.
| | `UIApplicationDelegate window` | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | | |
| | [`UIApplicationDelegate window`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/window) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | |
| | [`applicationWillResignActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive) | [`sceneWillResignActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197919-scenewillresignactive) | | ||
| | [`applicationWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground) | [`sceneWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197918-scenewillenterforeground) | | ||
| | [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) | | ||
| | [`application:continueUserActivity:restorationHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623072-application) | [`scene:continueUserActivity:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3238053-scene) | |
There was a problem hiding this comment.
The link for scene is not working on this one
| | [`applicationDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622956-applicationdidbecomeactive) | [`sceneDidBecomeActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197915-scenedidbecomeactive) | | ||
| | [`applicationWillResignActive`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive) | [`sceneWillResignActive`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197919-scenewillresignactive) | | ||
| | [`applicationWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623076-applicationwillenterforeground) | [`sceneWillEnterForeground`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197918-scenewillenterforeground) | | ||
| | [`applicationDidEnterBackground`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622997-applicationdidenterbackground) | [`sceneDidEnterBackground:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197917-scenedidenterbackground) | |
There was a problem hiding this comment.
I agree with Gemini, make the colons consistent
| </Tab> | ||
| </Tabs> | ||
|
|
||
| Optionally, while the below methods are not deprecated by UIScene, they should be migrated if you would like to support multiscene in the future. Note that this also entails an extra step of making your pluginRegistrar a variable. |
There was a problem hiding this comment.
I would move this section below step 5 and make it step 6
Perhaps:
6. [Optional] Migrate other deprecated APIs to support multiple scenes in the future.
| First, create a new view provider with an instance method that holds a reference to your `FlutterPluginRegistrar`. | ||
|
|
||
| <Tabs key="ios-language-switcher"> | ||
| <Tab name="Objective-C"> | ||
|
|
||
| ```objc | ||
| @implementation YourFlutterViewProvider | ||
| - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { | ||
| self = [super init]; | ||
| if (self) { | ||
| _registrar = registrar; | ||
| } | ||
| return self; | ||
| } | ||
|
|
||
| - (UIViewController *)viewController { | ||
| return self.registrar.viewController; | ||
| } | ||
| @end | ||
| ``` | ||
|
|
||
| </Tab> | ||
| <Tab name="Swift"> | ||
|
|
||
| ```swift | ||
| final class ViewPresenterProvider: ViewPresenterProvider { | ||
| private let registrar: FlutterPluginRegistrar | ||
|
|
||
| init(registrar: FlutterPluginRegistrar) { | ||
| self.registrar = registrar | ||
| } | ||
|
|
||
| var viewPresenter: ViewPresenter? { | ||
| registrar.viewController | ||
| } | ||
| } | ||
| ``` | ||
| </Tab> | ||
| </Tabs> | ||
|
|
||
| Next, in your plugin’s `registerWithRegistrar` method, initialize your new view provider and pass it into your plugin instance. | ||
|
|
||
| <Tabs key="ios-language-switcher"> | ||
| <Tab name="Objective-C"> | ||
|
|
||
| ```objc diff | ||
| + (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar { | ||
| - YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] init]; | ||
| + YourFlutterViewProvider *viewProvider = [[YourFlutterViewProvider alloc] initWithRegistrar:registrar]; | ||
| +. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider]; | ||
| SetUpFLTImagePickerApi(registrar.messenger, instance); | ||
| } | ||
| ``` | ||
|
|
||
| </Tab> | ||
| <Tab name="Swift"> | ||
|
|
||
| ```swift diff | ||
| public static func register(with registrar: FlutterPluginRegistrar) { | ||
| - let instance = FileSelectorPlugin() | ||
| + let instance = YourFlutterPlugin( | ||
| viewPresenterProvider: ViewPresenterProvider(registrar: registrar)) | ||
| YourFlutterPluginApiSetup.setUp(binaryMessenger: registrar.messenger(), api: instance) | ||
| } | ||
| ``` | ||
| </Tab> | ||
| </Tabs> |
There was a problem hiding this comment.
I think let's simplify this. Wrapping it in a ViewProvider is likely too verbose for an average developer.
I'd say something like:
Instead of accessing these APIs, you can access the windowScene through the viewController. See examples below.
ObjC
@interface MyPlugin ()
+ @property(nonatomic, weak) NSObject<FlutterPluginRegistrar> *registrar;
+ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar;
@end
@implementation MyPlugin
+ - (instancetype)initWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
+ self = [super init];
+ if (self) {
+ _registrar = registrar;
+ }
+ return self;
+ }
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
- MyPlugin *instance = [[MyPlugin alloc] init];
+ MyPlugin *instance = [[MyPlugin alloc] initWithRegistrar:registrar];
}
- (void)someMethod {
- UIScreen *screen = [UIScreen mainScreen];
+ UIScreen *screen = self.registrar.viewController.view.window.windowScene.screen;
- UIWindow *window = [UIApplication sharedApplication].delegate.window;
+ UIWindow *window = self.registrar.viewController.view.window;
- UIWindow *keyWindow = [[UIApplication sharedApplication] keyWindow];
+ if (@available(iOS 15.0, *)) {
+ UIWindow *keyWindow = self.registrar.viewController.view.window.windowScene.keyWindow;
+ } else {
+ for (UIWindow *window in self.registrar.viewController.view.window.windowScene.windows) {
+ if (window.isKeyWindow) {
+ UIWindow *keyWindow = window;
+ }
+ }
+ }
- NSArray<UIWindow *> *windows = [UIApplication sharedApplication].windows;
+ NSArray<UIWindow *> *windows = self.pluginRegistrar.viewController.view.window.windowScene.windows;
}Swift
public class MyPlugin: NSObject, FlutterPlugin {
+ var registrar: FlutterPluginRegistrar
+ init(registrar: FlutterPluginRegistrar) {
+ self.registrar = registrar
+ }
public static func register(with registrar: FlutterPluginRegistrar) {
- let instance = MyPlugin()
+ let instance = MyPlugin(registrar: registrar)
}
func someMethod {
- let screen = UIScreen.main;
+ let screen = self.registrar.viewController?.view.window?.windowScene?.screen;
- let window = UIApplication.shared.delegate?.window;
+ let window = self.registrar.viewController?.view.window;
- let keyWindow = UIApplication.shared.keyWindow;
+ if #available(iOS 15.0, *) {
+ let keyWindow = self.registrar.viewController?.view.window?.windowScene?.keyWindow
+ } else {
+ let keyWindow = self.registrar.viewController?.view.window?.windowScene?.windows.filter({ $0.isKeyWindow }).first
+ }
- let windows = UIApplication.shared.windows;
+ let windows = self.registrar.viewController?.view.window?.windowScene?.windows;
}| | [`UIScreen mainScreen`](https://developer.apple.com/documentation/uikit/uiscreen/1617815-mainscreen) | `self.pluginRegistrar.viewController.view.window.windowScene.screen` | | ||
| | [`UIApplication keyWindow`](https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | | ||
| | [`UIApplication windows`](https://developer.apple.com/documentation/uikit/uiapplication/1622975-windows) | `self.pluginRegistrar.viewController.view.window.windowScene.windows` | | ||
| | [`UIApplicationDelegate window`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/window) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | |
There was a problem hiding this comment.
Instead of using self.pluginRegistrar, let's link to the actual replacement APIs:
https://developer.apple.com/documentation/uikit/uiwindowscene/screen?language=objc
https://developer.apple.com/documentation/uikit/uiwindowscene/keywindow?language=objc
https://developer.apple.com/documentation/uikit/uiwindowscene/windows?language=objc
https://developer.apple.com/documentation/uikit/uiview/window?language=objc
| |:---------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------| | ||
| | [`UIScreen mainScreen`](https://developer.apple.com/documentation/uikit/uiscreen/1617815-mainscreen) | `self.pluginRegistrar.viewController.view.window.windowScene.screen` | | ||
| | [`UIApplication keyWindow`](https://developer.apple.com/documentation/uikit/uiapplication/1622924-keywindow) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` | | ||
| | [`UIApplication windows`](https://developer.apple.com/documentation/uikit/uiapplication/1622975-windows) | `self.pluginRegistrar.viewController.view.window.windowScene.windows` | |
There was a problem hiding this comment.
follow up to #13045 ## Presubmit checklist - [ ] If you are unwilling, or unable, to sign the CLA, even for a _tiny_, one-word PR, please file an issue instead of a PR. - [ ] If this PR is not meant to land until a future stable release, mark it as draft with an explanation. - [x] This PR follows the [Google Developer Documentation Style Guidelines](https://developers.google.com/style)—for example, it doesn't use _i.e._ or _e.g._, and it avoids _I_ and _we_ (first-person pronouns). - [x] This PR uses [semantic line breaks](https://github.com/dart-lang/site-shared/blob/main/doc/writing-for-dart-and-flutter-websites.md#semantic-line-breaks) of 80 characters or fewer. --------- Co-authored-by: Victoria Ashworth <15619084+vashworth@users.noreply.github.com>
Update UIScene migration guidelines.
Presubmit checklist
of 80 characters or fewer.