Javascript console.log () в iOS UIWebView

84

При написании приложения для iPhone / iPad с UIWebView консоль не отображается. этот отличный ответ показывает, как отлавливать ошибки, но я также хотел бы использовать console.log ().

TinkerTank
источник
1
Сначала напишите его в браузере, включите Инструменты разработчика, затем посмотрите на вывод консоли.
beatgammit

Ответы:

185

Посоветовавшись сегодня с уважаемым коллегой, он предупредил меня о Safari Developer Toolkit и о том, как его можно подключить к UIWebViews в симуляторе iOS для вывода на консоль (и отладки!).

Шаги:

  1. Откройте настройки Safari -> вкладка «Дополнительно» -> установите флажок «Показать меню разработки в строке меню»
  2. Запустить приложение с UIWebView в iOS Simulator
  3. Safari -> Разработка -> Симулятор i (Pad / Pod) -> [the name of your UIWebView file]

Теперь вы можете перетащить сложный (в моем случае, flot ) Javascript и другие вещи в UIWebViews и отлаживать по желанию.

EDIT: как указал @Joshua J McKinnon, эта стратегия также работает при отладке UIWebViews на устройстве. Просто включите Web Inspector в настройках вашего устройства: Settings-> Safari-> Advanced-> Web Inspector (приветствует @Jeremy Wiebe)

ОБНОВЛЕНИЕ: WKWebView тоже поддерживается

NSTJ
источник
13
Обратите внимание, эта стратегия также работает при отладке на реальных устройствах iOS.
Джошуа Дж. МакКиннон,
2
+100, если бы мог. Это замечательно, это работает и для приложений с разрывом телефона!
Энди Новосин
2
Пробую с iPad, когда я захожу в меню разработки в Safari, нет устройств, которые можно было бы выбрать. Когда я развертываюсь на симуляторе, он работает как шарм.
Floydian
10
@Floydian, вы должны включить Web Inspector на устройстве. Настройки-> Safari-> Дополнительно-> Веб-инспектор.
Джереми Вибе,
2
мое приложение не отображается в моем меню разработки. Я включил веб-инспектор. Safari отображается, но мое приложение (текущее; отображается 2 UIWebviews) не обнаружено .. есть идеи?
narco
82

У меня есть решение для входа в консоль отладки приложений с помощью javascript. Это немного грубо, но работает.

Сначала мы определяем функцию console.log () в javascript, которая открывает и немедленно удаляет iframe с URL-адресом ios-log:.

