Skip to content
Merged
Changes from 3 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
92 changes: 92 additions & 0 deletions src/content/release/breaking-changes/uiscenedelegate.md
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,19 @@ event, visit Apple's documentation on
[`UISceneDelegate`]: {{site.apple-dev}}/documentation/uikit/uiscenedelegate
[`UIWindowSceneDelegate`]: {{site.apple-dev}}/documentation/uikit/uiwindowscenedelegate

| App Delegate Method | Scene Delegate Equivalent |
| :--- |:-----------------------------------------------------------------------------------------------------------------------------------------------------------|
| [`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) |
| [`application:continueUserActivity:restorationHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623072-application) | [`scene:continueUserActivity:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/scene(_:continue:)) |
| [`application:performActionForShortcutItem:completionHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/application(_:performactionfor:completionhandler:)) | [`windowScene:performActionForShortcutItem:completionHandler:`](https://developer.apple.com/documentation/uikit/uiwindowscenedelegate/3238088-windowscene) |
| [`application:openURL:options:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623112-application) | [`scene:openURLContexts:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3238059-scene) |
| [`application:performFetchWithCompletionHandler:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623125-application) | [`BGAppRefreshTask`](https://developer.apple.com/documentation/backgroundtasks/bgapprefreshtask) |
| [`application:willFinishLaunchingWithOptions:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623032-application) | [`scene:willConnectToSession:options:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197914-scene) |
| [`application:didFinishLaunchingWithOptions:`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622921-application) | [`scene:willConnectToSession:options:`](https://developer.apple.com/documentation/uikit/uiscenedelegate/3197914-scene) |

<Tabs key="ios-language-switcher">
<Tab name="Swift">

Expand Down Expand Up @@ -788,6 +801,85 @@ migrating to the `UIScene` lifecycle, the launch options will be `nil`. Any logi
performed here related to the launch options should be moved to the
`scene:willConnectToSession:options:` event.


6. [Optional] Migrate other deprecated APIs to support multiple scenes in the future.

| Deprecated API | UIScene Replacement |
|:---------------------------------------------------------------------------------------------------------------|:------------------------------------------------------------------------|
| [`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` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

| [`UIApplicationDelegate window`](https://developer.apple.com/documentation/uikit/uiapplicationdelegate/window) | `self.pluginRegistrar.viewController.view.window.windowScene.keyWindow` |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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


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];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

There is an extra period at the beginning of this line. This appears to be a typo and should be removed.

Suggested change
+. YourFlutterViewPlugin *instance = [[YourFlutterViewPlugin alloc] initWithViewProvider:viewProvider];
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>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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;

  }

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

ah i see, okay.



## Bespoke FlutterViewController usage

For apps that use a `FlutterViewController` instantiated from Storyboards in
Expand Down
Loading