Расширение Share для открытия приложения
Я хочу создать функцию совместного использования стилей Android для своего приложения.
Я создал расширение общего доступа, которое вызывается, когда вы выбираете фотографии внутри приложения для фотодокументов и нажимаете "Поделиться".
Теперь я хочу, чтобы эти фотографии отправлялись в основное приложение и обрабатывались там.
Мой вопрос:
- Может ли iOS открыть мое приложение после нажатия кнопки в окне расширения общего доступа?
- Как получить файлы изображений внутри основного приложения?
Ответы
Ответ 1
В настоящее время нет способа сделать это. Расширение общего доступа не может открыть содержащее приложение.
Подходящий подход для расширений для общего доступа состоит в том, что они сами обрабатывают всю необходимую работу. Расширения могут совместно использовать код с их приложениями, используя пользовательские фреймворки, поэтому в большинстве случаев это не проблема.
Если вы хотите сделать доступными данные для своего приложения, вы можете настроить группу приложений, чтобы у вас был общий каталог. Расширение может записывать данные там, и приложение может его прочитать. Это произойдет не до следующего раза, когда пользователь запустит приложение.
Ответ 2
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 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 (_ :)) может быть найденным).
Затем я прохожу по респондентам, пока не найду тот, который действительно получен из 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 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
}
}
Ответ 3
Я открыл приложение-хост из общего расширения с трюком.
Использование веб-просмотра с четким фоном.
ниже приведен код
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];
Ответ 4
Технически вы не можете открыть приложение, содержащее расширение общего доступа, но вы можете запланировать локальное уведомление, и это то, что я в итоге делаю. Непосредственно перед вызовом super.didSelectPost я планирую локальное уведомление с некоторым текстом, и если пользователь хочет открыть содержащее приложение, он может, а если нет - может продолжить свой рабочий процесс. Я даже думаю, что это лучший подход, чем автоматическое открытие, содержащее приложение, и нарушение их работы.
Ответ 5
Внедрить пользовательскую схему url в хост-приложении и вызвать openURL (метод url:)
как openURL (url: NSURL (строка: "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)
}
}
Ответ 6
У меня была эта проблема, и в iOS 11+ ни один из предыдущих ответов не работает. В итоге я добавил обработчик завершения в свой код JavaScript и оттуда установил window.location="myapp://"
. Это немного странно, но это не выглядит плохо, и пользователь может следовать за ним.
Ответ 7
Не только для этого нет (и не будет):
нет НЕОБХОДИМОСТИ для этого в приложении.
Предполагается, что расширение должно справиться с этим
как и основное приложение. Вы должны создать структуру
с API безопасности расширения, совместно используемым между приложением и целями extesnion.
Это главная тема здесь:
https://developer.apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1
Ответ 8
EDIT: это решение работает на сегодняшний день (Widget).
Расширение может открыть приложение для хостинга:
- (IBAction)launchHostingApp:(id)sender
{
NSURL *pjURL = [NSURL URLWithString:@"hostingapp://home"];
[self.extensionContext openURL:pjURL completionHandler:nil];
}
И как Apple говорит в "Сценарии использования сообщества" :
Расширение напрямую не сообщает об открытии приложения, содержащего его; вместо этого он использует метод openURL: completeHandler: NSExtensionContext, чтобы сообщить системе открыть его содержащее приложение. Когда расширение использует этот метод для открытия URL-адреса, система проверяет запрос перед его выполнением.