Fixes for multi-window support. (#5528)

* Correctly handle the re-opening of the main window.

Add an additional safe-guard to ensure only one main window exists.
Make sure all secondary windows use the correct tint colour.

* Fix a bug where the settings screen isn't shown on macOS when the AppLock feature is enabled.
This commit is contained in:
Doug
2026-05-01 14:44:04 +01:00
committed by GitHub
parent c1184b748d
commit 9015c3e483
11 changed files with 77 additions and 17 deletions
@@ -14,8 +14,18 @@ import SwiftUI
class SceneDelegate: NSObject, UIWindowSceneDelegate {
weak static var windowManager: SecureWindowManagerProtocol!
/// The app's main window scene identifier.
static let mainSceneID = "Main"
/// The user info key used by SwiftUI for a `WindowGroup`s `id` parameter.
static let sceneIDKey = "com.apple.SwiftUI.sceneID"
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = scene as? UIWindowScene else { return }
Self.windowManager.configure(withScene: windowScene, session: session)
}
func sceneDidDisconnect(_ scene: UIScene) {
guard let windowScene = scene as? UIWindowScene else { return }
Self.windowManager.handleSceneDisconnection(windowScene)
}
}
@@ -48,15 +48,28 @@ class WindowManager: SecureWindowManagerProtocol {
}
func configure(withScene scene: UIWindowScene, session: UISceneSession) {
// This gets called for all opened windows, we're only interested in the
// first call, for the main window (works with state restoration too).
guard mainWindow == nil else {
// This gets called for all opened windows, we're only interested in the main window.
guard let userInfo = session.userInfo, userInfo[SceneDelegate.sceneIDKey] as? String == SceneDelegate.mainSceneID else {
scene.windows.forEach { $0.tintColor = .compound.textActionPrimary } // SecondaryWindow tinting.
return
}
// Don't allow more than 1 main window to be presented.
if mainScene != nil {
// The window will be presented momentarily, so lets leave it blank.
scene.keyWindow?.rootViewController = UIHostingController(rootView: Color.clear)
UIApplication.shared.requestSceneSessionDestruction(session, options: nil)
return
}
mainScene = scene
mainSession = session
// Restore the previous window size on macOS as this isn't automatic.
if let previousSize = mainWindow?.frame.size {
scene.resizeWindowOnMac(to: previousSize)
}
mainWindow = scene.keyWindow
mainWindow.tintColor = .compound.textActionPrimary
@@ -76,12 +89,20 @@ class WindowManager: SecureWindowManagerProtocol {
delegate?.windowManagerDidConfigureWindows(self)
}
func configure(withOpenWinddowAction openWindowAction: OpenWindowAction,
func configure(withOpenWindowAction openWindowAction: OpenWindowAction,
dismissWindowAction: DismissWindowAction) {
self.openWindowAction = openWindowAction
self.dismissWindowAction = dismissWindowAction
}
func handleSceneDisconnection(_ scene: UIWindowScene) {
if scene == mainScene {
mainScene = nil
mainSession = nil
// Leave the mainWindow so we can reapply it's size on macOS.
}
}
func handleRoute(_ appRoute: AppRoute, windowType: SecondaryWindowType) {
MXLog.info("Handling app route: \(appRoute) for window type: \(windowType)")
@@ -258,3 +279,19 @@ private struct InstantlyDismissingWindow: View {
}
}
}
private extension UIWindowScene {
func resizeWindowOnMac(to size: CGSize) {
// Hackity hack 🔨
guard ProcessInfo.processInfo.isiOSAppOnMac, let sizeRestrictions else { return }
self.sizeRestrictions?.minimumSize = size
self.sizeRestrictions?.maximumSize = size
Task {
try await Task.sleep(for: .milliseconds(100))
self.sizeRestrictions?.minimumSize = sizeRestrictions.minimumSize
self.sizeRestrictions?.maximumSize = sizeRestrictions.maximumSize
}
}
}
@@ -25,7 +25,9 @@ protocol SecureWindowManagerProtocol: WindowManagerProtocol {
/// Configures the window manager to operate on the supplied scene.
func configure(withScene scene: UIWindowScene, session: UISceneSession)
func configure(withOpenWinddowAction openWindowAction: OpenWindowAction, dismissWindowAction: DismissWindowAction)
func configure(withOpenWindowAction openWindowAction: OpenWindowAction, dismissWindowAction: DismissWindowAction)
func handleSceneDisconnection(_ scene: UIWindowScene)
func handleRoute(_ appRoute: AppRoute, windowType: SecondaryWindowType)