共享扩展程序打开包含应用程序

我想为我的应用程序创build一个Android风格共享function。 我创build了一个共享扩展,当你select股票照片应用程序内的图片,并按共享时被调用。 现在我想要这些照片被发送到主应用程序,并在那里处理。 我现在的问题是:

  1. 在共享扩展窗口上按下button之后,iOS能否打开我的应用程序?
  2. 我如何获得我的主要应用程序中的图片文件?

目前没有办法做到这一点。 共享扩展程序无法打开包含的应用程序。

共享扩展的预期方法是他们自己处理所有必要的工作。 扩展程序可以通过使用自定义框架与其包含的应用程序共享代码,所以在大多数情况下这没有问题。

如果您想让数据可用于您的应用程序,则可以设置一个应用程序组,以便拥有一个共享目录。 扩展可以在那里写数据,应用程序可以读取它。 但是,直到下一次用户启动应用程序才会发生。

Swift 3.1中的工作解决scheme(在iOS10中testing):

你需要创build你自己的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 } 

编辑:注释澄清: openURL是UIApplication的一种方法 – 因为你的ShareExtension不是从UIApplication派生的我添加了我自己的openURL与UIApplication的定义相同,以保持编译器的快乐(这样#selector(openURL(_ )可以find)。

然后我通过响应者,直到我find一个真正从UIApplication派生的,并调用openURL

更精简的示例代码,将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 (eg 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 } } 

我用共享扩展打开主机应用程序。 使用清晰的背景颜色的Web视图。 下面是代码

  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]; 

在主机应用程序中实现自定义url模式,并调用openURL(url :)方法

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

不仅没有办法(也不会)做到这一点:在应用程序中没有必要处理这个问题。 该扩展应该与主应用程序的代码相同。 您应该创build一个框架与应用程序和extesnion目标之间共享的扩展安全API。

这是这里的热门话题: https : //developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1

编辑:这个解决scheme适用于今天的扩展(Widget)。

扩展程序可以打开托pipe应用程序:

 - (IBAction)launchHostingApp:(id)sender { NSURL *pjURL = [NSURL URLWithString:@"hostingapp://home"]; [self.extensionContext openURL:pjURL completionHandler:nil]; } 

就像苹果公司在处理公共场景中所说:

扩展程序不会直接告诉其包含的应用程序打开; 相反,它使用NSExtensionContext的openURL:completionHandler:方法来告诉系统打开其包含的应用程序。 当一个扩展使用这个方法打开一个URL时,系统在完成之前validation这个请求。