简体   繁体   中英

Accessing `shared` variable of `UIApplication` from inside `share extension` in Swift

I need to execute my host app from inside an extension. In Objective-C I've used this:

// 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))
{
    //  A different way to call [UIApplication sharedApplication]
    id object = [NSClassFromString(className) performSelector: @selector(sharedApplication)];
    //  These lines invoke selector with 3 arguments
    SEL selector = @selector(openURL:options:completionHandler:);
    id  (*method)(id, SEL, id, id, id) = (void *)[object methodForSelector: selector];
    method(object, selector, myURL, nil, nil);
    //  Close extension
    [self.extensionContext completeRequestReturningItems: @[] completionHandler: nil];
}

But in Swift I have a couple of issues:

  1. UIApplication does not have sharedApplication method anymore. Instead it has a class property shared . So I can not perform a selector to get shared instance. I've tried to bypass this by writing an extension

    extension UIApplication { class func shared() -> UIApplication { return UIApplication.shared } }

but I get an error Function produces expected type 'UIApplication'; did you mean to call it with '()'? Function produces expected type 'UIApplication'; did you mean to call it with '()'? . Adding these braces will give me an infinite loop.

  1. Even if I get the instance somehow, I can't understand how to invoke open method.

    let selector = NSSelectorFromString("open(_:options:completionHandler:)") let method = object?.method(for: selector) method(destinationURL, String: Any, nil)

The last line gives me Cannot call value of non-function type 'IMP' . When pressing on the type in apple's documents, nothing happens. I can't find a description of IMP and how to work with it.

You might say: "just set Require only app-extension-safe api to NO in your extension's settings and call UIApplication.shared normally". To which I would reply that my builds get rejected by iTunes Connect with a message Compliance with export requirements is required or something along those lines (iTunes Connect is in Russian when my entire OS is in English).

So here are the questions:

  1. Is there a way to get UIApplication.shared using ASCII codes in Swift ?

BTW I get class name like so

let codes = [CUnsignedChar](arrayLiteral: 0x55, 0x49, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E)
let className = String(data: Data(bytes: UnsafeRawPointer(codes), count: 13), encoding: String.Encoding.ascii) ?? ""
  1. How do I invoke a method that has type IMP ?

Thank you very much.

The problem is that you shouldn't try to access sharedApplication from your extension in the first place.

As per App Extension Programming Guide :

Some APIs Are Unavailable to App Extensions

Because of its focused role in the system, an app extension is ineligible to participate in certain activities. An app extension cannot:

  • Access a sharedApplication object, and so cannot use any of the methods on that object

So you may hack your way through it, but that will result in your application being rejected upon review.

However, to do what you're trying to do (open a URL) you don't need sharedApplication - you can simply do that using NSExtensionContext s open(_:completionHandler:)

extensionContext.open(myUrl, completionHandler: myCompletionHandler)

虽然.shared属性不能直接在扩展中使用,但您可以通过#keyPath访问它:

let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as! UIApplication

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM