4

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.

Andy Dent
  • 17,578
  • 6
  • 88
  • 115

2 Answers2

4

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)
    }
}
Andy Dent
  • 17,578
  • 6
  • 88
  • 115
4

Because of this question' s title is generic and doesn' t refer specifically to Touchgram, i would suggest another workaround for two purposes:

  1. Try to help others that land on this question just reading the title;
  2. Look for opinions about this workaround and Apple' s policy compatibility.

It could be enough to go to your Extension' s Build settings and change the value for "Require Only App-Extension-Safe API" to "NO". This does the trick but it would be really helpful to read someone other' s comment about that.

In Addition to above, if someone have some pod dependency in the Extension Target, and error is happening in the files of the included Pod, then this error can be solved by setting Require Only App-Extension-Safe API to No for that particular Pod. Following would be the path for it:

Pods -> Targets -> Your-Problematic-Pod -> Build Settings -> Require Only App-Extension-Safe API set this to NO.

kamran
  • 382
  • 2
  • 14
MrHim
  • 401
  • 4
  • 16
  • Infact i said that someone could land on this page just because of title as i did. You could have the same problem in others scenarios too and i solved with build settings. So i publushed this answer in order to receive feedbacks about this problem and its clearest way to be solved. Was you able to get your app published by apple with your own solution ? – MrHim Jul 10 '20 at 06:43
  • Have you been able to build and ship a working extension to the app store with setting "Require Only App-Extension-Safe API" to "NO"? – Andy Dent Jul 10 '20 at 14:45
  • @AndyDent this is exactly what i am looking to talk about: the clearest way to do that. This setting did the trick for me, but i don' t know if it would be considered a valid workaround for that. – MrHim Jul 10 '20 at 15:02
  • 1
    I am fairly certain that you can only use that setting if you are NOT building an extension. The entire point of the setting is to enforce Apple's rules on what extensions are allowed to do. Even if you could build an extension with the setting off and get it onto the store, you are effectively using a "private api" and likely to fail review at some point. The only valid way around this is to do as I documented in my answer and as per my working sample shows - forward the work to your associated app. Don't fight DTS. – Andy Dent Jul 10 '20 at 17:05
  • "Was you able to get your app published by apple with your own solution" - if you'd bothered clicking on the link you would see the app is on the store. – Andy Dent Jul 10 '20 at 17:06