Skip to content

Commit

Permalink
Switches to UIResponder for App/Scene delegates
Browse files Browse the repository at this point in the history
This is the proper inheritence chain and allows us to use things like overriding the BuildMenu method.

This fixes #4390
  • Loading branch information
Redth committed Jan 28, 2022
1 parent 9c4c0e0 commit ed7460c
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 34 deletions.
2 changes: 1 addition & 1 deletion src/Core/src/MauiContextExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions src/Core/src/Platform/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
18 changes: 9 additions & 9 deletions src/Core/src/Platform/iOS/ApplicationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -25,7 +25,7 @@ public static void RequestNewWindow(this UIApplicationDelegate nativeApplication
err => application.Handler?.MauiContext?.CreateLogger<IApplication>()?.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<NSDictionary>();
Expand All @@ -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<NSDictionary>();
Expand All @@ -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();
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/Core/src/Platform/iOS/ElementExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down
44 changes: 28 additions & 16 deletions src/Core/src/Platform/iOS/MauiUIApplicationDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,23 @@

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;
}

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();

Expand All @@ -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<IApplication>();

Expand All @@ -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<iOSLifecycle.PerformActionForShortcutItem>(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;

Expand All @@ -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;

Expand All @@ -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<iOSLifecycle.OnActivated>(del => del(application));
}

public override void OnResignActivation(UIApplication application)
[Export("applicationWillResignActive:")]
public void OnResignActivation(UIApplication application)
{
Services?.InvokeLifecycleEvents<iOSLifecycle.OnResignActivation>(del => del(application));
}

public override void WillTerminate(UIApplication application)
[Export("applicationWillTerminate:")]
public void WillTerminate(UIApplication application)
{
Services?.InvokeLifecycleEvents<iOSLifecycle.WillTerminate>(del => del(application));
}

public override void DidEnterBackground(UIApplication application)
[Export("applicationDidEnterBackground:")]
public void DidEnterBackground(UIApplication application)
{
Services?.InvokeLifecycleEvents<iOSLifecycle.DidEnterBackground>(del => del(application));
}

public override void WillEnterForeground(UIApplication application)
[Export("applicationWillEnterForeground:")]
public void WillEnterForeground(UIApplication application)
{
Services?.InvokeLifecycleEvents<iOSLifecycle.WillEnterForeground>(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!;

Expand Down
14 changes: 9 additions & 5 deletions src/Core/src/Platform/iOS/MauiUISceneDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<iOSLifecycle.SceneWillConnect>(del => del(scene, session, connectionOptions));

Expand All @@ -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<iOSLifecycle.SceneDidDisconnect>(del => del(scene));
}

public override NSUserActivity? GetStateRestorationActivity(UIScene scene)
[Export("stateRestorationActivityForScene:")]
public NSUserActivity? GetStateRestorationActivity(UIScene scene)
{
var window = Window.GetWindow();
if (window is null)
Expand Down

0 comments on commit ed7460c

Please sign in to comment.