Как вызвать Objective-C из JavaScript в UIWebView в iOS 7?

Обратите внимание, что это не дубликат из-за части "в iOS 7" моего вопроса и того факта, что предыдущее поведение (и, следовательно, ответы на предыдущие вопросы) изменилось.

Я хочу знать, как код JavaScript, вызываемый через UIWebView, может вызывать код Objective C в iOS 7. Это то, что я ранее широко использовал в проектах в прошлом, однако поведение в iOS 7 изменилось, в результате чего делегат webView didFailLoadWithError вызывается, когда раньше этого не делалось.

Я использовал для вызова функциональности Objective C с помощью пользовательского трюка URL:

function invokeObjectiveC(action, target, data) {
   var iframe = document.createElement("IFRAME");
   iframe.setAttribute("src", "MY-URLScheme:" + action + ":" + target + ":" + data);
   document.documentElement.appendChild(iframe);
   iframe.parentNode.removeChild(iframe);
   iframe = null;
}

Конкретная строка, которая заставляет didFailLoadWithError: быть вызванным, выглядит так:

 document.documentElement.appendChild(iframe);

Другой распространенный альтернативный подход, распространяемый по Интернету, чтобы сделать то же самое, заключается в следующем:

window.location = "MY-URLScheme:" + action + ":" + target + ":" + data;

И это работает на iOS 7 в том смысле, что didFailLoadWithError не вызывается, однако у него есть (известная) проблема, заключающаяся в том, что при его быстром повторном вызове дважды первый вызов теряется.

Мне было интересно, как Cordova делает это, поэтому я скачал исходный код и посмотрел, и я думаю, что они делают это похоже на меня (3-я строка отличается):

var iframe = document.createElement("IFRAME");
iframe.setAttribute("src", "MY-URLScheme:" + action + ":" + target + ":" + data);
document.body.appendChild(iframe);
iframe.parentNode.removeChild(iframe);
iframe = null;

Однако это все еще приводит к тому, что didFailLoadWithError вызывается. Что заставило меня озадачиться тем, почему Cordova не сделала ничего, чтобы исправить это (при условии, что я правильно смотрю на их код).

Тот факт, что didFailLoadWithError вызывается, не кажется вредным, поскольку страницы все еще загружаются, а Objective-C по-прежнему вызывается. Однако очень и очень неудовлетворительно иметь код, который приводит к вызову метода ошибки, поэтому я хочу найти альтернативный способ.

Поэтому возникает вопрос: есть ли способ вызывать Objective-C из JS, загруженного в UIWebView, надежно и без вызова didFailLoadWithError на iOS 7?

Кто-нибудь может подтвердить, что Cordova использует вышеуказанный способ или что-то еще?

Решения, использующие JavaScriptCore в UIWebView (в отличие от внешнего по отношению к UIWebView), выглядят недокументированными / не поддерживаются в iOS/ хрупки? Например:

Доступ к движку JavaScriptCore UIWebView

Триггер целевой метод C из JavaScript с использованием JavaScriptCore в iOS 7 в ViewControllers

1 ответ

Решение

Обходной путь iframe немного глючит и не очень надежен. JavaScriptCore - это хороший подход, но фреймворк закрыт для iOS 6 и iOS 5. Существует более быстрый и быстрый способ вызова Objetive-C из UIWebView. Вы можете реализовать метод категории для функции оповещения.

@implementation UIWebView (JavaScriptAlert)
- (void)webView:(UIWebView *)sender runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WebFrame *)frame 
{
    if ([message hasPrefix:CUSTOM_SCHEME_STRING]) {
        //process your message
    }
    else {
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"Alert" message:message delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles: nil];
        [alert show];
        [alert release];
    }
} 
@end

Когда вы вызываете метод window.alert() из javascript, вызывается вышеупомянутая функция Objetive-C. Используйте свой префикс для ваших сообщений и откройте UIAlertView, когда вы хотите стандартное оповещение.

Этот метод работает на iOS 5, 6 и 7 без проблем. Это очень надежно. Я тестировал вызов оповещения 10000 раз подряд, и никакие сообщения не терялись. В случае решения iframe это не так, многие сообщения теряются, если вы создаете iframe слишком быстро, поэтому вы внедрили пакетную очередь в свой код JS и синхронизировали их с интервалом ~1 мс.

Другие вопросы по тегам