ios 共享扩展以打开包含应用程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/27506413/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Share Extension to open containing app
提问by zanzoken
I want to create an Android Style share feature for my app. I created a share extension which gets called when you select pictures inside the stock photo app and press share. Now I want those pictures to be sent to the main app and get handled over there. My question is now:
我想为我的应用创建一个 Android 风格的共享功能。我创建了一个共享扩展,当您在库存照片应用程序中选择图片并按下共享时,它会被调用。现在我希望将这些图片发送到主应用程序并在那里处理。我现在的问题是:
- Can iOS open my app after a button is pressed on the share extension window?
- How do I get the picture files inside my main app?
- 在共享扩展窗口上按下按钮后,iOS 可以打开我的应用程序吗?
- 如何在我的主应用程序中获取图片文件?
采纳答案by Tom Harrington
Currently there's no way to do this. A share extension cannot open the containing app.
目前没有办法做到这一点。共享扩展程序无法打开包含的应用程序。
The intendedapproach for share extensions is that they handle all of the necessary work themselves. Extensions can share code with their containing apps by using custom frameworks, so in most cases that's no problem.
共享扩展的预期方法是它们自己处理所有必要的工作。扩展可以使用自定义框架与其包含的应用程序共享代码,因此在大多数情况下这没有问题。
If you want to make data available to your app, you can set up an app group so that you have a shared directory. The extension can write data there, and the app can read it. That won't happen until the next time the user launches the app, though.
如果您想让数据对您的应用程序可用,您可以设置一个应用程序组,以便您拥有一个共享目录。扩展程序可以在那里写入数据,应用程序可以读取它。但是,直到用户下次启动应用程序时才会发生这种情况。
回答by coyer
Swift 4+ (tested on iOS 13)
Swift 4+(在 iOS 13 上测试)
@objc
should be added to the declaration of openURL
, that is,
@objc
应添加到 的声明中openURL
,即
@objc func openURL(_ url: URL) -> Bool {
// Code below.
}
Without it one would see this compiler error:
没有它,人们会看到这个编译器错误:
Argument of '#selector' refers to instance method 'openURL' that is not exposed to Objective-C
Working solution in Swift 3.1 (tested in iOS10):
Swift 3.1 中的工作解决方案(在 iOS10 中测试):
You need to create your own URL Scheme, then add this function to your ViewController and call it with openURL("myScheme://myIdentifier")
您需要创建自己的 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
}
Edit: Notes for clarification:openURL
is a method of UIApplication - since your ShareExtension is not derived from UIApplication I added my own openURL
with the same definition as the one from UIApplication to keep the compiler happy (so that #selector(openURL(_:)can be found).
编辑:澄清说明:openURL
是 UIApplication 的一种方法 - 由于您的 ShareExtension 不是从 UIApplication 派生的,所以我添加了自己的openURL
与UIApplication相同的定义以保持编译器满意(以便#selector(openURL(_:)可以被发现)。
Then I go through the responders until I find one that is really derived from UIApplication
and call openURL
on that.
然后我查看响应者,直到找到真正源自响应者UIApplication
并呼吁响应者openURL
。
More stripped-down-example-code which copies files in a ShareExtension to a local directory, serializing filenames and calling openURL on another app:
将 ShareExtension 中的文件复制到本地目录、序列化文件名并在另一个应用程序上调用 openURL 的更多精简示例代码:
//
// ShareViewController.swift
//
import UIKit
import Social
import MobileCoreServices
class ShareViewController: UIViewController {
var docPath = ""
override func viewDidLoad() {
super.viewDidLoad()
let containerURL = FileManager().containerURL(forSecurityApplicationGroupIdentifier: "group.com.my-domain")!
docPath = "\(containerURL.path)/share"
// Create directory if not exists
do {
try FileManager.default.createDirectory(atPath: docPath, withIntermediateDirectories: true, attributes: nil)
} catch let error as NSError {
print("Could not create the directory \(error)")
} catch {
fatalError()
}
// removing previous stored files
let files = try! FileManager.default.contentsOfDirectory(atPath: docPath)
for file in files {
try? FileManager.default.removeItem(at: URL(fileURLWithPath: "\(docPath)/\(file)"))
}
}
override func viewDidAppear(_ animated: Bool) {
let alertView = UIAlertController(title: "Export", message: " ", preferredStyle: .alert)
self.present(alertView, animated: true, completion: {
let group = DispatchGroup()
NSLog("inputItems: \(self.extensionContext!.inputItems.count)")
for item: Any in self.extensionContext!.inputItems {
let inputItem = item as! NSExtensionItem
for provider: Any in inputItem.attachments! {
let itemProvider = provider as! NSItemProvider
group.enter()
itemProvider.loadItem(forTypeIdentifier: kUTTypeData as String, options: nil) { data, error in
if error == nil {
// Note: "data" may be another type (e.g. Data or UIImage). Casting to URL may fail. Better use switch-statement for other types.
// "screenshot-tool" from iOS11 will give you an UIImage here
let url = data as! URL
let path = "\(self.docPath)/\(url.pathComponents.last ?? "")"
print(">>> sharepath: \(String(describing: url.path))")
try? FileManager.default.copyItem(at: url, to: URL(fileURLWithPath: path))
} else {
NSLog("\(error)")
}
group.leave()
}
}
}
group.notify(queue: DispatchQueue.main) {
NSLog("done")
let files = try! FileManager.default.contentsOfDirectory(atPath: self.docPath)
NSLog("directory: \(files)")
// Serialize filenames, call openURL:
do {
let jsonData : Data = try JSONSerialization.data(
withJSONObject: [
"action" : "incoming-files"
],
options: JSONSerialization.WritingOptions.init(rawValue: 0))
let jsonString = (NSString(data: jsonData, encoding: String.Encoding.utf8.rawValue)! as String).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
let result = self.openURL(URL(string: "myapp://com.myapp.share?\(jsonString!)")!)
} catch {
alertView.message = "Error: \(error.localizedDescription)"
}
self.dismiss(animated: false) {
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
}
})
}
// 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
}
}
回答by Cherpak Evgeny
Technically you can't open containing app from share extension, but you can schedule local notification, and that's what I end up doing. Just before I call super.didSelectPost, I schedule local notification with some text, and if user wants to open containing app, they can, and if not - they can continue with their workflow. I even think its a better approach than automatically opening containing app and disrupting what they are doing.
从技术上讲,您无法从共享扩展中打开包含应用程序,但您可以安排本地通知,这就是我最终要做的。就在我调用 super.didSelectPost 之前,我用一些文本安排本地通知,如果用户想要打开包含应用程序,他们可以,如果不是 - 他们可以继续他们的工作流程。我什至认为这是比自动打开包含应用程序并破坏他们正在做的事情更好的方法。
回答by Waqas
I opened the host app from shared extension with a trick. Using a webview with clear background color. below is the code
我用一个技巧从共享扩展中打开了主机应用程序。使用具有清晰背景颜色的 webview。下面是代码
NSString *customURL = @"MY_HOST_URL_SCHEME_APP://";
UIWebView *webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 300, 400)];
webView.backgroundColor = [UIColor clearColor];
webView.tintColor = [UIColor clearColor];
[webView setOpaque:NO];
[self.view addSubview:webView];
NSURLRequest *urlRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:customURL]];
[webView loadRequest:urlRequest];
[self didSelectCancel];
回答by SPatel
Implement custom url schema in host app and call openURL(url:) method
在主机应用程序中实现自定义 url 架构并调用 openURL(url:) 方法
like openURL(url:NSURL(string:"schema_name://"))
像 openURL(url:NSURL(string:"schema_name://"))
extension SLComposeServiceViewController {
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)
}
}
回答by Nylon
I was having this problem, and in iOS 11+ none of the previous answers work. I ended up adding a completion handler to my JavaScript code, and from there setting window.location="myapp://"
. It's a bit hacky but it doesn't look to bad and the user can follow along.
我遇到了这个问题,在 iOS 11+ 中,以前的答案都不起作用。我最终在我的 JavaScript 代码中添加了一个完成处理程序,并从那里设置window.location="myapp://"
. 它有点老套,但看起来还不错,用户可以跟上。
回答by Anton Tropashko
Not only there is no way (and won't be) to do this: there is no NEED to handle this in the app. The extension is supposed to handle this with the very same codebase as the main app. You should create a framework with extension safe API shared between the app and the extesnion targets.
不仅没有办法(也不会)这样做:没有必要在应用程序中处理这个问题。该扩展程序应该使用与主应用程序完全相同的代码库来处理这个问题。您应该创建一个在应用程序和扩展目标之间共享扩展安全 API 的框架。
This is the top topic here: https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1
这是这里的热门话题:https: //developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1
回答by Zubair
EDIT: This solution works for today extension (Widget).
编辑:此解决方案适用于今天的扩展(小部件)。
An extension can open the hosting app:
扩展程序可以打开托管应用程序:
- (IBAction)launchHostingApp:(id)sender
{
NSURL *pjURL = [NSURL URLWithString:@"hostingapp://home"];
[self.extensionContext openURL:pjURL completionHandler:nil];
}
And like Apple says in Handling Commons Scenarios:
就像苹果在处理公共场景中所说的那样 :
An extension doesn't directly tell its containing app to open; instead, it uses the openURL:completionHandler: method of NSExtensionContext to tell the system to open its containing app. When an extension uses this method to open a URL, the system validates the request before fulfilling it.
扩展程序不会直接告诉其包含的应用程序打开;相反,它使用 NSExtensionContext 的 openURL:completionHandler: 方法告诉系统打开其包含的应用程序。当扩展程序使用此方法打开 URL 时,系统会在完成请求之前验证请求。