From ed7460ce52bf7f6004563f4af240526dab7565f2 Mon Sep 17 00:00:00 2001 From: Redth Date: Fri, 28 Jan 2022 16:39:06 -0500 Subject: [PATCH 1/4] Switches to UIResponder for App/Scene delegates This is the proper inheritence chain and allows us to use things like overriding the BuildMenu method. This fixes #4390 --- src/Core/src/MauiContextExtensions.cs | 2 +- src/Core/src/Platform/ElementExtensions.cs | 4 +- .../src/Platform/iOS/ApplicationExtensions.cs | 18 ++++---- .../src/Platform/iOS/ElementExtensions.cs | 2 +- .../Platform/iOS/MauiUIApplicationDelegate.cs | 44 ++++++++++++------- .../src/Platform/iOS/MauiUISceneDelegate.cs | 14 +++--- 6 files changed, 50 insertions(+), 34 deletions(-) diff --git a/src/Core/src/MauiContextExtensions.cs b/src/Core/src/MauiContextExtensions.cs index aa1c22b66a5d..c36b32b6b5da 100644 --- a/src/Core/src/MauiContextExtensions.cs +++ b/src/Core/src/MauiContextExtensions.cs @@ -7,7 +7,7 @@ using NativeApplication = Microsoft.UI.Xaml.Application; using NativeWindow = Microsoft.UI.Xaml.Window; #elif __IOS__ || __MACCATALYST__ -using NativeApplication = UIKit.UIApplicationDelegate; +using NativeApplication = UIKit.IUIApplicationDelegate; using NativeWindow = UIKit.UIWindow; #elif __ANDROID__ using NativeApplication = Android.App.Application; diff --git a/src/Core/src/Platform/ElementExtensions.cs b/src/Core/src/Platform/ElementExtensions.cs index 1f55de97dac9..7723cb925b26 100644 --- a/src/Core/src/Platform/ElementExtensions.cs +++ b/src/Core/src/Platform/ElementExtensions.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; #if __IOS__ || MACCATALYST using NativeView = UIKit.UIView; -using BasePlatformType = Foundation.NSObject; +using BasePlatformType = ObjCRuntime.INativeObject; using PlatformWindow = UIKit.UIWindow; -using PlatformApplication = UIKit.UIApplicationDelegate; +using PlatformApplication = UIKit.IUIApplicationDelegate; #elif MONOANDROID using NativeView = Android.Views.View; using BasePlatformType = Android.Content.Context; diff --git a/src/Core/src/Platform/iOS/ApplicationExtensions.cs b/src/Core/src/Platform/iOS/ApplicationExtensions.cs index fb36a91e91aa..ab3c39cefbf4 100644 --- a/src/Core/src/Platform/iOS/ApplicationExtensions.cs +++ b/src/Core/src/Platform/iOS/ApplicationExtensions.cs @@ -10,7 +10,7 @@ namespace Microsoft.Maui.Platform { public static class ApplicationExtensions { - public static void RequestNewWindow(this UIApplicationDelegate nativeApplication, IApplication application, OpenWindowRequest? args) + public static void RequestNewWindow(this IUIApplicationDelegate nativeApplication, IApplication application, OpenWindowRequest? args) { if (application.Handler?.MauiContext is not IMauiContext applicationContext || args is null) return; @@ -25,7 +25,7 @@ public static void RequestNewWindow(this UIApplicationDelegate nativeApplication err => application.Handler?.MauiContext?.CreateLogger()?.LogError(new NSErrorException(err), err.Description)); } - public static void CreateNativeWindow(this UIApplicationDelegate nativeApplication, IApplication application, UIApplication uiApplication, NSDictionary launchOptions) + public static void CreateNativeWindow(this IUIApplicationDelegate nativeApplication, IApplication application, UIApplication uiApplication, NSDictionary launchOptions) { // Find any userinfo/dictionaries we might pass into the activation state var dicts = new List(); @@ -35,14 +35,14 @@ public static void CreateNativeWindow(this UIApplicationDelegate nativeApplicati dicts.Add(launchOptions); var window = CreateNativeWindow(application, null, dicts.ToArray()); - if (window is not null) + if (window is not null && nativeApplication is UIApplicationDelegate nativeAppDelegate) { - nativeApplication.Window = window; - nativeApplication.Window.MakeKeyAndVisible(); + nativeAppDelegate.Window = window; + nativeAppDelegate.Window?.MakeKeyAndVisible(); } } - public static void CreateNativeWindow(this UIWindowSceneDelegate sceneDelegate, IApplication application, UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions) + public static void CreateNativeWindow(this IUIWindowSceneDelegate sceneDelegate, IApplication application, UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions) { // Find any userinfo/dictionaries we might pass into the activation state var dicts = new List(); @@ -62,10 +62,10 @@ public static void CreateNativeWindow(this UIWindowSceneDelegate sceneDelegate, } var window = CreateNativeWindow(application, scene as UIWindowScene, dicts.ToArray()); - if (window is not null) + if (window is not null && sceneDelegate is UIWindowSceneDelegate windowSceneDelegate) { - sceneDelegate.Window = window; - sceneDelegate.Window.MakeKeyAndVisible(); + windowSceneDelegate.Window = window; + windowSceneDelegate.Window?.MakeKeyAndVisible(); } } diff --git a/src/Core/src/Platform/iOS/ElementExtensions.cs b/src/Core/src/Platform/iOS/ElementExtensions.cs index aa66185c39c8..16b8543a8233 100644 --- a/src/Core/src/Platform/iOS/ElementExtensions.cs +++ b/src/Core/src/Platform/iOS/ElementExtensions.cs @@ -20,7 +20,7 @@ public static UIViewController ToUIViewController(this IElement view, IMauiConte // If < iOS 13 or the Info.plist does not have a scene manifest entry we need to assume no multi window, and no UISceneDelegate. // We cannot check for iPads/Mac because even on the iPhone it uses the scene delegate if one is specified in the manifest. - public static bool HasSceneManifest(this UIApplicationDelegate nativeApplication) => + public static bool HasSceneManifest(this IUIApplicationDelegate nativeApplication) => UIDevice.CurrentDevice.CheckSystemVersion(13, 0) && NSBundle.MainBundle.InfoDictionary.ContainsKey(new NSString(UIApplicationSceneManifestKey)); } diff --git a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs index 46393c289566..06741a1f40e6 100644 --- a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs @@ -8,14 +8,14 @@ namespace Microsoft.Maui { - public abstract class MauiUIApplicationDelegate : UIApplicationDelegate, IUIApplicationDelegate, IPlatformApplication + public abstract class MauiUIApplicationDelegate : UIResponder, IUIApplicationDelegate, IPlatformApplication { internal const string MauiSceneConfigurationKey = "__MAUI_DEFAULT_SCENE_CONFIGURATION__"; internal const string GetConfigurationSelectorName = "application:configurationForConnectingSceneSession:options:"; IMauiContext _applicationContext = null!; - protected MauiUIApplicationDelegate() + protected MauiUIApplicationDelegate() : base() { Current = this; IPlatformApplication.Current = this; @@ -23,7 +23,8 @@ protected MauiUIApplicationDelegate() protected abstract MauiApp CreateMauiApp(); - public override bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) + [Export("application:willFinishLaunchingWithOptions:")] + public bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) { var mauiApp = CreateMauiApp(); @@ -38,7 +39,8 @@ public override bool WillFinishLaunching(UIApplication application, NSDictionary return true; } - public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) + [Export("application:didFinishLaunchingWithOptions:")] + public bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Application = Services.GetRequiredService(); @@ -62,15 +64,18 @@ public override bool RespondsToSelector(Selector? sel) return base.RespondsToSelector(sel); } - public override UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options) + [Export("application:configurationForConnectingSceneSession:options:")] + public UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options) => new(MauiUIApplicationDelegate.MauiSceneConfigurationKey, connectingSceneSession.Role); - public override void PerformActionForShortcutItem(UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler) + [Export("application:performActionForShortcutItem:completionHandler:")] + public void PerformActionForShortcutItem(UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler) { Services?.InvokeLifecycleEvents(del => del(application, shortcutItem, completionHandler)); } - public override bool OpenUrl(UIApplication application, NSUrl url, NSDictionary options) + [Export("application:openURL:options:")] + public bool OpenUrl(UIApplication application, NSUrl url, NSDictionary options) { var wasHandled = false; @@ -79,10 +84,11 @@ public override bool OpenUrl(UIApplication application, NSUrl url, NSDictionary wasHandled = del(application, url, options) || wasHandled; }); - return wasHandled || base.OpenUrl(application, url, options); + return wasHandled; } - public override bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler) + [Export("application:continueUserActivity:restorationHandler:")] + public bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler) { var wasHandled = false; @@ -91,37 +97,43 @@ public override bool ContinueUserActivity(UIApplication application, NSUserActiv wasHandled = del(application, userActivity, completionHandler) || wasHandled; }); - return wasHandled || base.ContinueUserActivity(application, userActivity, completionHandler); + return wasHandled; } - public override void OnActivated(UIApplication application) + [Export("applicationDidBecomeActive:")] + public void OnActivated(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } - public override void OnResignActivation(UIApplication application) + [Export("applicationWillResignActive:")] + public void OnResignActivation(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } - public override void WillTerminate(UIApplication application) + [Export("applicationWillTerminate:")] + public void WillTerminate(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } - public override void DidEnterBackground(UIApplication application) + [Export("applicationDidEnterBackground:")] + public void DidEnterBackground(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } - public override void WillEnterForeground(UIApplication application) + [Export("applicationWillEnterForeground:")] + public void WillEnterForeground(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } public static MauiUIApplicationDelegate Current { get; private set; } = null!; - public override UIWindow? Window { get; set; } + [Export("window")] + public UIWindow? Window { get; set; } public IServiceProvider Services { get; protected set; } = null!; diff --git a/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs b/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs index d462eb16318a..9c2aa65d1b35 100644 --- a/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs @@ -6,11 +6,13 @@ namespace Microsoft.Maui { - public class MauiUISceneDelegate : UIWindowSceneDelegate + public class MauiUISceneDelegate : UIResponder, IUIWindowSceneDelegate { - public override UIWindow? Window { get; set; } + [Export("window")] + public UIWindow? Window { get; set; } - public override void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions) + [Export("scene:willConnectToSession:options:")] + public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions) { MauiUIApplicationDelegate.Current?.Services?.InvokeLifecycleEvents(del => del(scene, session, connectionOptions)); @@ -20,12 +22,14 @@ public override void WillConnect(UIScene scene, UISceneSession session, UISceneC } } - public override void DidDisconnect(UIScene scene) + [Export("sceneDidDisconnect:")] + public void DidDisconnect(UIScene scene) { MauiUIApplicationDelegate.Current?.Services?.InvokeLifecycleEvents(del => del(scene)); } - public override NSUserActivity? GetStateRestorationActivity(UIScene scene) + [Export("stateRestorationActivityForScene:")] + public NSUserActivity? GetStateRestorationActivity(UIScene scene) { var window = Window.GetWindow(); if (window is null) From 04aa0218d1b3b552734a170ebe07d796d66e97bf Mon Sep 17 00:00:00 2001 From: redth Date: Fri, 28 Jan 2022 17:19:21 -0500 Subject: [PATCH 2/4] Fix window property calls --- src/Core/src/Platform/iOS/ApplicationExtensions.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Core/src/Platform/iOS/ApplicationExtensions.cs b/src/Core/src/Platform/iOS/ApplicationExtensions.cs index ab3c39cefbf4..0e79c8cf61e7 100644 --- a/src/Core/src/Platform/iOS/ApplicationExtensions.cs +++ b/src/Core/src/Platform/iOS/ApplicationExtensions.cs @@ -35,10 +35,10 @@ public static void CreateNativeWindow(this IUIApplicationDelegate nativeApplicat dicts.Add(launchOptions); var window = CreateNativeWindow(application, null, dicts.ToArray()); - if (window is not null && nativeApplication is UIApplicationDelegate nativeAppDelegate) + if (window is not null) { - nativeAppDelegate.Window = window; - nativeAppDelegate.Window?.MakeKeyAndVisible(); + nativeApplication.SetWindow(window); + nativeApplication.GetWindow()?.MakeKeyAndVisible(); } } @@ -62,10 +62,10 @@ public static void CreateNativeWindow(this IUIWindowSceneDelegate sceneDelegate, } var window = CreateNativeWindow(application, scene as UIWindowScene, dicts.ToArray()); - if (window is not null && sceneDelegate is UIWindowSceneDelegate windowSceneDelegate) + if (window is not null) { - windowSceneDelegate.Window = window; - windowSceneDelegate.Window?.MakeKeyAndVisible(); + sceneDelegate.SetWindow(window); + sceneDelegate.GetWindow()?.MakeKeyAndVisible(); } } From c52fb69dd080411b6e2656b3e6bdfc7bffe50cca Mon Sep 17 00:00:00 2001 From: redth Date: Sat, 29 Jan 2022 14:12:44 -0500 Subject: [PATCH 3/4] Make protocol implementations virtual --- .../Platform/iOS/MauiUIApplicationDelegate.cs | 24 +++++++++---------- .../src/Platform/iOS/MauiUISceneDelegate.cs | 8 +++---- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs index 06741a1f40e6..ee24100b9a89 100644 --- a/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs @@ -24,7 +24,7 @@ protected MauiUIApplicationDelegate() : base() protected abstract MauiApp CreateMauiApp(); [Export("application:willFinishLaunchingWithOptions:")] - public bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) + public virtual bool WillFinishLaunching(UIApplication application, NSDictionary launchOptions) { var mauiApp = CreateMauiApp(); @@ -40,7 +40,7 @@ public bool WillFinishLaunching(UIApplication application, NSDictionary launchOp } [Export("application:didFinishLaunchingWithOptions:")] - public bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) + public virtual bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { Application = Services.GetRequiredService(); @@ -65,17 +65,17 @@ public override bool RespondsToSelector(Selector? sel) } [Export("application:configurationForConnectingSceneSession:options:")] - public UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options) + public virtual UISceneConfiguration GetConfiguration(UIApplication application, UISceneSession connectingSceneSession, UISceneConnectionOptions options) => new(MauiUIApplicationDelegate.MauiSceneConfigurationKey, connectingSceneSession.Role); [Export("application:performActionForShortcutItem:completionHandler:")] - public void PerformActionForShortcutItem(UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler) + public virtual void PerformActionForShortcutItem(UIApplication application, UIApplicationShortcutItem shortcutItem, UIOperationHandler completionHandler) { Services?.InvokeLifecycleEvents(del => del(application, shortcutItem, completionHandler)); } [Export("application:openURL:options:")] - public bool OpenUrl(UIApplication application, NSUrl url, NSDictionary options) + public virtual bool OpenUrl(UIApplication application, NSUrl url, NSDictionary options) { var wasHandled = false; @@ -88,7 +88,7 @@ public bool OpenUrl(UIApplication application, NSUrl url, NSDictionary options) } [Export("application:continueUserActivity:restorationHandler:")] - public bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler) + public virtual bool ContinueUserActivity(UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler) { var wasHandled = false; @@ -101,31 +101,31 @@ public bool ContinueUserActivity(UIApplication application, NSUserActivity userA } [Export("applicationDidBecomeActive:")] - public void OnActivated(UIApplication application) + public virtual void OnActivated(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } [Export("applicationWillResignActive:")] - public void OnResignActivation(UIApplication application) + public virtual void OnResignActivation(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } [Export("applicationWillTerminate:")] - public void WillTerminate(UIApplication application) + public virtual void WillTerminate(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } [Export("applicationDidEnterBackground:")] - public void DidEnterBackground(UIApplication application) + public virtual void DidEnterBackground(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } [Export("applicationWillEnterForeground:")] - public void WillEnterForeground(UIApplication application) + public virtual void WillEnterForeground(UIApplication application) { Services?.InvokeLifecycleEvents(del => del(application)); } @@ -133,7 +133,7 @@ public void WillEnterForeground(UIApplication application) public static MauiUIApplicationDelegate Current { get; private set; } = null!; [Export("window")] - public UIWindow? Window { get; set; } + public virtual UIWindow? Window { get; set; } public IServiceProvider Services { get; protected set; } = null!; diff --git a/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs b/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs index 9c2aa65d1b35..16f58b7cf660 100644 --- a/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs +++ b/src/Core/src/Platform/iOS/MauiUISceneDelegate.cs @@ -9,10 +9,10 @@ namespace Microsoft.Maui public class MauiUISceneDelegate : UIResponder, IUIWindowSceneDelegate { [Export("window")] - public UIWindow? Window { get; set; } + public virtual UIWindow? Window { get; set; } [Export("scene:willConnectToSession:options:")] - public void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions) + public virtual void WillConnect(UIScene scene, UISceneSession session, UISceneConnectionOptions connectionOptions) { MauiUIApplicationDelegate.Current?.Services?.InvokeLifecycleEvents(del => del(scene, session, connectionOptions)); @@ -23,13 +23,13 @@ public void WillConnect(UIScene scene, UISceneSession session, UISceneConnection } [Export("sceneDidDisconnect:")] - public void DidDisconnect(UIScene scene) + public virtual void DidDisconnect(UIScene scene) { MauiUIApplicationDelegate.Current?.Services?.InvokeLifecycleEvents(del => del(scene)); } [Export("stateRestorationActivityForScene:")] - public NSUserActivity? GetStateRestorationActivity(UIScene scene) + public virtual NSUserActivity? GetStateRestorationActivity(UIScene scene) { var window = Window.GetWindow(); if (window is null) From 1b53964b820a0ae8d0c79318b5b74c2aedf1800d Mon Sep 17 00:00:00 2001 From: redth Date: Sat, 29 Jan 2022 14:18:58 -0500 Subject: [PATCH 4/4] Fix handler to be interface --- src/Core/src/Handlers/Application/ApplicationHandler.cs | 2 +- src/Core/src/Handlers/Application/ApplicationHandler.iOS.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Core/src/Handlers/Application/ApplicationHandler.cs b/src/Core/src/Handlers/Application/ApplicationHandler.cs index 89ce35ff89dc..fa040cb006da 100644 --- a/src/Core/src/Handlers/Application/ApplicationHandler.cs +++ b/src/Core/src/Handlers/Application/ApplicationHandler.cs @@ -2,7 +2,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; #if __IOS__ || MACCATALYST -using NativeView = UIKit.UIApplicationDelegate; +using NativeView = UIKit.IUIApplicationDelegate; #elif MONOANDROID using NativeView = Android.App.Application; #elif WINDOWS diff --git a/src/Core/src/Handlers/Application/ApplicationHandler.iOS.cs b/src/Core/src/Handlers/Application/ApplicationHandler.iOS.cs index 2ad5099c42da..36accc4c5dd1 100644 --- a/src/Core/src/Handlers/Application/ApplicationHandler.iOS.cs +++ b/src/Core/src/Handlers/Application/ApplicationHandler.iOS.cs @@ -7,7 +7,7 @@ namespace Microsoft.Maui.Handlers { - public partial class ApplicationHandler : ElementHandler + public partial class ApplicationHandler : ElementHandler { public static void MapTerminate(ApplicationHandler handler, IApplication application, object? args) {