Jembatan JavaScript iOS

101

Saya sedang mengerjakan aplikasi di mana saya akan menggunakan HTML5 di UIWebView dan framework iOS asli secara bersamaan. Saya tahu bahwa saya dapat mengimplementasikan komunikasi antara JavaScript dan Objective-C. Apakah ada perpustakaan yang menyederhanakan penerapan komunikasi ini? Saya tahu bahwa ada beberapa perpustakaan untuk membuat aplikasi iOS asli dalam HTML5 dan javascript (misalnya AppMobi, PhoneGap), tetapi saya tidak yakin apakah ada perpustakaan untuk membantu membuat aplikasi iOS asli dengan penggunaan JavaScript yang berat. Aku ingin:

  1. Jalankan metode JS dari Objective-C
  2. Jalankan metode Objective-C dari JS
  3. Dengarkan peristiwa JS asli dari Objective-C (misalnya peristiwa siap DOM)
andr111
sumber
1
Anda dapat menggunakan WKWebView: panggilan dari javascript window.webkit.messageHandlers. {NAME} .postMessage (message) dan kemudian menanganinya dengan [WKUserContentController addScriptMessageHandler: name:] untuk memanggil Objective-C dari JS
kostyl

Jawaban:

150

Ada beberapa perpustakaan, tetapi saya tidak menggunakan semua ini dalam proyek besar, jadi Anda mungkin ingin mencobanya:

-

Namun, menurut saya ini adalah sesuatu yang cukup sederhana sehingga Anda dapat mencobanya sendiri. Saya pribadi melakukan ini persis ketika saya perlu melakukan itu. Anda juga dapat membuat perpustakaan sederhana yang sesuai dengan kebutuhan Anda.

1. Jalankan metode JS dari Objective-C

Ini sebenarnya hanya satu baris kode.

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

Rincian lebih lanjut tentang Dokumentasi UIWebView resmi .

2. Jalankan metode Objective-C dari JS

Sayangnya ini sedikit lebih kompleks, karena tidak ada properti windowScriptObject (dan kelas) yang sama yang ada di Mac OSX yang memungkinkan komunikasi lengkap antara keduanya.

Namun, Anda dapat dengan mudah memanggil dari URL yang dibuat khusus javascript, seperti:

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

Dan hentikan itu dari Objective-C dengan ini:

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

Ini tidak sebersih yang seharusnya (atau dengan menggunakan windowScriptObject) tetapi berhasil.

3. Dengarkan peristiwa JS asli dari Objective-C (misalnya peristiwa siap DOM)

Dari penjelasan di atas, Anda melihat bahwa jika Anda ingin melakukan itu, Anda harus membuat beberapa kode JavaScript, melampirkannya ke acara yang ingin Anda pantau dan memanggil panggilan yang benar window.locationuntuk kemudian dicegat.

Sekali lagi, tidak bersih sebagaimana mestinya, tetapi berhasil.

Folletto
sumber
12
Petunjuk: jangan beri garis bawah atau serupa dalam skema Anda.
fabb
4
dan mengembalikan nilai saat Anda seharusnya :)
Pizzaiola Gorgonzola
2
Petunjuk Lain: Muat URL di iFrame untuk mencegah kedipan
iCaramba
@Folleto Hai, Anda berkata "tapi saya tidak menggunakan semua ini dalam proyek besar", mengapa tidak ?? apakah Anda menemukan beberapa masalah pada pustaka ini ??? Terima kasih sebelumnya.
JERC
1
Tidak ada masalah. Saya tidak menggunakannya, jadi saya tidak dapat berkomentar tentang penggunaannya pada proyek yang lebih besar. Saya ingin mengklarifikasi itu. :)
Folletto
57

Metode yang disarankan untuk memanggil tujuan c dari JS dalam jawaban yang diterima tidak disarankan. Salah satu contoh masalah: jika Anda membuat dua panggilan berturut-turut secara langsung, salah satunya diabaikan (Anda tidak dapat mengubah lokasi terlalu cepat).

Saya merekomendasikan pendekatan alternatif berikut:

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

Anda memanggil executefungsi berulang kali dan karena setiap panggilan dijalankan dalam iframe-nya sendiri, fungsi tersebut tidak boleh diabaikan saat dipanggil dengan cepat.

