web-dev-qa-db-fra.com

Partager l'extension pour ouvrir l'application contenant

Je souhaite créer une fonctionnalité de partage de style Android pour mon application. J'ai créé une extension de partage qui est appelée lorsque vous sélectionnez des images dans l'application photo et que vous appuyez sur Partager. Maintenant, je veux que ces images soient envoyées à l'application principale et traitées ici. .____.] Ma question est la suivante:

  1. IOS peut-il ouvrir mon application après avoir appuyé sur un bouton dans la fenêtre d'extension de partage?
  2. Comment puis-je obtenir les fichiers image dans mon application principale?
15
zanzoken

Actuellement, il n'y a aucun moyen de le faire. Une extension de partage ne peut pas ouvrir l'application contenant.

L'approche prévue pour les extensions de partage consiste à gérer elles-mêmes tout le travail nécessaire. Les extensions peuvent partager du code avec les applications qu'elles contiennent en utilisant des frameworks personnalisés, donc dans la plupart des cas, ce n'est pas un problème.

Si vous souhaitez rendre des données disponibles pour votre application, vous pouvez configurer un groupe d'applications de sorte à disposer d'un répertoire partagé. L'extension peut y écrire des données et l'application peut les lire. Cela ne se produira pas avant la prochaine fois que l'utilisateur lancera l'application.

8
Tom Harrington

Solution de travail dans Swift 3.1 (testée dans iOS10):

Vous devez créer votre propre schéma d'URL , puis ajouter cette fonction à votre ViewController et l'appeler avec 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
}

Édition: Notes pour clarification: openURL est une méthode d’UIApplication - puisque votre ShareExtension n’est pas dérivée de UIApplication, j’ai ajouté ma propre openURL avec la même définition que celle d’UIApplication pour que le compilateur reste heureux ( de sorte que #selector (openURL (_ :) peut être trouvé).

Ensuite, je passe en revue les répondants jusqu'à ce que j'en trouve un qui dérive vraiment de UIApplication et que j'appelle openURL à ce sujet. 

Un exemple plus détaillé de code qui copie les fichiers d’un ShareExtension dans un répertoire local, en sérialisant les noms de fichiers et en appelant openURL sur une autre application:

//
//  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
}
}
21
coyer

J'ai ouvert l'application hôte à partir d'une extension partagée avec une astuce. Utilisation d'une vue Web avec une couleur d'arrière-plan claire. Est le code ci-dessous.

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

Implémentez un schéma d'URL personnalisé dans l'application hôte et appelez la méthode openURL (url :)

comme openURL (url: NSURL (string: "nom_schéma: //"))

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

}
2
SPatel

Techniquement, vous ne pouvez pas ouvrir une application contenant une extension de partage, mais vous pouvez planifier une notification locale, et c'est ce que je finis par faire. Juste avant d'appeler super.didSelectPost, je planifie une notification locale avec du texte. Si un utilisateur souhaite ouvrir une application contenant l'application, il peut le faire, sinon, il peut poursuivre son flux de travail. Je pense même que c'est une meilleure approche que d'ouvrir automatiquement l'application contenant et de perturber ce qu'ils font.

1
Cherpak Evgeny

J'avais ce problème et dans iOS 11+, aucune des réponses précédentes ne fonctionne. J'ai fini par ajouter un gestionnaire d'achèvement à mon code JavaScript, puis à partir de window.location="myapp://". C'est un peu hacky mais cela ne semble pas trop grave et l'utilisateur peut suivre.

0
Nylon

Non seulement il n’ya aucun moyen (et ne le sera pas) de faire cela: Il n’ya pas BESOIN de gérer cela dans l’application. L’extension est supposée gérer cela avec le très même base de code que l'application principale. Vous devez créer une structure Avec une API sécurisée pour l'extension partagée entre l'application et les cibles externes.

Voici le sujet principal ici: https://developer.Apple.com/library/content/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//Apple_ref/doc/uid/TP40014214- CH21-SW1

0
Anton Tropashko