Launching scene-based app from file URL does not result in expected call to scene(_ scene: UIScene, openURLContexts...)

Working on a new app built in xcode 11.2 for iOS 13.x. The app is a single-view storyboard app, but like all apps created under 11, it defaults to using the scene delegate for lifecycle events.


The app exports a custom file type and registers as owner of that type. When the app is in the background it is automatically brought to the foreground when a file of the custom type is accessed in the Files app, delivered to the device over AirDrop, opene from Mail, etc. This happens by calling the scene delegate's openURLContainers method:


func scene(_ scene: UIScene, openURLContexts URLContexts: Set) {
    // handle URLs here...
    // This works when the app is already in memory
    // but is never called if the app is launched from cold/dark
}


However, when the app is not resident in memory (i.e. following a restart or after the app has been killed) the expected behavior does not occur. The app is launched, but the opeURLContexts method is never called.


I thought that perhaps in this situation the legacy process in appDelegate would be called (i.e. a LauchOptionsKey of type .url in the launchOptions dictionary). That does not appear to be the case either. The launchOptions dictionary is always nil.


Is this a bug, or is there some other way to catch the URL for the file in this new scene-based world?

Replies

A clarification:


the func signature is

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>)


Is it the one you use ?

Oops. You are correct. Here's the actual code from my app:


func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        for context in URLContexts {
            var json:String?
            do {
                print(context.url.path)
                
                if context.options.openInPlace {
                    if !context.url.startAccessingSecurityScopedResource() {
                        print("Unable to get access to security scoped resource.")
                        return
                    }
                }
                
                json = try String(contentsOf: context.url, encoding: .utf8)
                
                if context.options.openInPlace {
                    context.url.stopAccessingSecurityScopedResource()
                }
            } catch {
                print("Unable to read file.")
                print(error)
            }
            
            do {
                if let js = json {
                    if let list = FBCheckList.init(JSONString: js) {
                        list.save()
                    }
                } else {
                    print("JSON string is empty")
                }
                if !context.options.openInPlace {
                    try FileManager.default.removeItem(at: context.url)
                }
            } catch {/* error handling here */
                print("Couldn't parse JSON.")
                print(error)
            }
        }
        let rootVC = self.window?.rootViewController as! UINavigationController
        let listVC = rootVC.viewControllers[0] as! ListTableViewController
        listVC.reloadListData()
    }


Again, this works when the app is resident in memory (background or foregound). It only fails to work when the app is not resident.

And I found the solution here https://developer.apple.com/forums/thread/134099?answerId=424542022#424542022