Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MacCatalyst app launches multiple windows if previous launch had multiple windows open #10939

Closed
hjharvey-MSFT opened this issue Oct 26, 2022 · 4 comments
Labels
area/desktop 🖥️ Windows / WinUI / Project Reunion & Mac Catalyst / macOS specifics (Menus & other Controls)) area/lifecycle 🔁 XPlat and Native UIApplicationDelegate/Activity/Window lifecycle events external p/1 Work that is critical for the release, but we could probably ship without partner/cat 😻 Client CAT Team platform/macOS 🍏 macOS / Mac Catalyst t/bug Something isn't working
Milestone

Comments

@hjharvey-MSFT
Copy link

Description

Under MacCatalyst if your app can launch multiple windows then upon relaunch (ex after a crash) then the app will reload with the same number of windows, all of which are a copy of the initial launch window.

As you can see here we have 3 windows. The main window has 2 buttons, the other 2 are blank
image

After launching the app under visual studio you can see that it reopened with 3 windows, all being the starting window
image

Steps to Reproduce

Create a sample MAUI app that can launch Multi windows
Build in visual studio
Launch app from macCatalyst build folder
Open Multiple windows
Leave app open
Launch app via Visual Studio

You will see the app open with multiple windows

Link to public reproduction project repository

https://github.com/hjharvey-MSFT/MauiMultiWindowSample

Version with bug

6.0.419

Last version that worked well

Unknown/Other

Affected platforms

macOS

Affected platform versions

MacCatalyst

Did you find any workaround?

Override the App.cs Create window method. Check if a window exists, if it does, then close it

protected override Window CreateWindow(IActivationState activationState)
    {
        if (Application.Current.Windows.Count > 0)
            Application.Current.CloseWindow(Application.Current.Windows.FirstOrDefault());

this ensures that only 1 window is open on launch, but it isnt ideal. I would prefer a way to block the window from being created to begin with, or stop it at the Scene delegate level, but nothing else I tried worked.

Relevant log output

No response

@hjharvey-MSFT hjharvey-MSFT added the t/bug Something isn't working label Oct 26, 2022
@samhouts samhouts added platform/macOS 🍏 macOS / Mac Catalyst area/lifecycle 🔁 XPlat and Native UIApplicationDelegate/Activity/Window lifecycle events area/desktop 🖥️ Windows / WinUI / Project Reunion & Mac Catalyst / macOS specifics (Menus & other Controls)) partner/cat 😻 Client CAT Team labels Oct 26, 2022
@jsuarezruiz jsuarezruiz added this to the Backlog milestone Oct 26, 2022
@ghost
Copy link

ghost commented Oct 26, 2022

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.

@mattleibow
Copy link
Member

mattleibow commented Oct 28, 2022

This is a "feature" of macOS, but will need to see why if the app was already running and the code for saving things is not specified it still felt the need to restore windows... Maybe just the act of overriding things causes this...

@Redth any gut feelings on things to look at first?

@samhouts samhouts added the p/1 Work that is critical for the release, but we could probably ship without label Nov 2, 2022
@samhouts samhouts modified the milestones: Backlog, .NET 8 Planning Jan 26, 2023
@Redth
Copy link
Member

Redth commented Jul 12, 2023

There's a few things happening here and I don't think there's actually a bug, but rather a few misunderstandings of how things work.

First of all, MacCatalyst is a bit tough to test some of these states in because what you're effectively doing in the repro here by launching the app and then debugging in visual studio is causing the existing instance of the app to be forcefully terminated, in which case each MAUI Window instance doesn't have the opportunity to gracefully save out its state so that it can properly restore the window next launch. One thing I could see us doing is potentially trying to provide access to the window state sooner than just in the Window.OnBackgrounding(IPersistedState state) call, which might allow us to actually save out state in the event the process/window is terminated before we call this. I'm not sure this is feasible cross platform though.

Next, in order to properly handle multi window scenarios successfully on both MacCatalyst and iPad, you need to do a couple of things:

  1. Do not use MainPage to set a main page
  2. Override Window CreateWindow(IActivationState activationState) in your Application subclass - this is where you will need to implement some logic to decide what type of window (and/or more importantly, what type of Page) you are going to return given the contents of the IActivationState parameter passed to this call.
  3. Subclass Window yourself in order to provide an override of OnBackgrounding(IPersistedState state) so that you can save out any state that you need to help decide which type of Window/Page to create (and any parameters like an item id, etc) and how to re-inflate or restore the state again when you're asked to create a window in the CreateWindow method of your Application class

The tricky part here is that on MacCatalyst and iPadOS, the OS may ask you to restore a given Scene at any time, even if you did not initiate the window create call yourself. This history can persist across process boundaries too. When this happens you can't really say 'no', and you will need to return a window of some type.

What you do have control over is the state persisted when your window goes into the background, so be sure you save out enough information to rebuild a given window when asked.

@Redth
Copy link
Member

Redth commented Jul 12, 2023

Looking more closely at MAUI's scene delegate implementation, I don't see how you would actually be able to persist state in the case where the app is forcefully terminated.

My guess is that some apps might do better in these cases because they might have multiple scene identifiers registered, so at least they might know the type of window they will be opening. It's also possible to more proactively save out your own state however you choose to do so. You might keep a state of the active 'stack' of windows for example and their states once they are opened, and then as you are asked to CreateWindow you could 'pop' window state off your stack and arbitrarily assign windows to own that state.

I don't think there's much more we can reasonably easily do at the moment to mitigate forceful process terminations not having saved state in such a global fashion, and if that becomes critically important for someone's app, they will need to invest a bit more in proactive state management.

@Redth Redth closed this as completed Jul 12, 2023
@samhouts samhouts modified the milestones: .NET 8 Planning, .NET 8 GA Jul 12, 2023
@dotnet dotnet locked as resolved and limited conversation to collaborators Aug 13, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
area/desktop 🖥️ Windows / WinUI / Project Reunion & Mac Catalyst / macOS specifics (Menus & other Controls)) area/lifecycle 🔁 XPlat and Native UIApplicationDelegate/Activity/Window lifecycle events external p/1 Work that is critical for the release, but we could probably ship without partner/cat 😻 Client CAT Team platform/macOS 🍏 macOS / Mac Catalyst t/bug Something isn't working
Projects
None yet
Development

No branches or pull requests

5 participants