iOS JavaScript köprüsü


102

Hem UIWebView'da HTML5 hem de yerel iOS çerçevesini birlikte kullanacağım bir uygulama üzerinde çalışıyorum. JavaScript ve Objective-C arasında iletişim kurabileceğimi biliyorum. Bu iletişimi uygulamayı basitleştiren herhangi bir kitaplık var mı? HTML5 ve javascript'te yerel iOS uygulamaları oluşturmak için birkaç kitaplık olduğunu biliyorum (örneğin, AppMobi, PhoneGap), ancak yoğun JavaScript kullanımına sahip yerel iOS uygulamaları oluşturmaya yardımcı olacak bir kitaplık olup olmadığından emin değilim. Yapmam gerek:

  1. Objective-C'den JS yöntemlerini yürütün
  2. JS'den Objective-C yöntemlerini yürütün
  3. Objective-C'den yerel JS olaylarını dinleyin (örneğin, DOM'a hazır olay)

1
WKWebView'ı kullanabilirsiniz: javascript window.webkit.messageHandlers çağrısı. {NAME} .postMessage (mesaj) ve ardından [WKUserContentController addScriptMessageHandler: name:] ile JS'den Objective-C'yi çağırmak için
kostyl

Yanıtlar:


150

Birkaç kütüphane var, ancak bunların hiçbirini büyük projelerde kullanmadım, bu yüzden onları denemek isteyebilirsiniz:

-

Ancak, kendi kendinize deneyebileceğiniz kadar basit bir şey olduğunu düşünüyorum. Bunu yapmam gerektiğinde şahsen tam olarak bunu yaptım. İhtiyaçlarınıza uyan basit bir kitaplık da oluşturabilirsiniz.

1. Objective-C'den JS yöntemlerini yürütün

Bu gerçekten sadece bir satır koddur.

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

Resmi UIWebView Belgeleri hakkında daha fazla ayrıntı .

2. JS'den Objective-C yöntemlerini yürütün

Bu maalesef biraz daha karmaşıktır, çünkü Mac OSX'te var olan windowScriptObject özelliği (ve sınıfı) ikisi arasında tam iletişime izin veren aynı değildir.

Ancak, aşağıdaki gibi özel javascript URL'lerinden kolayca arayabilirsiniz:

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

Ve bununla Objective-C'den onu durdurun:

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

Bu olması gerektiği kadar temiz değil (veya windowScriptObject kullanarak) ama işe yarıyor.

3. Objective-C'den yerel JS olaylarını dinleyin (örneğin, DOM'a hazır olay)

Yukarıdaki açıklamadan, bunu yapmak istiyorsanız, bazı JavaScript kodu oluşturmanız, onu izlemek istediğiniz olaya eklemeniz ve window.locationdaha sonra yakalanması için doğru çağrıyı çağırmanız gerektiğini görüyorsunuz .

Yine olması gerektiği gibi temiz değil ama işe yarıyor.


12
İpucu: Planınıza alt çizgi veya benzeri bir şey koymayın.
fabb

4
ve yapmanız gerektiğinde bir değer döndürmek :)
Pizzaiola Gorgonzola

2
Başka Bir İpucu:
Titremeyi

@Folleto Merhaba, "ama bunların hiçbirini büyük projelerde kullanmadım" dediniz, neden olmasın? bu kütüphanelerde birkaç sorun buldunuz mu ??? Şimdiden teşekkürler.
JERC

1
Sorun yok. Bunları kullanmadım, bu yüzden daha büyük projelerde kullanma hakkında yorum yapamam. Bunu açıklığa kavuşturmak istedim. :)
Folletto

57

Kabul edilen cevapta JS'den c hedefini çağırmak için önerilen yöntem tavsiye edilmez. Sorunlara bir örnek: Arka arkaya iki arama yaparsanız, biri göz ardı edilir (konumu çok hızlı değiştiremezsiniz).

Aşağıdaki alternatif yaklaşımı tavsiye ederim:

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

executeİşlevi tekrar tekrar çağırırsınız ve her çağrı kendi iframe içinde yürütüldüğünden, hızlı çağrıldıklarında göz ardı edilmemelidirler.

Bu adama kredi .


1
.Src'den gelen iletilerin çok hızlı değişmesini önlemek için iOS'un alabileceği bir JavaScript kuyruğu da kullanabilirsiniz. Yukarıda bağlantısı verilen github.com/marcuswestin/WebViewJavascriptBridge uygulaması , bir kuyruk yaklaşımı kullanır.
kevintcoughlin

12

Güncelleme: Bu iOS 8'de değişti. Cevabım önceki sürümler için geçerli.

Uygulama mağazasından reddedilmenize neden olabilecek bir alternatif, WebScriptObject kullanmaktır.

Bu API'ler OSX'te geneldir ancak iOS'ta değildir.

Dahili sınıflara arayüz tanımlamanız gerekir.

@interface WebScriptObject: NSObject
@end

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

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

WebScriptObject'iniz olarak hizmet verecek nesnenizi tanımlamanız gerekir.

@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

Şimdi UIWebView'unuza bir örnek ayarlayın

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

Şimdi javascript'inizin içinde arayabilirsin:

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

Uygulamam uygulama mağazasında reddedildi. Aldığım sorun "Uygulama, AppName'de herkese açık olmayan sınıfları içeriyor veya bu sınıflardan miras alıyor: UIWebDocumentView"
chandru

5

İOS8'de UIWebView yerine WKWebView'a bakabilirsiniz . Bu, aşağıdaki sınıfa sahiptir: WKScriptMessageHandler: Bir web sayfasında çalışan JavaScript'ten mesaj almak için bir yöntem sağlar.


Elbette, ancak yöntem sürekli çalışmıyor .. bu apple tarafından sağlanan çerçevede bir hata
Josh Kethepalli

WKWebViewçerezlerin düzgün şekilde kullanılmaması da dahil olmak üzere birçok sorunu vardır. Buraya gelip tüm sorunlarınızı çözeceğini düşünen herkes için bir uyarı - benimsemeden önce biraz google.
CupawnTae


0

En iyi bahsiniz, Appcelerators Titanium teklifidir. Webkit tarafından kullanılan V8 motoru JavascriptCore motorunu kullanarak zaten bir Obj-C javascript köprüsü oluşturdular . Aynı zamanda açık kaynaklıdır, bu nedenle onu indirebilir ve Obj-C ile istediğiniz gibi kurcalayabilirsiniz.


@hova, Obj-C V8 köprüsü yok. V8 yalnızca Android için kullanılır.
Ross

0

KirinJS projesine bir göz atın: Uygulama mantığı ve üzerinde çalıştığı platforma uygun yerel kullanıcı arayüzü için Javascript kullanımına izin veren Kirin JS .


Hala kirin'e katkıda bulunan var mı? son birkaç ayda herhangi bir aktivite görmüyorum ...
Sam

0

WebViewJavascriptBridge gibi bir kitaplık oluşturdum, ancak daha JQuery benzeri, kurulumu daha kolay ve kullanımı daha kolay. JQuery'ye güvenmiyor (kredisine rağmen, WebViewJavascriptBridge'in bunu yazmadan önce var olduğunu bilseydim, içeri girmeden önce biraz geride kalmış olabilirdim). Ne düşündüğü söyle! jockeyjs


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.