best - shortcuts ios 12 francais




openURL ne fonctionne pas dans Action Extension (11)

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

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.


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
}

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.


Comme le document Apple "Un widget Today (et aucun autre type d'extension d'application) peut demander au système d'ouvrir son application contenant en appelant la méthode openURL: completionHandler: de la classe NSExtensionContext."

Pour d'autres extensions, j'ai utilisé cette solution

UIWebView * webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 0, 0)];
NSString *urlString = @"ownApp://"; // Use other application url schema.

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

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?


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


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.


Solution pour le dernier SDK iOS 10.2. Toutes les solutions précédentes utilisent des API désapprouvées. Cette solution est basée sur la recherche UIApplication UIResponder de l'application d'hébergement (Cette application qui crée un contexte d'exécution pour notre extension). La solution ne peut être fournie qu'en Objective-C car il y a une méthode à 3 arguments à invoquer et c'est impossible à faire avec performSelector: methods. Pour appeler cette méthode non obsolète openURL:options:completionHandler: nous devons utiliser l'instance NSInvocation indisponible dans Swift. La solution fournie peut être appelée depuis Objective-C et Swift (n'importe quelle version). Je dois dire que je ne sais pas encore si la solution fournie sera valide pour le processus de révision Apple.

UIViewController+OpenURL.h

#import <UIKit/UIKit.h>
@interface UIViewController (OpenURL)
- (void)openURL:(nonnull NSURL *)url;
@end

UIViewController+OpenURL.m

#import "UIViewController+OpenURL.h"

@implementation UIViewController (OpenURL)

- (void)openURL:(nonnull NSURL *)url {

    SEL selector = NSSelectorFromString(@"openURL:options:completionHandler:");

    UIResponder* responder = self;
    while ((responder = [responder nextResponder]) != nil) {
        NSLog(@"responder = %@", responder);
        if([responder respondsToSelector:selector] == true) {
            NSMethodSignature *methodSignature = [responder methodSignatureForSelector:selector];
            NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSignature];

            // Arguments
            NSDictionary<NSString *, id> *options = [NSDictionary dictionary];
            void (^completion)(BOOL success) = ^void(BOOL success) {
                NSLog(@"Completions block: %i", success);
            };

            [invocation setTarget: responder];
            [invocation setSelector: selector];
            [invocation setArgument: &url atIndex: 2];
            [invocation setArgument: &options atIndex:3];
            [invocation setArgument: &completion atIndex: 4];
            [invocation invoke];
            break;
        }
    }
}

@end

À partir de Swift 3 Vous ne pouvez l'exécuter que si votre contrôleur de vue est en hiérarchie de vue. C'est le code que je l'utilise:

override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)

        let context = self.extensionContext!
        let userAuthenticated = self.isUserAuthenticated()

        if !userAuthenticated {
            let alert = UIAlertController(title: "Error", message: "User not logged in", preferredStyle: .alert)
            let cancel = UIAlertAction(title: "Cancel", style: .cancel) { _ in
                context.completeRequest(returningItems: nil, completionHandler: nil)
            }
            let login = UIAlertAction(title: "Log In", style: .default, handler: { _ in
                //self.openContainingAppForAuthorization()
                let url = URL(string: "fashionapp://login")!
                self.open(url)
                context.completeRequest(returningItems: nil, completionHandler: nil)
            })

            alert.addAction(cancel)
            alert.addAction(login)
            present(alert, animated: true, completion: nil)
        }
    }

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.


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!


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

}




ios-app-extension