私はAndroidアプリのスタイル共有機能を作成します。ストックフォトアプリ内の画像を選択して共有を押すと呼び出される共有拡張機能を作成しました。これらの画像をメインアプリに送信され、そこで処理されます。私の質問は次のとおりです。
現在、これを行う方法はありません。共有拡張機能は、それを含むアプリを開くことができません。
共有拡張のintendedアプローチは、必要なすべての作業を自分で処理するというものです。拡張機能は、カスタムフレームワークを使用してコードを含むアプリとコードを共有できるため、ほとんどの場合、問題はありません。
アプリでデータを利用できるようにする場合は、アプリグループを設定して共有ディレクトリを作成できます。拡張機能はそこにデータを書き込むことができ、アプリはそれを読み取ることができます。ただし、それはユーザーが次にアプリを起動するまで発生しません。
Swift 4+(iOS 13でテスト済み)
_@objc
_をopenURL
の宣言に追加する必要があります。つまり、
_@objc func openURL(_ url: URL) -> Bool {
// Code below.
}
_
これがないと、このコンパイラエラーが発生します。
_Argument of '#selector' refers to instance method 'openURL' that is not exposed to Objective-C
_
Swift 3.1(iOS10でテスト済み):の作業ソリューション
独自のURLスキームを作成し、この関数を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
を追加しました#selector(openURL(_:)が見つかるように)UIApplicationからコンパイラを幸せに保ちます。
次に、実際に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 (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
}
}
_
技術的には、共有拡張機能から含まれているアプリを開くことはできませんが、ローカル通知をスケジュールすることができます。それが私がやっていることです。 super.didSelectPostを呼び出す直前に、テキストでローカル通知をスケジュールします。ユーザーが含まれているアプリを開きたい場合は、できます。できなかった場合は、ワークフローを続行できます。私は、それを含むアプリを自動的に開いて、彼らがしていることを妨害するよりも良いアプローチだとさえ思っています。
ホストアプリにカスタムURLスキーマを実装し、openURL(url :)メソッドを呼び出す
like 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)
}
}
共有拡張機能からホストアプリをトリックで開きました。クリアな背景色のウェブビューを使用します。以下はコードです
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];
私はこの問題を抱えていました。iOS11以降では、これまでの回答はどれも機能しません。 JavaScriptコードに完了ハンドラーを追加して、そこからwindow.location="myapp://"
。少しハックですが、見た目は悪くないので、ユーザーはそれに従ってください。
これを行う方法がない(そしてできない)だけでなく、アプリでこれを処理する必要もありません。拡張機能は、メインアプリとまったく同じコードベースでこれを処理することになっています。アプリと拡張ターゲット間で共有される拡張セーフAPIを使用してフレームワークを作成する必要があります。