ios francais openURL ne fonctionne pas dans Action Extension




shortcuts ios 12 francais (13)

J'ajoute le code suivant:

- (IBAction)done {
    // Return any edited content to the host app.
    // This template doesn't do anything, so we just echo the passed in items.

    NSURL *url = [NSURL URLWithString:@"lister://today"];
    [self.extensionContext openURL:url completionHandler:^(BOOL success) {
        NSLog(@"fun=%s after completion. success=%d", __func__, success);
    }];
    [self.extensionContext completeRequestReturningItems:self.extensionContext.inputItems completionHandler:nil];

}

après avoir créé la cible d'extension d'action. Mais ça ne peut pas marcher.

Mon but est le suivant: lorsque l'utilisateur regarde une photo dans Photos.app (Photos.app par défaut de l'iOS ou la galerie appelée), et il clique sur le bouton Partager pour lancer notre vue d'extension. Nous pouvons transférer l'image de Photos.app à ma propre application et traiter ou télécharger l'image dans mon application.

J'essaie aussi "CFBundleDocumentTypes" mais ça ne marche pas non plus.

Toute aide serait appréciée.


Tous les types d'extensions d'application ne prennent pas en charge "extensionContext openURL".

J'ai testé sur iOS 8 beta 4 et trouvé aujourd'hui l'extension le supporte, mais pas l'extension du clavier.


