Ponte JavaScript iOS

Sto lavorando a un’app in cui utilizzerò sia HTML5 in UIWebView sia framework iOS nativo. So che posso implementare la comunicazione tra JavaScript e Objective-C. Ci sono librerie che semplificano l’implementazione di questa comunicazione? So che ci sono diverse librerie per creare app iOS native in HTML5 e javascript (ad esempio AppMobi, PhoneGap), ma non sono sicuro che esista una libreria che aiuti a creare app iOS native con un uso intensivo di JavaScript. Ho bisogno di:

  1. Esegui i metodi JS da Objective-C
  2. Esegui i metodi Objective-C da JS
  3. Ascolta gli eventi JS nativi da Objective-C (ad esempio, evento pronto per DOM)

Ci sono alcune librerie, ma non ho usato nessuno di questi in grandi progetti, quindi potresti voler provarli:

Tuttavia, penso che sia qualcosa di abbastanza semplice da poter provare da solo. Ho fatto personalmente esattamente questo quando avevo bisogno di farlo. Potresti anche creare una libreria semplice adatta alle tue esigenze.

1. Esegui i metodi JS da Objective-C

Questa è solo una riga di codice.

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

Maggiori dettagli sulla documentazione ufficiale di UIWebView .

2. Esegui i metodi Objective-C da JS

Questo è sfortunatamente leggermente più complesso, perché non esiste la stessa proprietà windowScriptObject (e class) esistente su Mac OSX che consente una comunicazione completa tra i due.

Tuttavia, puoi chiamare facilmente da URL personalizzati su JavaScript, come:

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

E intercettarlo da Objective-C con questo:

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

Questo non è pulito come dovrebbe (o usando windowScriptObject) ma funziona.

3. Ascolta gli eventi JS nativi da Objective-C (ad esempio, evento pronto per DOM)

Dalla spiegazione precedente, vedi che se vuoi farlo, devi creare un codice JavaScript, collegarlo all’evento che vuoi monitorare e chiamare la chiamata window.location corretta per essere poi intercettata.

Ancora una volta, non pulito come dovrebbe essere, ma funziona.

Il metodo suggerito per chiamare l’objective c da JS nella risposta accettata non è raccomandato. Un esempio di problemi: se si effettuano due chiamate consecutive consecutive una viene ignorata (non è ansible cambiare la posizione troppo rapidamente).

Raccomando il seguente approccio alternativo:

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

execute ripetutamente la funzione execute e poiché ogni chiamata viene eseguita nel proprio iframe, non dovrebbero essere ignorate quando vengono richiamate rapidamente.

Crediti a questo ragazzo .

Aggiornamento: questo è cambiato in iOS 8. La mia risposta si applica alle versioni precedenti.

Un’alternativa, che potrebbe farti rifiutare dall’app store, è utilizzare WebScriptObject.

Queste API sono pubbliche su OSX ma non su iOS.

È necessario definire le interfacce per le classi interne.

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

È necessario definire l’object che verrà utilizzato come 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 

Ora imposta un’istanza su 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"]; } } } 

Ora all’interno del tuo javascript puoi chiamare:

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

In iOS8 puoi guardare WKWebView invece di UIWebView . Questo ha la seguente class: WKScriptMessageHandler: fornisce un metodo per ricevere messaggi da JavaScript in esecuzione in una pagina Web.

La tua migliore scommessa è l’offerta di Appcelerators Titanium. Hanno già costruito un ponte javascript Obj-C usando il motore JavascriptCore del motore V8 usato da webkit. È anche open source, quindi potrai scaricarlo e armeggiare con Obj-C come preferisci.

Dai un’occhiata al progetto KirinJS: Kirin JS che permette di usare Javascript per la logica dell’applicazione e l’interfaccia utente nativa adeguata alla piattaforma su cui gira.

Ho creato una libreria come WebViewJavascriptBridge, ma è più simile a JQuery, è più facile da configurare ed è più facile da usare. Non si basa su jQuery (anche se a suo merito, se avessi saputo che WebViewJavascriptBridge esisteva prima di scriverlo, potrei averlo trattenuto leggermente prima di immergerti). Fatemi sapere cosa ne pensate! jockeyjs

Se utilizzi WKWebView su iOS 8, dai un’occhiata a XWebView che può esporre automaticamente l’interfaccia nativa a javascript.