在的OpenURL行动扩展不行在的OpenURL行动扩展不行(openURL not work in

2019-05-09 06:42发布

我添加以下代码:

- (IBAction)done {
    // Return any edited content to the host app.
    // This template doesn't do anything, so we just echo the passed in items.

    NSURL *url = [NSURL URLWithString:@"lister://today"];
    [self.extensionContext openURL:url completionHandler:^(BOOL success) {
        NSLog(@"fun=%s after completion. success=%d", __func__, success);
    }];
    [self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];

}

之后我创建Action扩展目标。 但它不能正常工作。

我的目的就是:当用户查看Photos.app照片(iOS的默认Photos.app或者叫画廊),他点击分享按钮来启动我们的扩展视图。 我们可以从Photos.app将图像传送到自己的应用程序和处理或上传的图片中我的应用程序。

我也尝试“CFBundleDocumentTypes”,但它也不能工作。

任何帮助将不胜感激。

Answer 1:

这是由设计。 我们不希望自定义操作成为应用发射器。



Answer 2:

这是我用来做:

UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
NSString *urlString = @"https://itunes.apple.com/us/app/watuu/id304697459";
NSString * content = [NSString stringWithFormat : @"<head><meta http-equiv='refresh' content='0; URL=%@'></head>", urlString];
[webView loadHTMLString:content baseURL:nil];
[self.view addSubview:webView];
[webView performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:2.0];

请注意,在这种情况下,我实例从UIInputViewController此调用。

这种方法也应努力使用来自含有应用程序的URL方案

UPDATE 2015年4月17日:这并不与iOS 8.3的工作。 我们正在寻找一个解决方案,我们会尽快更新的答案

UPDATE 2015年6月1日:我们发现,在iOS 8.3版本,有效的解决方案

var responder = self as UIResponder?

while (responder != nil){
    if responder!.respondsToSelector(Selector("openURL:")) == true{
        responder!.callSelector(Selector("openURL:"), object: url, delay: 0)
    }
    responder = responder!.nextResponder()
}

这将找到一个合适的应答到的OpenURL发送到。

您需要添加此扩展,它取代了performSelector迅速和在机制建设帮助:

extension NSObject {
    func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
        let delay = delay * Double(NSEC_PER_SEC)
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

        dispatch_after(time, dispatch_get_main_queue(), {
            NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
        })
    }
}

UPDATE 2015年6月15日:Objective-C的

有人问在Objective-C所以这里是代码。 我不会运行它,因为我没有时间,但现在它应该是相当简单:

UIResponder *responder = self;
while(responder){
    if ([responder respondsToSelector: @selector(OpenURL:)]){
        [responder performSelector: @selector(OpenURL:) withObject: [NSURL URLWithString:@"www.google.com" ]];
    }
    responder = [responder nextResponder];
}

如前所述,我还没有运行该Objective-C代码,这是刚刚从银行代码转换。 请让我知道,如果你遇到任何问题和解决方案,我将更新它。 现在,我只是用迅速而不幸的是我的大脑将弃用的Objective-C

UPDATE 2016年5月2日:弃用功能

正如指出的@KyleKIM选择器功能已在雨燕2.2 #selector被替换。 此外,还有是过时,可能会在雨燕3.0得到去除,所以我做了一些研究,以找到一个替代的功能。

UPDATE 2016年9月16日:8的XCode,雨燕3.0和iOS10下面的代码仍然是工作在提到版本。 你会得到一些警告:

let url = NSURL(string:urlString)
let context = NSExtensionContext()
context.open(url! as URL, completionHandler: nil)

var responder = self as UIResponder?

while (responder != nil){
    if responder?.responds(to: Selector("openURL:")) == true{
        responder?.perform(Selector("openURL:"), with: url)
    }
    responder = responder!.next
}

UPDATE 2017年6月15日:8.3.3的XCode

let url = NSURL(string: urlString)
let selectorOpenURL = sel_registerName("openURL:")
let context = NSExtensionContext()
context.open(url! as URL, completionHandler: nil)

var responder = self as UIResponder?

while (responder != nil){
    if responder?.responds(to: selectorOpenURL) == true{
        responder?.perform(selectorOpenURL, with: url)
    }
    responder = responder!.next
}


Answer 3:

