如何从其应用扩展中启动iOS父应用程序

22

有谁知道如何从应用程序扩展的视图控制器中启动父应用程序?

我只想从应用程序扩展中启动主应用程序。

9个回答

10

在Swift 3.1中的可行解决方案(测试于iOS10):

您需要为您的主机应用程序创建自己的URL Scheme,然后将此函数添加到您的ViewController并使用openURL("myScheme://myIdentifier") 调用它。

//  Function must be named exactly like this so a selector can be found by the compiler!
//  Anyway - it's another selector in another instance that would be "performed" instead.
func openURL(_ url: URL) -> Bool {
    var responder: UIResponder? = self
    while responder != nil {
        if let application = responder as? UIApplication {
            return application.perform(#selector(openURL(_:)), with: url) != nil
        }
        responder = responder?.next
    }
    return false
}

请告诉我如何通过扩展名调用它,因为这似乎是不完整的答案。 - Abhishek Thapliyal
1
其实没有什么特别的。在我的情况下,我有一个只包含一个ViewController的共享扩展。我将openURL放在那个ViewController里面,在viewDidAppear后调用了self.openURL(URL(string: "myapp://com.mydomain.share?urlparams")!)。ViewController本身几乎是空的 - 我只是收集传入的文件并将它们保存到本地文件夹中,然后执行上述调用。不要忘记注册你的URL方案myapp... - coyer

10

1
这个好像不起作用。从苹果的文档中我读到一件事,他们说上面的打开URL只适用于某些应用程序扩展,而不是所有应用程序。那么,我还有其他方法可以尝试吗? - loganathan
它绝对适用于今天的扩展 - 我在github上有一个示例:https://github.com/kmonaghan/CBPExtensionExample - Karl Monaghan
除了今天小部件,例如操作扩展或文件提供程序扩展之外,还有其他吗?请确认。 - loganathan
1
尝试从键盘上操作,但是不起作用。 - Tom Kincaid
OpenUrl也可以从共享扩展中工作,但如果主机应用程序关闭并且我尝试共享,则AppDelegate将调用两种方法:带有选项的FinishedLaunching和OpenUrl。有没有办法处理这种情况? - Sagar Panwala
显示剩余5条评论

7

我当前使用的应用程序中,通过操作扩展功能可以使其正常工作。

1- 在父应用程序的 plist 文件中添加自定义 URL,例如: enter image description here

2- 在您的扩展视图控制器中添加这两个函数。

func openURL(url: NSURL) -> Bool {
    do {
        let application = try self.sharedApplication()
        return application.performSelector(inBackground: "openURL:", with: url) != nil
    }
    catch {
        return false
    }
}

func sharedApplication() throws -> UIApplication {
    var responder: UIResponder? = self
    while responder != nil {
        if let application = responder as? UIApplication {
            return application
        }

        responder = responder?.next
    }

    throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
}

3- 在您的按钮动作或其他想要执行的地方调用此函数

self.openURL(url: NSURL(string:"openPdf://HomeVC")!)

这里的homevc是您要呈现的视图控制器的类名

4- 在您的应用程序代理中实现以下方法:

  func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
    let urlPath : String = url.absoluteString
    print(urlPath)
    if urlPath.contains("HomeVC"){
        //here go to firstViewController view controller
        self.window = UIWindow(frame: UIScreen.main.bounds)

        let storyboard = UIStoryboard(name: "Main", bundle: nil)

        let initialViewController = storyboard.instantiateViewController(withIdentifier: "homeVC")

        self.window?.rootViewController = initialViewController
        self.window?.makeKeyAndVisible()
    }
}

我希望它能正常工作,并且可以在扩展应用程序和父应用程序之间共享数据。


由于某种原因,它在没有第四步的情况下启动应用程序并正常工作。此外,AppDelegate方法中的任何一个 func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Boolfunc application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool 都没有触发。 - Paul B
1
步骤4将用于当您需要打开除初始视图控制器之外的视图控制器时。 - Awais Mobeen
阿瓦伊斯,为什么要通过在“Info.plist”中添加“URL类型”,而不是通过项目设置->主机目标->信息选项卡->添加URL类型来实现呢? - Paul B
这真是救了我的一天。谢谢! - Cihan Kalmaz

2
这里是Valentin Shergin针对iOS 14和Xcode 13的解决方案。
extension KeyboardViewController{
    func openURL(url: NSURL) -> Bool {
            do {
                let application = try self.sharedApplication()
                return (application.perform("openURL:", with: url) != nil)
            }
            catch {
                return false
            }
        }

