Javascript console.log () ใน iOS UIWebView


84

เมื่อเขียนแอพ iPhone / iPad ด้วย UIWebView คอนโซลจะมองไม่เห็น คำตอบที่ยอดเยี่ยมนี้แสดงวิธีดักจับข้อผิดพลาด แต่ฉันต้องการใช้ console.log () ด้วย


1
เขียนลงในเบราว์เซอร์ก่อนเปิดเครื่องมือสำหรับนักพัฒนาจากนั้นดูที่เอาต์พุตคอนโซล
beatgammit

คำตอบ:


186

หลังจากปรึกษากับเพื่อนร่วมงานที่นับถือแล้ววันนี้เขาแจ้งเตือนฉันถึง Safari Developer Toolkit และวิธีนี้จะเชื่อมต่อกับ UIWebViews ใน iOS Simulator สำหรับเอาต์พุตคอนโซล (และการดีบัก!)

ขั้นตอน:

  1. เปิดการตั้งค่า Safari -> แท็บ "ขั้นสูง" -> เปิดใช้งานช่องทำเครื่องหมาย "แสดงเมนูการพัฒนาในแถบเมนู"
  2. เริ่มแอพด้วย UIWebView ใน iOS Simulator
  3. Safari -> พัฒนา -> i (Pad / Pod) Simulator -> [the name of your UIWebView file]

ตอนนี้คุณสามารถวางJavascript ที่ซับซ้อน (ในกรณีของฉันคือflot ) และสิ่งอื่น ๆ ลงใน UIWebViews และดีบักได้ตามต้องการ

แก้ไข: ตามที่ระบุไว้โดย @Joshua J McKinnon กลยุทธ์นี้ยังใช้งานได้เมื่อดีบัก UIWebViews บนอุปกรณ์ เพียงเปิดใช้งาน Web Inspector ในการตั้งค่าอุปกรณ์ของคุณ: การตั้งค่า -> Safari-> ขั้นสูง -> Web Inspector (ไชโย @Jeremy Wiebe)

อัปเดต: รองรับ WKWebView ด้วย


13
หมายเหตุกลยุทธ์นี้ยังใช้ได้เมื่อทำการดีบักบนอุปกรณ์ iOS จริง
Joshua J. McKinnon

2
+100 ถ้าฉันทำได้ มันยอดเยี่ยมมากมันใช้ได้กับแอพ phone gap ด้วย!
Andy Novocin

2
ลองกับ iPad เมื่อฉันไปที่เมนูพัฒนาบน Safari ไม่มีอุปกรณ์ให้เลือก เมื่อฉันปรับใช้โปรแกรมจำลองมันจะทำงานได้อย่างมีเสน่ห์
Floydian

10
@Floydian คุณต้องเปิดใช้งาน Web Inspector บนอุปกรณ์ การตั้งค่า -> Safari-> ขั้นสูง -> ตัวตรวจสอบเว็บ
Jeremy Wiebe

2
แอพของฉันไม่ปรากฏในเมนูการพัฒนาของฉัน ฉันเปิดใช้ตัวตรวจสอบเว็บแล้ว Safari ปรากฏขึ้น แต่ตรวจไม่พบแอปของฉัน (ซึ่งเป็นปัจจุบัน; y แสดง 2 UIWebviews) .. มีความคิดใดบ้าง
narco

82

ฉันมีวิธีการบันทึกโดยใช้จาวาสคริปต์ไปยังคอนโซลการดีบักของแอป มันค่อนข้างหยาบ แต่ใช้งานได้

ขั้นแรกเรากำหนดฟังก์ชัน console.log () ใน javascript ซึ่งจะเปิดขึ้นและลบ iframe ทันทีด้วย 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;

ตอนนี้เราต้องจับ 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;
}

1
ดูแนวคิดง่ายๆของ NSTJ ด้านล่าง
Ashwin S

ใน swift 4 อาจจะ? : D
Konstantinos Natsios

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! ช่วยฉันประหยัดเวลาได้หลายครั้งแฮ็คที่ยอดเยี่ยมต้องการการเปลี่ยนแปลงรหัส JS 0 ครั้ง ขอบคุณ !! เพียง 2 เซ็นต์ของฉันสำหรับผู้อ่านในอนาคต: อย่าลืมเชื่อมโยงJavaScriptCoreเฟรมเวิร์กกับโปรเจ็กต์ของคุณและimportในไฟล์ swift webview ของคุณ
mindbomb

ใช้งานได้กับ Swift 4 ... คุณต้องส่ง "log" ไปยัง NSString..context.objectForKeyedSubscript ("console") setObject (unsafeBitCast (logFunction, to: AnyObject.self), forKeyedSubscript: "log" เป็น NSString)
Serge

28

เริ่มจาก iOS7 คุณสามารถใช้ Javascript bridge สิ่งง่ายๆดังต่อไปนี้

 #import <JavaScriptCore/JavaScriptCore.h>

JSContext *ctx = [webview valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
ctx[@"console"][@"log"] = ^(JSValue * msg) {
NSLog(@"JavaScript %@ log message: %@", [JSContext currentContext], msg);
    };

หากไม่สนใจสถานที่ที่เหมาะสำหรับใส่รหัสนี้อยู่ที่ไหน
Leslie Godwin

ตกลงคิดออกแล้ว หลังจากที่คุณสร้างUIWebviewคุณสามารถตั้งค่าJSContextสิ่งต่างๆได้
Leslie Godwin

4
ไม่JSContextยังคงทำงานใน iOS 8 ด้วยWKWebView?
Nikolai Samteladze

ฉันใส่เข้าไป- (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);
};

ข้อดีของเทคนิคนี้คือการขึ้นบรรทัดใหม่ในข้อความบันทึกจะถูกเก็บรักษาไว้


+1. หมายเหตุสำหรับผู้ใช้Apache Cordova - Cordova จัดการอยู่แล้วconsole.logแต่window.onerrorฟังก์ชันในคำตอบนี้มีประโยชน์มาก!
mpontillo

สำหรับนักพัฒนา Appcelerator / Titanium: ใช้งานได้เช่นกันเพื่อแก้จุดบกพร่อง Ti.UI ของคุณ WebView
Byters

0

พยายามแก้ไขของ Leslie Godwin แต่ได้รับข้อผิดพลาดนี้:

'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")
}

จากนั้นในโค้ดจาวาสคริปต์ของคุณการเรียก 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

สวิฟต์ 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)
          }
      }
 }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.