// Debug
console = new Object();
console.log = function(log) {
  var iframe = document.createElement("IFRAME");
  iframe.setAttribute("src", "ios-log:#iOS#" + log);
  document.documentElement.appendChild(iframe);
  iframe.parentNode.removeChild(iframe);
  iframe = null;    
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;

Теперь нам нужно поймать этот URL-адрес в UIWebViewDelegate в приложении iOS с помощью функции shouldStartLoadWithRequest.

- (BOOL)webView:(UIWebView *)webView2 
shouldStartLoadWithRequest:(NSURLRequest *)request 
 navigationType:(UIWebViewNavigationType)navigationType {

    NSString *requestString = [[[request URL] absoluteString] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
    //NSLog(requestString);

    if ([requestString hasPrefix:@"ios-log:"]) {
        NSString* logString = [[requestString componentsSeparatedByString:@":#iOS#"] objectAtIndex:1];
                               NSLog(@"UIWebView console: %@", logString);
        return NO;
    }

    return YES;
}
TinkerTank
источник
1
см. простую идею NSTJ ниже.
Ashwin S
в Swift 4 может быть? : D
Константинос Нациос
35

Вот решение Swift: (Это немного похоже на взлом, чтобы получить контекст)

  1. Вы создаете UIWebView.

  2. Получите внутренний контекст и переопределите функцию javascript console.log () .

    self.webView = UIWebView()
    self.webView.delegate = self
    
    let context = self.webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") as! JSContext
    
    let logFunction : @convention(block) (String) -> Void =
    {
        (msg: String) in
    
        NSLog("Console: %@", msg)
    }
    context.objectForKeyedSubscript("console").setObject(unsafeBitCast(logFunction, AnyObject.self), 
                                                         forKeyedSubscript: "log")
    
Лесли Годвин
источник
3
+100! сэкономил мне ТОННУ времени, отличный хак, требует 0 изменений в коде JS. Благодаря!! Только мои 2 цента для будущих читателей: не забудьте связать JavaScriptCoreфреймворк с вашим проектом и importего в своем быстром файле webview.
mindbomb
У меня работает со Swift 4 ... вам нужно привести "журнал" к NSString..context.objectForKeyedSubscript ("console"). SetObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" as NSString)
Serge
28

Начиная с iOS7, вы можете использовать собственный мост Javascript. Что-то простое, как следующее

 #import <JavaScriptCore/JavaScriptCore.h>

JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
    };
Джи Фанг
источник
Ради интереса, где лучше всего разместить этот код?
Лесли Годвин
ОК, разобрался. Сразу после создания UIWebviewвы можете настроить любые JSContextвещи.
Лесли Годвин
4
Все JSContextеще работает в iOS 8+ с WKWebView?
Николай Самтеладзе
Вставил - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypeи работает отлично!
Artur Bartczak
@NikolaiSamteladze: Я пробовал с WKWebViewiOS 11.4.1 и он не может найти documentViewи вылетает. Я видел этот ответ, и мне кажется, что это невозможно.
тестирование
10

NativeBridge очень полезен для связи от UIWebView с Objective-C. Вы можете использовать его для передачи журналов консоли и вызова функций Objective-C.

https://github.com/ochameau/NativeBridge

console = new Object();
console.log = function(log) {
    NativeBridge.call("logToConsole", [log]);
};
console.debug = console.log;
console.info = console.log;
console.warn = console.log;
console.error = console.log;

window.onerror = function(error, url, line) {
    console.log('ERROR: '+error+' URL:'+url+' L:'+line);
};

Преимущество этого метода в том, что такие вещи, как новые строки в сообщениях журнала, сохраняются.

касательная405
источник
+1. Примечание для пользователей Apache Cordova - Cordova уже обрабатывает console.log, но window.onerrorфункция в этом ответе очень полезна!
mpontillo 02
Для разработчиков Appcelerator / Titanium: это тоже работает для отладки вашего Ti.UI.WebView
Byters,
0

Пробовал исправить Лесли Годвин, но получал эту ошибку:

'objectForKeyedSubscript' is unavailable: use subscripting

Для Swift 2.2 у меня сработало следующее:

Вам нужно будет импортировать JavaScriptCore для компиляции этого кода:

import JavaScriptCore

if let context = webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
    context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
    let consoleLog: @convention(block) String -> Void = { message in
        print("javascript_log: " + message)
    }
    context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
}

Затем в вашем коде javascript вызов console.log ("_ your_log_") будет печататься в консоли Xcode.

А еще лучше добавить этот код как расширение к UIWebView:

import JavaScriptCore

extension UIWebView {
    public func hijackConsoleLog() {
        if let context = valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") {
            context.evaluateScript("var console = { log: function(message) { _consoleLog(message) } }")
            let consoleLog: @convention(block) String -> Void = { message in
                print("javascript_log: " + message)
            }
            context.setObject(unsafeBitCast(consoleLog, AnyObject.self), forKeyedSubscript: "_consoleLog")
        }
    }
}

Затем вызовите этот метод на этапе инициализации UIWebView:

let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()
параг
источник
0

Swift 5

func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
      webView.evaluateJavaScript("your javascript string") { (value, error) in
          if let errorMessage = (error! as NSError).userInfo["WKJavaScriptExceptionMessage"] as? String {
                print(errorMessage)
          }
      }
 }
Барат
источник