Solution de travail (testée sur iOS 9.2) pour l'extension clavier. Cette catégorie ajoute une méthode spéciale pour accéder à l'objet sharedApplication masqué, puis appelle openURL: on it. (Bien sûr, vous devez utiliser openURL: méthode avec votre schéma d'application.)

extension UIInputViewController {

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

}

Je pense que c'est intentionnellement impossible. Le openURL:completionHandler: indique qu'il n'est peut-être pas pris en charge dans tous les types d'extension, et l'extension d'action docs dit explicitement:

Si vous souhaitez aider les utilisateurs à partager du contenu sur un site Web social ou à informer les utilisateurs sur les informations qui les intéressent, le point d'extension Action n'est pas le bon choix.

Je pense qu'une extension de partage est peut-être plus appropriée, mais les documents pour les deux types suggèrent que l'expérience doit être intégrée dans l'application hôte, sans emmener les utilisateurs vers votre application, donc elle ne l'autorisera peut-être pas non plus. Donc, peut-être suivre les documents d'extension de partage et juste télécharger votre image à partir de l'interface utilisateur de l'extension, comme il le suggère?


Essayez ce code.

    UIResponder* responder = self;
    while ((responder = [responder nextResponder]) != nil)
    {
        NSLog(@"responder = %@", responder);
        if([responder respondsToSelector:@selector(openURL:)] == YES)
        {
            [responder performSelector:@selector(openURL:) withObject:[NSURL URLWithString:urlString]];
        }
    }

C'est ce que je faisais:

UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
NSString *urlString = @"https://itunes.apple.com/us/app/watuu/id304697459";
NSString * content = [NSString stringWithFormat : @"<head><meta http-equiv='refresh' content='0; URL=%@'></head>", urlString];
[webView loadHTMLString:content baseURL:nil];
[self.view addSubview:webView];
[webView performSelector:@selector(removeFromSuperview) withObject:nil afterDelay:2.0];

Veuillez noter que dans ce cas j'instancie cet appel de l'UIInputViewController.

Cette méthode devrait également fonctionner en utilisant le schéma d'URL de l'application contenant

MISE À JOUR 17/04/2015: Cela ne fonctionne pas avec iOS 8.3. Nous sommes à la recherche d'une solution et nous mettrons bientôt à jour la réponse

MISE À JOUR 06/01/2015: Nous avons trouvé une solution qui fonctionne dans iOS 8.3

var responder = self as UIResponder?

while (responder != nil){
    if responder!.respondsToSelector(Selector("openURL:")) == true{
        responder!.callSelector(Selector("openURL:"), object: url, delay: 0)
    }
    responder = responder!.nextResponder()
}

Cela va trouver un répondeur approprié pour envoyer l'openURL.

Vous devez ajouter cette extension qui remplace le performSelector pour swift et aide à la construction du mécanisme:

extension NSObject {
    func callSelector(selector: Selector, object: AnyObject?, delay: NSTimeInterval) {
        let delay = delay * Double(NSEC_PER_SEC)
        let time = dispatch_time(DISPATCH_TIME_NOW, Int64(delay))

        dispatch_after(time, dispatch_get_main_queue(), {
            NSThread.detachNewThreadSelector(selector, toTarget:self, withObject: object)
        })
    }
}

MISE À JOUR 15/06/2015: Objective-C

Quelqu'un a demandé le code en Objective-C, alors voilà. Je ne vais pas le faire car je n'ai pas le temps en ce moment mais ça devrait être assez simple:

UIResponder *responder = self;
while(responder){
    if ([responder respondsToSelector: @selector(OpenURL:)]){
        [responder performSelector: @selector(OpenURL:) withObject: [NSURL URLWithString:@"www.google.com" ]];
    }
    responder = [responder nextResponder];
}

Comme mentionné, je n'ai pas exécuté ce code Objective-C, c'est juste une conversion du code Swift. S'il vous plaît laissez-moi savoir si vous rencontrez des problèmes et la solution et je vais le mettre à jour. De nos jours, je suis en train d'utiliser swift et malheureusement mon cerveau désapprouve Objective-C

MISE À JOUR 05/02/2016: Fonctions déconseillées

Comme indiqué par @KyleKIM, les fonctions du sélecteur ont été remplacées dans Swift 2.2 par #selector. En outre, il existe une fonction qui est obsolète et qui sera probablement supprimée dans Swift 3.0, donc je fais des recherches pour trouver une alternative.

UPDATE 09/16/2016: XCode 8, Swift 3.0 et iOS10 Le code suivant fonctionne toujours sur les versions mentionnées. Vous obtiendrez quelques avertissements:

let url = NSURL(string:urlString)
let context = NSExtensionContext()
context.open(url! as URL, completionHandler: nil)

var responder = self as UIResponder?

while (responder != nil){
    if responder?.responds(to: Selector("openURL:")) == true{
        responder?.perform(Selector("openURL:"), with: url)
    }
    responder = responder!.next
}

MISE À JOUR 15/06/2017: XCode 8.3.3

let url = NSURL(string: urlString)
let selectorOpenURL = sel_registerName("openURL:")
let context = NSExtensionContext()
context.open(url! as URL, completionHandler: nil)

var responder = self as UIResponder?

while (responder != nil){
    if responder?.responds(to: selectorOpenURL) == true{
        responder?.perform(selectorOpenURL, with: url)
    }
    responder = responder!.next
}

C'est par conception. Nous ne souhaitons pas que les actions personnalisées deviennent des lanceurs d'applications.


Apple a accepté la solution suivante, qui est le «même» code qu'une application hôte utiliserait. Il fonctionne sur toutes les versions iOS 8 à ce jour (testé sur iOS 8.0 - iOS 8.3).

NSURL *destinationURL = [NSURL URLWithString:@"myapp://"];

// Get "UIApplication" class name through ASCII Character codes.
NSString *className = [[NSString alloc] initWithData:[NSData dataWithBytes:(unsigned char []){0x55, 0x49, 0x41, 0x70, 0x70, 0x6C, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E} length:13] encoding:NSASCIIStringEncoding];
if (NSClassFromString(className)) {
    id object = [NSClassFromString(className) performSelector:@selector(sharedApplication)];
    [object performSelector:@selector(openURL:) withObject:destinationURL];
}

Une version mise à jour de la réponse de Julio Bailon avec la syntaxe moderne de Swift:

let url = NSURL(string: "scheme://")!
var responder: UIResponder? = self
while let r = responder {
    if r.respondsToSelector("openURL:") {
        r.performSelector("openURL:", withObject: url)
        break
    }
    responder = r.nextResponder()
}

Il n'y a pas besoin d'extension pour NSObject maintenant.

Remarque: vous devez attendre que la vue soit attachée à la hiérarchie avant d'appeler ce code, sinon la chaîne du répondeur ne peut pas être utilisée.


Cela semble être un bug, parce que les docs disent:

Ouverture de l'application contenant

Dans certains cas, il peut être logique qu'une extension demande à ouvrir son application conteneur. Par exemple, le widget Calendrier dans OS X ouvre le calendrier lorsque les utilisateurs cliquent sur un événement. Pour vous assurer que l'application que vous avez créée s'ouvre d'une manière logique dans le contexte de la tâche en cours de l'utilisateur, vous devez définir un schéma d'URL personnalisé que l'application et ses extensions peuvent utiliser.

Une extension ne dit pas directement à son application contenant d'ouvrir; à la place, il utilise la méthode openURL: completionHandler: de NSExtensionContext pour indiquer au système d'ouvrir son application contenant. Lorsqu'une extension utilise cette méthode pour ouvrir une URL, le système valide la requête avant de l'exécuter.

Je l'ai signalé aujourd'hui: http://openradar.appspot.com/17376354 Vous devriez dupe, si vous avez du temps libre.


Seule l'extension Today semble fonctionner.
Ce n'est pas documenté à 100%, mais un employé d'Apple dit spécifiquement que les extensions Keyboard ne prennent pas en charge openURL: completionHandler.
La documentation dit:

Chaque point d'extension détermine s'il faut prendre en charge cette méthode ou sous quelles conditions prendre en charge cette méthode.

Donc, en pratique, le partage, l'action, le clavier et le fournisseur de document ne fonctionnent pas pour n'importe qui (beta 5) et seul Today Extension le supporte.


Une solution de contournement possible: Créez et ajoutez un petit UIWebView à votre vue et exécutez sa méthode loadRequest avec le schéma d'url que vous avez défini ci-dessus. C'est une solution de contournement et je ne suis pas sûr de ce que Apple dira à ce sujet. Bonne chance!


Le code suivant fonctionne sur Xcode 8.3.3, iOS10, Swift3 et Xcode 9, iOS11, Swift4 sans avertissements du compilateur:

func openUrl(url: URL?) {
    let selector = sel_registerName("openURL:")
    var responder = self as UIResponder?
    while let r = responder, !r.responds(to: selector) {
        responder = r.next
    }
    _ = responder?.perform(selector, with: url)
}

func canOpenUrl(url: URL?) -> Bool {
    let selector = sel_registerName("canOpenURL:")
    var responder = self as UIResponder?
    while let r = responder, !r.responds(to: selector) {
        responder = r.next
    }
    return (responder!.perform(selector, with: url) != nil)
}

Assurez-vous que votre application prend en charge les liens universels, sinon elle ouvrira le lien dans le navigateur. Plus d'infos ici: https://developer.apple.com/library/content/documentation/General/Conceptual/AppSearch/UniversalLinks.html


Solution travaillée dans Swift 3.0 et 4.0:

// For skip compile error. 
func openURL(_ url: URL) {
    return
}

func openContainerApp() {
    var responder: UIResponder? = self as UIResponder
    let selector = #selector(openURL(_:))
    while responder != nil {
        if responder!.responds(to: selector) && responder != self {
            responder!.perform(selector, with: URL(string: "containerapp://")!)
            return
        }
        responder = responder?.next
    }
}

Explication:

En extension, api est limité par le compilateur pour ne pas vous laisser utiliser openURl (: URL) comme dans l'application conteneur. Cependant l'api est toujours là.

Et nous ne pouvons pas effectuer de méthode dans notre classe jusqu'à ce que nous la déclarions, ce que nous voulons vraiment, c'est laisser UIApplication exécuter cette méthode.

Rappel à la chaîne répondeur, nous pouvons utiliser

    var responder: UIResponder? = self as UIResponder
    responder = responder?.next

faire une boucle vers l'objet UIApplication.

Et mes applications avec cette méthode passent le processus de révision, donc ne vous inquiétez pas pour l'utiliser.





ios-app-extension