105 votes

Pont JavaScript pour iOS

Je travaille sur une application où je vais utiliser à la fois le HTML5 dans UIWebView et le framework natif iOS. Je sais que je peux mettre en œuvre la communication entre JavaScript et Objective-C. Existe-t-il des bibliothèques qui simplifient la mise en œuvre de cette communication ? Je sais qu'il existe plusieurs bibliothèques pour créer des applications iOS natives en HTML5 et JavaScript (par exemple AppMobi, PhoneGap), mais je ne suis pas sûr qu'il existe une bibliothèque pour aider à créer des applications iOS natives avec une utilisation intensive de JavaScript. J'en ai besoin :

  1. Exécuter des méthodes JS depuis Objective-C
  2. Exécuter des méthodes Objective-C depuis JS
  3. Écouter les événements JS natifs depuis Objective-C (par exemple l'événement DOM ready)

1 votes

Vous pouvez utiliser WKWebView : appel depuis javascript window.webkit.messageHandlers.{NAME}.postMessage(message) et ensuite le gérer avec [WKUserContentController addScriptMessageHandler:name :] pour appeler Objective-C depuis JS

153voto

Folletto Points 1035

Il existe quelques bibliothèques, mais je n'ai pas utilisé l'une d'entre elles dans de grands projets, alors vous pourriez vouloir les essayer :

-

Cependant, je pense que c'est quelque chose d'assez simple pour que vous puissiez l'essayer vous-même. J'ai personnellement fait exactement cela lorsque j'en ai eu besoin. Vous pouvez également créer une bibliothèque simple qui répond à vos besoins.

1. Exécuter des méthodes JS depuis Objective-C

Il s'agit en fait d'une seule ligne de code.

NSString *returnvalue = [webView stringByEvaluatingJavaScriptFromString:@"your javascript code string here"];

Plus de détails sur le site officiel Documentation sur les UIWebView .

2. Exécuter des méthodes Objective-C depuis JS

C'est malheureusement un peu plus complexe, car il n'y a pas la même propriété (et classe) windowScriptObject qui existe sur Mac OSX permettant une communication complète entre les deux.

Cependant, vous pouvez facilement appeler à partir de javascript des URLs personnalisées, comme :

window.location = yourscheme://callfunction/parameter1/parameter2?parameter3=value

Et l'intercepter depuis l'Objective-C avec ceci :

- (BOOL)webView:(UIWebView*)webView shouldStartLoadWithRequest:(NSURLRequest*)request navigationType:(UIWebViewNavigationType)navigationType {
   NSURL *URL = [request URL]; 
   if ([[URL scheme] isEqualToString:@"yourscheme"]) {
       // parse the rest of the URL object and execute functions
   } 
}

Ce n'est pas aussi propre que cela devrait l'être (ou en utilisant windowScriptObject) mais cela fonctionne.

3. Ecouter les événements natifs de JS depuis Objective-C (par exemple l'événement DOM ready)

D'après l'explication ci-dessus, vous voyez que si vous voulez faire cela, vous devez créer du code JavaScript, l'attacher à l'événement que vous voulez surveiller et appeler la bonne méthode. window.location pour être ensuite intercepté.

Encore une fois, ce n'est pas aussi propre que ça devrait l'être, mais ça fonctionne.

12 votes

Conseil : ne mettez pas de caractères de soulignement ou autres dans votre schéma.

4 votes

Et renvoyer une valeur quand vous êtes censé le faire :)

2 votes

Autre astuce : chargez l'URL dans une iFrame pour éviter le scintillement.

57voto

talkol Points 4279

La méthode suggérée d'appeler l'objectif c depuis JS dans la réponse acceptée n'est pas recommandée. Un exemple de problème : si vous faites deux appels consécutifs immédiats, l'un est ignoré (vous ne pouvez pas changer de lieu trop rapidement).

Je recommande l'approche alternative suivante :

function execute(url) 
{
  var iframe = document.createElement("IFRAME");
  iframe.setAttribute("src", url);
  document.documentElement.appendChild(iframe);
  iframe.parentNode.removeChild(iframe);
  iframe = null;
}

Vous appelez le execute de manière répétée et comme chaque appel s'exécute dans sa propre iframe, ils ne doivent pas être ignorés lorsqu'ils sont appelés rapidement.

Crédits pour ce type .

1 votes

Vous pouvez également utiliser une file d'attente en JavaScript que iOS peut récupérer pour éviter de perdre les messages provenant de .src qui change trop rapidement. L'implémentation liée ci-dessus, github.com/marcuswestin/WebViewJavascriptBridge utilise une approche de file d'attente.

12voto

Joseph Lennox Points 821

Mise à jour : Cela a changé dans iOS 8. Ma réponse s'applique aux versions précédentes.

Une autre solution, qui risque de vous faire rejeter de la boutique d'applications, consiste à utiliser WebScriptObject.

Ces API sont publiques sur OSX mais ne le sont pas sur iOS.

Vous devez définir des interfaces pour les classes internes.

@interface WebScriptObject: NSObject
@end

@interface WebView
- (WebScriptObject *)windowScriptObject;
@end

@interface UIWebDocumentView: UIView
- (WebView *)webView;
@end

Vous devez définir l'objet qui servira de WebScriptObject.

@interface WebScriptBridge: NSObject
- (void)someEvent: (uint64_t)foo :(NSString *)bar;
- (void)testfoo;
+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
+ (WebScriptBridge*)getWebScriptBridge;
@end

static WebScriptBridge *gWebScriptBridge = nil;

@implementation WebScriptBridge
- (void)someEvent: (uint64_t)foo :(NSString *)bar
{
    NSLog(bar);
}

-(void)testfoo {
    NSLog(@"testfoo!");
}

+ (BOOL)isKeyExcludedFromWebScript:(const char *)name;
{
    return NO;
}

+ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector;
{
    return NO;
}

+ (NSString *)webScriptNameForSelector:(SEL)sel
{
    // Naming rules can be found at: https://developer.apple.com/library/mac/documentation/Cocoa/Reference/WebKit/Protocols/WebScripting_Protocol/Reference/Reference.html
    if (sel == @selector(testfoo)) return @"testfoo";
    if (sel == @selector(someEvent::)) return @"someEvent";

    return nil;
}
+ (WebScriptBridge*)getWebScriptBridge {
    if (gWebScriptBridge == nil)
        gWebScriptBridge = [WebScriptBridge new];

    return gWebScriptBridge;
}
@end

Maintenant, définissez une instance de votre UIWebView.

if ([uiWebView.subviews count] > 0) {
    UIView *scrollView = uiWebView.subviews[0];

    for (UIView *childView in scrollView.subviews) {
        if ([childView isKindOfClass:[UIWebDocumentView class]]) {
            UIWebDocumentView *documentView = (UIWebDocumentView *)childView;
            WebScriptObject *wso = documentView.webView.windowScriptObject;

            [wso setValue:[WebScriptBridge getWebScriptBridge] forKey:@"yourBridge"];
        }
    }
}

Maintenant, dans votre javascript, vous pouvez appeler :

yourBridge.someEvent(100, "hello");
yourBridge.testfoo();

0 votes

Mon application est rejetée dans l'App Store. Le problème que j'ai reçu était "L'application contient ou hérite de classes non publiques dans AppName : UIWebDocumentView "

6voto

Alex Stone Points 8138

Dans iOS8, vous pouvez utiliser WKWebView au lieu de UIWebView. . Celui-ci a la classe suivante : WKScriptMessageHandler : Fournit une méthode pour recevoir des messages de JavaScript s'exécutant dans une page Web.

0 votes

Bien sûr, mais la méthode ne s'exécute pas en continu c'est un bug dans le cadre fourni par apple.

0 votes

WKWebView a beaucoup de problèmes, y compris le fait de ne pas gérer les cookies correctement. Juste un avertissement pour tous ceux qui viennent ici en pensant que cela résoudra tous leurs problèmes - cherchez un peu sur Google avant de l'adopter.

3voto

CocoaChris Points 134

3 votes

Pas vraiment : JavaScriptCore n'interagit pas avec les UIWebViews. C'est un outil formidable, mais pour une autre catégorie de problèmes ;)

1 votes

Oui, c'est vrai : JSContext* context = [_webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"] ; Mais JavaScriptCore est trop bogué en ce moment pour être d'une réelle utilité.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X