JavaScript console.log () w UIWebView iOS

84

Podczas pisania aplikacji na iPhone'a / iPada za pomocą UIWebView konsola nie jest widoczna. ta doskonała odpowiedź pokazuje, jak wychwytywać błędy, ale chciałbym również użyć console.log ().

TinkerTank
źródło
1
Najpierw napisz go w przeglądarce, włącz Narzędzia programistyczne, a następnie spójrz na dane wyjściowe konsoli.
beatgammit

Odpowiedzi:

183

Po dzisiejszej konsultacji z cenionym kolegą, poinformował mnie o Safari Developer Toolkit i o tym, jak można to połączyć z UIWebViews w symulatorze iOS w celu uzyskania danych wyjściowych z konsoli (i debugowania!).

Kroki:

  1. Otwórz Preferencje Safari -> zakładka „Zaawansowane” -> zaznacz pole wyboru „Pokaż menu rozwijania na pasku menu”
  2. Uruchom aplikację z UIWebView w symulatorze iOS
  3. Safari -> Develop -> i (Pad / Pod) Simulator -> [the name of your UIWebView file]

Możesz teraz upuszczać złożony (w moim przypadku flot ) JavaScript i inne rzeczy do UIWebViews i debugować do woli.

EDYCJA: Jak wskazał @Joshua J McKinnon, ta strategia działa również podczas debugowania UIWebViews na urządzeniu. Po prostu włącz Web Inspector w ustawieniach swojego urządzenia: Settings-> Safari-> Advanced-> Web Inspector (cheers @Jeremy Wiebe)

UPDATE: WKWebView jest również obsługiwany

NSTJ
źródło
13
Uwaga, ta strategia działa również podczas debugowania na prawdziwych urządzeniach z systemem iOS.
Joshua J. McKinnon
2
+100, gdybym mógł. To wspaniałe, działa też w przypadku aplikacji obsługujących przerwy w telefonach!
Andy Novocin
2
Próbując z iPadem, kiedy przechodzę do menu rozwijania w Safari, nie ma żadnych urządzeń do wyboru. Kiedy wdrażam na symulatorze, działa to jak urok.
Floydian
10
@Floydian musisz włączyć Web Inspector na urządzeniu. Ustawienia-> Safari-> Zaawansowane-> Inspektor sieci.
Jeremy Wiebe
2
moja aplikacja nie pojawia się w moim menu programowania. Włączyłem inspektora sieci. Pojawia się Safari, ale moja aplikacja (która jest aktualna; y wyświetla 2 UIWebviews) nie jest wykrywana ... jakieś pomysły?
narco
82

Mam rozwiązanie do logowania za pomocą javascript do konsoli debugowania aplikacji. To trochę prymitywne, ale działa.

Najpierw definiujemy funkcję console.log () w javascript, która otwiera i natychmiast usuwa ramkę iframe z ios-log: url.

// 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;

Teraz musimy przechwycić ten adres URL w UIWebViewDelegate w aplikacji iOS za pomocą funkcji 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
źródło
1
zobacz prosty pomysł NSTJ poniżej.
Ashwin S,
w Swift 4 może? : D
Konstantinos Natsios
35

Oto rozwiązanie Swift: (trochę hackowania, aby uzyskać kontekst)

  1. Tworzysz UIWebView.

  2. Pobierz kontekst wewnętrzny i nadpisz funkcję 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")
    
Leslie Godwin
źródło
3
+100! zaoszczędził mi TONY czasu, świetny hack, wymaga 0 zmian w kodzie JS. Dzięki!! Tylko moje 2 centy dla przyszłych czytelników: nie zapomnij połączyć JavaScriptCoreframeworka ze swoim projektem i importgo w swoim pliku swift z przeglądarką internetową.
bombardowanie
Działa dla mnie ze Swift 4 ... musisz przesłać "log" do NSString..context.objectForKeyedSubscript ("console"). SetObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" jako NSString)
Serge
28

Począwszy od iOS7, możesz używać natywnego mostka JavaScript. Coś tak prostego, jak śledzenie

 #import <JavaScriptCore/JavaScriptCore.h>

JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
    };
Ji Fang
źródło
Poza zainteresowaniem, gdzie jest idealne miejsce na umieszczenie tego kodu?
Leslie Godwin
OK, rozgryzłem to. Zaraz po utworzeniu UIWebviewmożesz skonfigurować dowolne JSContextrzeczy.
Leslie Godwin
4
Czy JSContextnadal działa w iOS 8+ z WKWebView?
Nikolai Samteladze
Włożyłem go - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationTypei działa idealnie!
Artur Bartczak
@NikolaiSamteladze: Próbowałem z WKWebViewiOS 11.4.1 i iOS, ale nie może znaleźć documentViewi ulega awarii. Widziałem tę odpowiedź i wydaje się, że nie jest to możliwe w ten sposób.
testowanie
10

NativeBridge jest bardzo pomocny w komunikacji z UIWebView do Objective-C. Możesz go użyć do przekazywania dzienników konsoli i wywoływania funkcji 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);
};

Zaletą tej techniki jest to, że zachowywane są takie elementy, jak znaki nowego wiersza w komunikatach dziennika.

styczna405
źródło
+1. Uwaga dla użytkowników Apache Cordova - Cordova już obsługuje console.log, ale window.onerrorfunkcja w tej odpowiedzi jest bardzo przydatna!
mpontillo
Dla programistów Appcelerator / Titanium: to również działa do debugowania Ti.UI.WebView
Byters
0

Wypróbowałem rozwiązanie Leslie Godwin, ale otrzymywałem ten błąd:

'objectForKeyedSubscript' is unavailable: use subscripting

W przypadku Swift 2.2, oto co zadziałało:

Aby skompilować ten kod, musisz zaimportować 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")
}

Następnie w kodzie javascript wywołanie console.log ("_ twój_log_") zostanie wydrukowane w konsoli Xcode.

Jeszcze lepiej, dodaj ten kod jako rozszerzenie do 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")
        }
    }
}

A następnie wywołaj tę metodę podczas kroku inicjalizacji UIWebView:

let webView = UIWebView(frame: CGRectZero)
webView.hijackConsoleLog()
parag
źródło
0

Szybki 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)
          }
      }
 }
Barath
źródło