Мост iOS JavaScript

102

Я работаю над приложением, в котором я собираюсь использовать как HTML5 в UIWebView, так и собственную платформу iOS. Я знаю, что могу реализовать связь между JavaScript и Objective-C. Существуют ли библиотеки, упрощающие реализацию этого взаимодействия? Я знаю, что существует несколько библиотек для создания собственных приложений iOS на HTML5 и javascript (например, AppMobi, PhoneGap), но я не уверен, есть ли библиотека, помогающая создавать собственные приложения iOS с интенсивным использованием JavaScript. Мне нужно:

  1. Выполнять JS-методы из Objective-C
  2. Выполнение методов Objective-C из JS
  3. Слушайте собственные события JS от Objective-C (например, событие готовности DOM)
andr111
источник
1
Вы можете использовать WKWebView: call from javascript window.webkit.messageHandlers. {NAME} .postMessage (message), а затем обработать его с помощью [WKUserContentController addScriptMessageHandler: name:] для вызова Objective-C из JS
костыл

Ответы:

150

Есть несколько библиотек, но я не использовал ни одну из них в больших проектах, так что вы можете попробовать их:

-

Тем не менее, я думаю, что это что-то достаточно простое, чтобы вы могли попробовать сами. Я лично делал именно это, когда мне нужно было это сделать. Вы также можете создать простую библиотеку, которая соответствует вашим потребностям.

1. Выполните методы JS из Objective-C

На самом деле это всего лишь одна строчка кода.

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

Подробнее в официальной документации UIWebView .

2. Выполнение методов Objective-C из JS.

К сожалению, это немного сложнее, потому что не существует того же свойства (и класса) windowScriptObject, которое существует в Mac OSX, обеспечивающего полную связь между ними.

Тем не менее, вы можете легко позвонить с пользовательских URL-адресов javascript, например:

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

И перехватите его из Objective-C с помощью этого:

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

Это не так чисто, как должно быть (или при использовании windowScriptObject), но работает.

3. Слушайте собственные JS-события из Objective-C (например, событие готовности DOM)

Из приведенного выше объяснения вы видите, что если вы хотите это сделать, вам нужно создать некоторый код JavaScript, прикрепить его к событию, которое вы хотите отслеживать, и вызвать правильный window.locationвызов, который будет затем перехвачен.

Опять не чисто, как должно быть, но работает.

Фоллетто
источник
12
Подсказка: не используйте подчеркивание или что-то подобное в своей схеме.
fabb
4
и верните значение, когда должны :)
Pizzaiola Gorgonzola
2
Еще один совет: загрузите URL-адрес в iFrame, чтобы предотвратить мерцание
iCaramba
@Folleto Привет, ты сказал: «Но я не использовал ничего из этого в больших проектах», почему бы и нет ?? Вы обнаружили несколько проблем с этими библиотеками ??? Заранее спасибо.
JERC
1
Без вопросов. Я просто не использовал их, поэтому не могу комментировать их использование в более крупных проектах. Я хотел это прояснить. :)
Фоллетто
57

Предлагаемый метод вызова цели c из JS в принятом ответе не рекомендуется. Один из примеров проблем: если вы сделаете два последовательных вызова сразу же, один будет проигнорирован (вы не можете изменить местоположение слишком быстро).

Я рекомендую следующий альтернативный подход:

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

Вы вызываете executeфункцию несколько раз, и поскольку каждый вызов выполняется в собственном iframe, их нельзя игнорировать при быстром вызове.

Кредиты этому парню .

талькол
источник
1
Вы также можете использовать очередь в JavaScript, которую iOS может извлечь, чтобы избежать потери сообщений из слишком быстрого изменения .src. Связанная выше реализация, github.com/marcuswestin/WebViewJavascriptBridge , использует подход очереди.
kevintcoughlin
12

Обновление: это изменилось в iOS 8. Мой ответ относится к предыдущим версиям.

Альтернативный вариант, который может привести к отказу в доступе в магазине приложений, - использовать WebScriptObject.

Эти API-интерфейсы общедоступны в OSX, но отсутствуют в iOS.

Вам необходимо определить интерфейсы для внутренних классов.

@interface WebScriptObject: NSObject
@end

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

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

Вам необходимо определить свой объект, который будет служить вашим 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

Теперь установите этот экземпляр в свой 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"];
        }
    }
}

Теперь внутри вашего javascript вы можете вызвать:

yourBridge.someEvent(100, "hello");
yourBridge.testfoo();
Джозеф Леннокс
источник
Мое приложение отклонено в магазине приложений. Я получил следующую проблему: «Приложение содержит закрытые классы в AppName: UIWebDocumentView или наследует от них»
chandru
5

В iOS8 вы можете посмотреть WKWebView вместо UIWebView . У него есть следующий класс: WKScriptMessageHandler: предоставляет метод для получения сообщений от JavaScript, запущенного на веб-странице.

Алекс Стоун
источник
Конечно, но метод не выполняется непрерывно .. это ошибка в структуре, предоставляемой Apple
Джош Кетепалли
WKWebViewимеет много проблем, включая неправильную обработку файлов cookie. Просто предупреждение для всех, кто приходит сюда, думая, что это решит все ваши проблемы - немного погуглите, прежде чем принять его.
CupawnTae
3

Это возможно с iOS7, оформить заказ http://blog.bignerdranch.com/3784-javascriptcore-and-ios-7/

КакаоКрис
источник
3
Не совсем: JavaScriptCore не взаимодействует с UIWebViews. Это отличный инструмент, но для другого класса проблем. ;)
Folletto 06
1
Да, это так: JSContext * context = [_webView valueForKeyPath: @ "documentView.webView.mainFrame.javaScriptContext"]; Но JavaScriptCore на данный момент слишком глючит, чтобы его можно было использовать по-настоящему.
Malhal
0

Ваш лучший выбор - это предложение Appcelerators Titanium. Они уже построили мост javascript Obj-C, используя движок JavascriptCore V8 , используемый webkit. Это также открытый исходный код, поэтому вы сможете скачать его и поработать с Obj-C, как вам нравится.

хова
источник
@hova, моста Obj-C V8 нет. V8 используется только для Android.
Росс
0

Взгляните на проект KirinJS: Kirin JS, который позволяет использовать Javascript для логики приложения и собственного пользовательского интерфейса, соответствующего платформе, на которой он работает.

Рочал
источник
кто-нибудь все еще вносит свой вклад в кирин? я не вижу никакой активности за последние несколько месяцев ...
Сэм
0

Я создал такую ​​библиотеку, как WebViewJavascriptBridge, но она больше похожа на JQuery, проще в установке и использовании. Не полагается на jQuery (хотя, к его чести, если бы я знал, что WebViewJavascriptBridge существует до того, как писать это, я, возможно, просто немного сдержался, прежде чем погрузиться в него). Дайте мне знать, что вы думаете! jockeyjs

Тим Коултер
источник
0

Если вы используете WKWebView на iOS 8, обратите внимание на XWebView, который может автоматически открывать собственный интерфейс для javascript.

мягкий свет
источник