Penghargaan untuk orang ini .

talkol
sumber
1
Anda juga dapat menggunakan antrean dalam JavaScript yang dapat diambil iOS untuk menghindari hilangnya pesan dari .src yang berubah terlalu cepat. Penerapan yang ditautkan di atas, github.com/marcuswestin/WebViewJavascriptBridge , menggunakan pendekatan antrian.
kevintcoughlin
12

Pembaruan: Ini telah berubah di iOS 8. Jawaban saya berlaku untuk versi sebelumnya.

Alternatifnya, yang mungkin membuat Anda ditolak dari app store, adalah dengan menggunakan WebScriptObject.

API ini publik di OSX tetapi tidak di iOS.

Anda perlu mendefinisikan antarmuka ke kelas internal.

@interface WebScriptObject: NSObject
@end

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

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

Anda perlu menentukan objek Anda yang akan berfungsi sebagai WebScriptObject Anda

@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

Sekarang setel instance itu ke UIWebView Anda

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

Sekarang di dalam javascript Anda, Anda dapat memanggil:

yourBridge.someEvent(100, "hello");
yourBridge.testfoo();
Joseph Lennox
sumber
Aplikasi saya ditolak di App Store. Masalah yang saya terima adalah "Aplikasi berisi atau mewarisi dari kelas non-publik di AppName: UIWebDocumentView"
chandru
5

Di iOS8, Anda dapat melihat WKWebView, bukan UIWebView . Ini memiliki kelas berikut: WKScriptMessageHandler: Menyediakan metode untuk menerima pesan dari JavaScript yang berjalan di halaman web.

Alex Stone
sumber
Tentu saja, tetapi metode ini tidak mengeksekusi secara kontinu .. ini adalah bug dalam kerangka kerja yang disediakan oleh apple
Josh Kethepalli
WKWebViewmemiliki banyak masalah, termasuk tidak menangani cookie dengan benar. Hanya peringatan bagi siapa saja yang datang ke sini berpikir itu akan menyelesaikan semua masalah Anda - google sekitar sedikit sebelum mengadopsinya.
CupawnTae
3

Ini bisa dilakukan dengan iOS7, periksa http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

CocoaChris
sumber
3
Tidak juga: JavaScriptCore tidak berinteraksi dengan UIWebViews. Ini adalah alat yang hebat, tetapi untuk kelas masalah yang berbeda. ;)
Folletto
1
Ya, itu benar: JSContext * context = [_webView valueForKeyPath: @ "documentView.webView.mainFrame.javaScriptContext"]; Tetapi JavaScriptCore saat ini terlalu bermasalah untuk digunakan secara nyata.
malhal
0

Taruhan terbaik Anda adalah penawaran Appcelerators Titanium. Mereka telah membangun jembatan javascript Obj-C menggunakan mesin JavascriptCore mesin V8 yang digunakan oleh webkit. Ini juga open source, jadi Anda akan bisa mendownloadnya dan bermain-main dengan Obj-C sesuka Anda.

hova
sumber
@hova, tidak ada jembatan Obj-C V8. V8 hanya dimanfaatkan untuk Android.
Ross
0

Lihat proyek KirinJS: Kirin JS yang memungkinkan penggunaan Javascript untuk logika aplikasi dan UI asli yang memadai untuk platform tempat menjalankannya.

rochal
sumber
apakah masih ada yang berkontribusi untuk kirin? saya tidak melihat aktivitas apa pun beberapa bulan terakhir ...
Sam
0

Saya membuat perpustakaan seperti WebViewJavascriptBridge, tetapi lebih mirip JQuery, lebih mudah diatur dan lebih mudah digunakan. Tidak bergantung pada jQuery (meskipun untuk kreditnya, seandainya saya tahu WebViewJavascriptBridge ada sebelum menulis ini, saya mungkin hanya menahan sedikit sebelum menyelam). Biarkan aku tahu apa yang kamu pikirkan! jockeyjs

Tim Coulter
sumber
0

Jika Anda menggunakan WKWebView di iOS 8, lihat XWebView yang secara otomatis dapat mengekspos antarmuka asli ke javascript.

suar
sumber