        func sharedApplication() throws -> UIApplication {
            var responder: UIResponder? = self
            while responder != nil {
                if let application = responder as? UIApplication {
                    return application
                }

                responder = responder?.next
            }

            throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
        }
}

2
我曾在这里问过类似的问题:分享扩展与包含应用的交互。基本上,你可以采取一些措施。
  1. Use a UIWebView's loadRequest webView to load an NSURL request with your containing app's url. For example,

    NSURL *url = [NSURL URLWithString:@"myurl"];
    NSURLRequest *request = [[NSURLRequest alloc] initWithURL:url];
    [self.webView loadRequest:request];
    
  2. Use a UIDocumentInteractionController and a custom file extension type to provide a link to your containing app (and your containing app only)

  3. Start a "fake" NSURL session to get the following functionality: In iOS, if your extension isn’t running when a background task completes, the system launches your containing app in the background and calls the application:handleEventsForBackgroundURLSession:completionHandler: app delegate method.

第一个可能是你最好的选择。

但是苹果公司会推荐这种方式吗?请确认。 - loganathan
@loganathan 我不是很确定,但我个人认为它足够干净,以至于苹果公司会让它通过(尤其是考虑到他们允许进入应用商店的一些东西)。 - arcticmatt
1
值得一提的是:我曾经使用过这种方法,在iOS 8.0上确实有效,但在那之后的某个时间点(可能是9.0,也可能更早),它停止了工作 :( - radex

2
文档很明确地说明,您可以在Today扩展中使用extensionContext openURL。暗示着,openURL只能在Today扩展中使用,我们的经验证明这是正确的——我也无法从共享扩展中让它工作。
如果任何这些解决方法被苹果审核通过,我会很感兴趣。但我觉得不会。如果苹果想让它工作,他们很容易允许它。

Apple允许任何Today小部件使用openURL:completionHandler:方法来打开小部件自己的包含应用程序。

如果您使用此方法从Today小部件打开其他应用程序,则您的App Store提交可能需要额外的审核以确保符合Today小部件的意图。

我确定这段第二段是在Launcher被接受、拒绝,然后后来获得批准出售之后添加的。

2
这里有另一种方法:
  1. 在这里执行第1步
  2. 选择扩展目标并转到其构建设置。将仅需要应用程序扩展安全API设置为NO
  3. 像在扩展之外打开URL一样使用UIApplication.shared.openURL(URL(string:"openPdf://")!)来打开URL。

请注意,您将收到'openURL' was deprecated in iOS 10.0 warning警告。因此,未来无法保证此方法能正常工作。

考虑使用本地通知来唤醒主机应用程序,而不是使用此方法。


使用UIApplication.shared.open(URL(string:"openPdf://")!)代替UIApplication.shared.openURL(URL(string:"openPdf://")!) - Fadi Abuzant
它有效了!不再需要在响应器之间运行了。 - djdance
我遇到了这个问题: 应用程序扩展及其链接的任何库必须使用设置为YES的APPLICATION_EXTENSION_API_ONLY构建设置进行构建。 - jontelang
现在没有时间检查,@jontelang。可能会有弃用警告。你尝试按照错误消息建议设置构建设置了吗? - Paul B
是的,问题在于这个扩展需要使用这个答案所涉及的API。我猜它以前能用,但现在似乎已经不能用了。 - jontelang

1

这里是一个可行的解决方案(在iOS 9.2上测试通过),至少适用于键盘扩展。该类别添加了一种特殊方法,可以访问隐藏的sharedApplication对象,然后对其调用openURL:方法。(当然,接下来您需要使用您的应用程序方案来使用openURL:方法。)

extension UIInputViewController {

    func openURL(url: NSURL) -> Bool {
        do {
            let application = try self.sharedApplication()
            return application.performSelector("openURL:", withObject: url) != nil
        }
        catch {
            return false
        }
    }

    func sharedApplication() throws -> UIApplication {
        var responder: UIResponder? = self
        while responder != nil {
            if let application = responder as? UIApplication {
                return application
            }

            responder = responder?.nextResponder()
        }

        throw NSError(domain: "UIInputViewController+sharedApplication.swift", code: 1, userInfo: nil)
    }

}

1

我在我的扩展中使用这段代码来打开主应用程序并使用自定义的 scheme url:

fileprivate func openUrl(url: URL?) {
    let selector = sel_registerName("openURL:")
    var responder = self as UIResponder?
    while let r = responder, !r.responds(to: selector) {
        responder = r.next
    }
    _ = responder?.perform(selector, with: url)
}

确保在调用此函数之前,扩展程序有足够的时间来加载。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接