试试这个代码。

    UIResponder* responder = self;
    while ((responder = [responder nextResponder]) != nil)
    {
        NSLog(@"responder = %@", responder);
        if([responder respondsToSelector:@selector(openURL:)] == YES)
        {
            [responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:urlString]];
        }
    }


Answer 4:

苹果接受了以下解决方案,这是“相同”的代码,一台主机的应用程序将使用。 它适用于所有iOS 8版本更新(在iOS 8.0测试 - iOS 8.3版本)。

NSURL *destinationURL = [NSURL URLWithString:@"myapp://"];

// Get "UIApplication" class name through ASCII Character codes.
NSString *className = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []){0x55, 0x49, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E} length:13] encoding:NSASCIIStringEncoding];
if (NSClassFromString(className)) {
    id object = [NSClassFromString(className) performSelector:@selector(sharedApplication)];
    [object performSelector:@selector(openURL:) withObject:destinationURL];
}


Answer 5:

在雨燕3.0与4.0的工作方案:

// For skip compile error. 
func openURL(_ url: URL) {
    return
}

func openContainerApp() {
    var responder: UIResponder? = self as UIResponder
    let selector = #selector(openURL(_:))
    while responder != nil {
        if responder!.responds(to: selector) && responder != self {
            responder!.perform(selector, with: URL(string: "containerapp://")!)
            return
        }
        responder = responder?.next
    }
}

说明:

在扩展,API是由编译器仅限于不会让你使用的OpenURL:在容器应用程式,例如(URL)。 然而,API还在这里。

我们可以在我们的班,直到我们声明不执行方法,我们真正想要的是让UIApplication的执行这个方法。

回想一下,以响应链,我们可以使用

    var responder: UIResponder? = self as UIResponder
    responder = responder?.next

循环到UIApplication对象。

我用这种方法的应用程序通过审查过程,所以不用担心使用它。



Answer 6:

工作了键盘分机解决方案(在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)
    }

}


Answer 7:

这似乎是一个错误,因为文件说:

打开包含应用程序

在某些情况下,它可以使感延期要求其包含的应用程序打开。 例如,在OS X中的日历控件打开日历,当用户单击一个事件。 为了确保您的应用程序包含在某种程度上是有道理的用户的当前任务的情况下打开,你需要定义,无论是应用程序及其扩展可以使用自定义URL方案。

分机不能直接告诉它包含要打开的应用; 相反,它使用的OpenURL:completionHandler:NSExtensionContext的方法来告诉系统,以打开其包含的应用程序。 当一个分机使用此方法来打开一个URL,系统会验证完成之前该请求。

我今天报告说,它: http://openradar.appspot.com/17376354你应该欺骗它,如果你有一些空闲时间。



Answer 8:

NSExtensionContext仅支持的OpenURL功能在今天的扩展,这是苹果公司的约NSExtensionContext.The原话的文件描述是“每个扩展点确定是否支持此方法,或在何种条件下支持这种方法。在iOS系统8.0,只有今天扩展点支持这种方法“。



Answer 9:

可能的解决方法:创建并添加一个小的UIWebView到您的视图,并运行它与你上面设置的URL方案的loadRequest方法。 这是一种解决方法,我不知道苹果会说些什么。 祝好运!



Answer 10:

胡里奥Bailon的回答与现代斯威夫特语法的更新版本:

let url = NSURL(string: "scheme://")!
var responder: UIResponder? = self
while let r = responder {
    if r.respondsToSelector("openURL:") {
        r.performSelector("openURL:", withObject: url)
        break
    }
    responder = r.nextResponder()
}

有没有必要再延长NSObject的现在。

注意:你必须等待,以便调用此代码,否则不能使用响应链之前连接到视图层次结构。



Answer 11:

解决方案最新的iOS SDK 10.2。 所有以前的解决方案使用过时的API。 该解决方案是基于搜索宿主应用程序的UIApplication UIResponder(这个应用程序,它为我们的扩展创建执行上下文)。 该解决方案只能在Objective-C被提供,因为有一个3个参数的方法来调用,这是不可能做performSelector:方法。 要调用该未过时方法openURL:options:completionHandler:我们需要使用NSInvocation的实例,它是在斯威夫特不可用。 所提供的解决方案可以从Objective-C和夫特(任何版本)被调用。 我必须说,我还不知道,如果提供的解决方案将适用于苹果的审核流程。

