问题
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 arguements
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 '()'?
. Adding these braces will give me an infinite loop.
2) 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) ?? ""
2) How do I invoke a method that has type IMP
?
Thank you very much.
回答1:
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)
回答2:
Although .shared
property is not available in extensions directly, you can access it via #keyPath
:
let application = UIApplication.value(forKeyPath: #keyPath(UIApplication.shared)) as! UIApplication
来源:https://stackoverflow.com/questions/46240431/accessing-shared-variable-of-uiapplication-from-inside-share-extension-in