问题
I want to launch my containing app.
I tried using URL schemes.
The URL scheme launched the app from other places - so the problem is not there.
Looks like this object is nil:
self.extensionContext
thus i can't run this method:
[self.extensionContext openURL:url completionHandler:nil];
Can I launch my app? Do URL Schemes work in a custom keyboard?
thanks!
回答1:
Try this code
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]];
}
}
回答2:
To answer MY OWN question:
In an iOS8 custom keyboard, the extensionContext object is nil, thus I can't use it to launch the containing app.
The workaround I came up with is:
- create a url scheme for your app
- add a UIWebView to your inputView
- load the url scheme for your containing app in the webview
I'm not sure if Apple will allow this to happen, but it works now.
回答3:
Here is working solution (tested on iOS 9.2) for Keyboard Extension. This category adds special method for access to hidden sharedApplication
object and then call openURL:
on it.
(Of course then you have to use openURL:
method with your app scheme.)
// Usage:
// UIInputViewController.openURL(NSURL(string: "your-app-scheme://")!)
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)
}
}
Lately I developed slightly different approach:
// Usage:
// UIApplication.🚀sharedApplication().🚀openURL(NSURL(string: "your-app-scheme://")!)
extension UIApplication {
public static func 🚀sharedApplication() -> UIApplication {
guard UIApplication.respondsToSelector("sharedApplication") else {
fatalError("UIApplication.sharedKeyboardApplication(): `UIApplication` does not respond to selector `sharedApplication`.")
}
guard let unmanagedSharedApplication = UIApplication.performSelector("sharedApplication") else {
fatalError("UIApplication.sharedKeyboardApplication(): `UIApplication.sharedApplication()` returned `nil`.")
}
guard let sharedApplication = unmanagedSharedApplication.takeUnretainedValue() as? UIApplication else {
fatalError("UIApplication.sharedKeyboardApplication(): `UIApplication.sharedApplication()` returned not `UIApplication` instance.")
}
return sharedApplication
}
public func 🚀openURL(url: NSURL) -> Bool {
return self.performSelector("openURL:", withObject: url) != nil
}
}
回答4:
Apple does not allow any app extensions other than Today extensions to open the containing app.
From the guidelines:
A Today widget (and no other app extension type) can ask the system to open its containing app by calling the openURL:completionHandler: method of the NSExtensionContext class.
You can check Here
回答5:
I tried to do above solutions for latest xcode 8.2 and swift 3.0.
Unfortunately. I can't get it work. So I found my own solution and it works well in swift 3.0, xcode 8.2
func openURL(_ url: URL) {
return
}
func openApp(_ urlstring:String) {
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: urlstring)!)
return
}
responder = responder?.next
}
}
// Usage
//call the method like below
//self.openApp(urlString)
//URL string need to included custom scheme.
//for example, if you created scheme name = customApp
//urlString will be "customApp://?[name]=[value]"
//self.openApp("customApp://?category=1")
回答6:
According to Apple's documentation https://developer.apple.com/library/ios/documentation/General/Conceptual/ExtensibilityPG/index.html, you should use NSExtensionContext like this:
NSExtensionContext *ctx = [[NSExtensionContext alloc] init];
[ctx openURL:[NSURL URLWithString:@"myapp://"] completionHandler:^(BOOL success){return ;}];
回答7:
In an App Extension (ex: custom keyboard), that would have been handled through UIViewController.extensionContext
but as of iOS 8.1.2, the field is nil
in the following Swift call:
self.extensionContext?.openURL(appURL, completionHandler: nil)
// Does nothing because extensionContext is nil (iOS 8.1)
Actually, it is not possible either to use Application.sharedApplication.openURL(...)
in an App Extension as stated in Apple documentation.
So, as of 8.1.2, the workaround is to use a dumb UIWebView
to redirect to the containing app like this:
let webView = UIWebView(frame: CGRectMake(0, 0, 0, 0));
let url = "MyKeyboard://";
let content = "<head><meta http-equiv='refresh' content='0; URL=\(url)'></head>";
webView.loadHTMLString(content, baseURL: nil);
self.view.addSubview(webView);
let delayInSeconds = 2.0
let startTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delayInSeconds * Double(NSEC_PER_SEC)))
dispatch_after(startTime, dispatch_get_main_queue()) { () -> Void in
webView.removeFromSuperview()
}
Where MyKeyboard://
must be defined accordingly as URL scheme in the containing app.
Please take note that I don't know yet whether this is approved by Apple. Forwarding to apps from custom keyboards may be bad enough for user experience.
回答8:
This is what I found to open any URL using what has been described above:
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];
Please note that in this case I am instantiating this call from the UIInputViewController.
This method should also work using the URL scheme from the containing app
NOTE: As of iOS 8.3 Apple has killed this method
回答9:
A Swift 2.2 version, akin to DongHyun Jang's answer for Objective-C:
func webLink(urlString: String) {
let url = NSURL(string: urlString)
var responder: UIResponder? = self
while let r = responder {
if r.respondsToSelector("openURL:") {
r.performSelector("openURL:", withObject: url)
break;
}
responder = r.nextResponder()
}
}
回答10:
As per Apple review guidelines for Extension, it is now not advised to open external apps from the extension.
For reference please see apple review guidelines
Section 4.4.1
They must not:
- Include marketing, advertising, or in-app purchases;
- Launch other apps besides Settings; or
回答11:
I struggled with this for a couple of days. I could not get the UIWebView working inside the custom keyboard.
Fix: put it inside the viewDidAppear:animated instead of viewDidLoad (on another note this is where the code for the keyboard's custom height should stay as well).
Code:
- (void)viewDidAppear:(BOOL)animated {
UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
NSString * content = @"<a href='your_app_schema://your_params'>anchor</a>";
[webView loadHTMLString:content baseURL:nil];
[self.view addSubview:webView];
}
来源:https://stackoverflow.com/questions/24495362/launch-containing-app-from-ios8-custom-keyboard