UIViewController+OpenURL.h

#import <UIKit/UIKit.h>
@interface UIViewController (OpenURL)
- (void)openURL:(nonnull NSURL *)url;
@end

UIViewController+OpenURL.m

#import "UIViewController+OpenURL.h"

@implementation UIViewController (OpenURL)

- (void)openURL:(nonnull NSURL *)url {

    SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:");

    UIResponder* responder = self;
    while ((responder = [responder nextResponder]) != nil) {
        NSLog(@"responder = %@", responder);
        if([responder respondsToSelector:selector] == true) {
            NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector];
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];

            // Arguments
            NSDictionary<NSString *, id> *options = [NSDictionary dictionary];
            void (^completion)(BOOL success) = ^void(BOOL success) {
                NSLog(@"Completions block: %i", success);
            };

            [invocation setTarget: responder];
            [invocation setSelector: selector];
            [invocation setArgument: &url atIndex: 2];
            [invocation setArgument: &options atIndex:3];
            [invocation setArgument: &completion atIndex: 4];
            [invocation invoke];
            break;
        }
    }
}

@end

从斯威夫特3,如果你的视图控制器是视图层次可以执行此而已。 这是我如何使用它的代码:

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let context = self.extensionContext!
        let userAuthenticated = self.isUserAuthenticated()

        if !userAuthenticated {
            let alert = UIAlertController(title: "Error", message: "User not logged in", preferredStyle: .alert)
            let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in
                context.completeRequest(returningItems: nil, completionHandler: nil)
            }
            let login = UIAlertAction(title: "Log In", style: .default, handler: { _ in
                //self.openContainingAppForAuthorization()
                let url = URL(string: "fashionapp://login")!
                self.open(url)
                context.completeRequest(returningItems: nil, completionHandler: nil)
            })

            alert.addAction(cancel)
            alert.addAction(login)
            present(alert, animated: true, completion: nil)
        }
    }


Answer 12:

下面的代码工作在Xcode的8.3.3,iOS10,Swift3Xcode中9,iOS11,Swift4没有任何编译器警告:

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)
}

func canOpenUrl(url: URL?) -> Bool {
    let selector = sel_registerName("canOpenURL:")
    var responder = self as UIResponder?
    while let r = responder, !r.responds(to: selector) {
        responder = r.next
    }
    return (responder!.perform(selector, with: url) != nil)
}

确保您的应用支持通用链接,否则将打开浏览器的链接。 更多资讯: https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html



Answer 13:

我的猜测是,这是故意不可能的。 该openURL:completionHandler:块说,它可能无法在所有的扩展类型的支持,并在行动扩展文档明确地说:

如果你想帮助在一个社交网站的用户分享内容或他们所关心的信息给用户的更新,行动扩展点是不是正确的选择。

我认为A股的扩展可能是比较合适的,但对于这两种类型的文档表明,经验应该被嵌入在主机应用程序,而不是采取用户到你的应用程序,所以它可能不是允许的,要么。 所以,也许跟随份额扩展文档和距离扩展UI因为它表明中上传图片?



Answer 14:

并不是每一个应用程序扩展类型支持“extensionContext的OpenURL”。

我测试在iOS 8的测试版4,发现今天的扩展支持,但键盘扩展没有。



Answer 15:

只有今天,扩展似乎工作。
这不是100%的记录,但苹果员工特别说键盘的扩展不支持的OpenURL:completionHandler。
该文件说:

每个扩展点确定是否支持该方法,或在该条件下,以支持这种方法。

因此,在实践中,分享,行动,键盘和文件提供者为任何人(测试版5),只有今天,推广工作支持它。



Answer 16:

如苹果文件“A今天插件(并且没有其他应用程序扩展类型)可以要求系统通过调用的OpenURL以打开其包含应用程序:completionHandler:所述NSExtensionContext类的方法”。

对于其他的扩展,我用这个解决方案

UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
NSString *urlString = @"ownApp://"; // Use other application url schema.

NSString * content = [NSString stringWithFormat : @"<head><meta http-equiv='refresh' content='0; URL=%@'></head>", urlString];
[webView loadHTMLString:content baseURL:nil];
[self.view addSubview:webView];
[webView performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:1.0];


文章来源: openURL not work in Action Extension