swiftxcode11imessage-extension

XCode11 error "open(_:options:completionHandler:) is unavailable in application extensions"


After launching Touchgram v1.0, which is 99% iMessage app extension, I tried to update to XCode11.

I started getting an error open(_:options:completionHandler:) is unavailable in application extensions

I confirmed this occurs even in a trivial sample that tries to launch a web URL from an iMessage app:

For example:

    let openSel = #selector(UIApplication.open(_:options:completionHandler:))
    while (responder != nil){
        if responder?.responds(to: openSel ) == true {
            // cannot package up multiple args to openSel 
            // so we explicitly call it on the iMessage application instance

            // found by iterating up the chain
            (responder as? UIApplication)?.open(url, completionHandler:handler) 
            return
        }
        responder = responder!.next
    }

Update 2020

My own answer to this question, below, details how the workaround works. Note that the sample linked above has been fixed to both use this workaround and also show opening a web URL inside a WKWebView inside the iMessage extension itself.


Solution

  • As documented in the (sole) issue on that sample, this was an intentional change in iOS 13 as confirmed by DTS. My belief is this was part of a crackdown on abusive behaviour in keyboard extensions, which picked up iMessage extensions as a side-effect.

    I'd already come up with a workaround, which is the same as they recommend.

    1. Forward the URL to your parent app using self.extensionContext?.open
    2. Have the parent app then launch the external app or URL on your behalf.

    Here's the complete working extension from Touchgram

    // UIViewController+iMessageContext.swift
    // applied to class MessagesViewController: MSMessagesAppViewController, UrlOpeningInIMessage 
    
    protocol UrlOpeningInIMessage {
        func openFromiMessageContext(url:URL)
    }
    
    
    extension UrlOpeningInIMessage where Self:UIViewController {
        func openFromiMessageContext(url:URL) {
            let handler = { (success:Bool) -> () in
                if success {
                    os_log("Finished opening URL", log:tgEnv.logImUI, type:.debug)
                } else {
                    os_log("Failed to open URL", log:tgEnv.logImUI, type:.debug)
                }
            }
            // logic same as onLaunchMainPressed, since XCode11 unable to compile extension using UIApplication.open
            // so we pass the URL through to the parent app to launch on our behalf
            let appName = Bundle.appName()
            let encodedUrl = url.dataRepresentation.base64EncodedString()
            guard let appUrl: URL = URL(string: "\(appName)://?url=\(encodedUrl)") else { return }
            // can only open our app, not generalised URLs
            self.extensionContext?.open(appUrl, completionHandler: handler)
        }
    }