UIDocumentViewController based app built for iOS 17.5 unexpectedly uses new document launch experience under iOS 18 developer beta

In the WWDC24 session Evolve your document launch experience, it is mentioned that apps linked against the iOS 17 SDK would not get the new document launch experience. However, I found that the new document browser is active in my iOS 17 app when installed from the App Store on iOS 18, without rebuilding it.

This (along with other iOS 18 UIKit behavioral regressions) renders the app unusable when running under iOS 18. To be clear, my app uses a UIDocumentViewController as the root view controller of a UINavigationController and is implemented primarily in Obj-C.

I don't want to show the new document browser to users at app launch time. The current behavior of my app is that it always launches to either the current document or a new document and then allows the user to open a new document using the document picker if desired (this was accomplished by invoking the action associated with the Documents button on iOS 17; see related feedback FB13418866: ER: UIDocumentViewController should provide API to allow customization of Documents button behavior).

The new UIDocumentViewController behavior is problematic because it has replaced the Documents button with a backAction that moves the user into the new document browser with no way to back out, aside from picking a document or creating a new document. Previously, the user could choose Cancel to exit the document picker and get back to the currently-open document without choosing a new one.

While the new UIDocumentViewController behavior looks nice for apps like Swift Playgrounds, it is problematic for apps that want to take advantage of the UIDocument infrastructure without forcing the user to deal with a more complicated browser-centric app UI.

I would expect there to be some way to maintain the previous behavior as it existed on iOS 17, but I don't see any way to do this. Suggestions welcome. Thanks!

UPDATE: If there were only a supported way to dismiss the new document browser without choosing a file, I think I could implement something close to the desired behavior on iOS 18.

I tried adding this to my -viewDidLoad override:

    if ( @available(iOS 18, macOS 15, *) ) {
        _documentBrowserViewController = self.launchOptions.browserViewController;

        // see https://stackoverflow.com/a/76047554/21963209
        self.launchOptions.browserViewController.additionalLeadingNavigationBarButtonItems = @[
            [[UIBarButtonItem alloc] initWithTitle:@"Cancel" style:UIBarButtonItemStylePlain target:self action:@selector(didTapDocumentBrowserCancel:)]
        ];
    }

with this action method:

- (IBAction)didTapDocumentBrowserCancel:(id)sender {
    [_documentBrowserViewController dismissViewControllerAnimated:YES completion:nil];
}

and this almost does what I want, except that a moment after the browser view is dismissed, this message is emitted to the console and the browser is presented again:

A visible UIDocumentBrowserViewController was asked to dismiss unexpectedly. Avoid calling -[UIViewController dismissViewControllerAnimated:completion:] when this browser view controller is used within a UIDocumentViewControllerLaunchOptions context. Browser view controller: <UIDocumentBrowserViewController: 0x106b78f00>

Why must Apple stand in my way at every turn?

The issue you are running into here is due to the fact that the browser view controller is only the bottom half of the new launch experience. So dismissing that view controller will not get you back into the document, it would only hide the bottom sheet that shows the browser.

You should be able to hide the back button and replace it with a custom documents button that shows a document picker view controller. This is what the document view controller did in iOS 17. In order to achieve this, set hidesBackButton to YES on your view controller's navigationItem and then set a custom leftBarButtonItem that presents your picker.

If you always have a document set on your document view controller you should furthermore never see the new document launch experience.

The current behavior of my app is that it always launches to either the current document or a new document and then allows the user to open a new document using the document picker if desired

This is still possible with the two things I mentioned above. Make sure you have a document set (this will hide the launch experience) and then replace the back button with a custom button that shows a document picker.

The issue you are running into here is due to the fact that the browser view controller is only the bottom half of the new launch experience. So dismissing that view controller will not get you back into the document, it would only hide the bottom sheet that shows the browser.

That makes sense, though it doesn't mesh with what I actually observe on the screen when I do this—what I see is the full UI of my app is exposed momentarily (minus keyboard, but I believe it's just hidden), and then it is hidden again by the browser & new launch experience (NLE?). And when I look at the UI layers in Xcode's view debugger, it shows all of my UI layers (UINavigationBar and custom view displaying document contents) are actually in front of the UIDocumentViewController view that displays the browser & NLE "poster" (my custom UI is a full-screen subview of the UIDocumentViewController's view in the storyboard).

You should be able to hide the back button and replace it with a custom documents button that shows a document picker view controller. This is what the document view controller did in iOS 17.

Yes, I figured this was possible, but I had hoped to leverage the existing machinery from iOS 17 (which I assumed still existed in the framework for compatibility reasons) rather than having to add code to my app to re-implement this for iOS 18 (and risk getting it wrong).

Ultimately, I presume it might be better to find a way to adopt the new UI rather than trying to implement something like this, except as a stopgap. I would like to minimize custom code and rely on system behaviors where possible.

This is still possible with the two things I mentioned above. Make sure you have a document set (this will hide the launch experience)

Makes sense. At this point I think my normal app launch sequence is actually fine and does not show the NLE view. Outside of the "file open" flow, I believe the NLE only occurs momentarily during a transition where my app is opened via a URL from an app extension. I assume that I might need to provide a dummy document to cover the transition during this time when the document is presumably nil, currently.

Thanks for your insights on this!

One more thought on this:

It seems like it would be nice to have the ability to configure a UIDocumentBrowserViewController to behave like a UIDocumentPickerViewController, as far as having the option for it be presented modally, with a cancel button.

Perhaps I'm misunderstanding the use cases, but these classes seem very similar, with the exception of UIDocumentPickerViewController having the limitation of not being able to create new documents (which is not actually a desirable behavior in my use case, though I can live with it). Since UIDocumentBrowserViewController is newer, I have the impression that it's mostly meant to replace UIDocumentPickerViewController for many use cases.

Since we seem to be stuck with UIDocumentBrowserViewController in the new UIDocumentViewController (unless we implement it ourselves), it seems like having such capability would allow for greater adoption in apps that don't fit the mold of Pages and Swift Playground.

Problem remains in iOS 18.0 beta 2 (22A5297f). No change.

I’m unable to mask all appearances of the NLE by keeping the document non-nil. it still appears in transition when opening a new document by assigning the document property.

My understanding from discussion with the UIKit team is that this is a known issue that they are working on. I had hoped it would be fixed in the second beta or at least before the public beta.

@Frameworks Engineer

Could you explain how UIKit presents the browser UI in front of my own custom view? Since the problem is not resolved in beta 2, I am looking for ways to work around it myself.

The browser UI doesn't appear to be presented as a normal view controller. I suspected that it was added as a subview of the UIDocumentViewController's view, but that does not appear to be the case, either. I tried stopping in Xcode during a transition where the browser UI was visible and inspecting the view hierarchy in the Xcode view debugger, but this offered no clues. From what I saw there, I would expect my UI to be onscreen.

Also, while trying to inspect state in the Xcode view debugger, the app managed to advance so that the browser was no longer shown on the device's screen. Perhaps this indicates what I'm seeing is some transitional animation captured in a CATransaction or something like that?

The issue with the new design briefly flashing is a known issue we are tracking. I can't comment on timelines unfortunately.

I would advise against using private API. You won't get this through app review and you will have no guarantee that this property exists in any future seeds or releases. Aside form that you might also cause subtle issues with out of sync states that might create other bugs in your app.

As for access to the underlying presentation, unfortunately we do not expose this presentation via any of the usual API hooks, so you do not have access to it. I think the best course of action here is to wait for the OS to fix this issue.

UIDocumentViewController based app built for iOS 17.5 unexpectedly uses new document launch experience under iOS 18 developer beta
 
 
Q