簡體   English   中英

我的 iPhone Objective-C 代碼如何在 UIWebView 中收到 Javascript 錯誤的通知?

[英]How can my iPhone Objective-C code get notified of Javascript errors in a UIWebView?

我需要讓我的 iPhone Objective-C 代碼在 UIWebView 中捕獲 Javascript 錯誤。 這包括未捕獲的異常、加載文件時的語法錯誤、未定義的變量引用等。

這是一個開發環境,所以它不需要是 SDK-kosher。 事實上,它只需要在模擬器上工作。

我已經發現使用了一些隱藏的 WebKit 技巧,例如將 Obj-C 對象暴露給 JS 並攔截警報彈出窗口,但我仍然無法理解這一點。

[注意:發布這篇文章后,我確實找到了一種使用調試委托的方法。 有沒有一種方法可以降低開銷,使用錯誤控制台/網絡檢查器?]

我現在找到了一種在 WebView 中使用腳本調試器掛鈎的方法(注意,不是 UIWebView)。 我首先必須繼承 UIWebView 並添加一個這樣的方法:

- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject {
    // save these goodies
    windowScriptObject = newWindowScriptObject;
    privateWebView = webView;

    if (scriptDebuggingEnabled) {
        [webView setScriptDebugDelegate:[[YourScriptDebugDelegate alloc] init]];
    }
}

接下來,您應該創建一個包含以下方法的 YourScriptDebugDelegate 類:

// in YourScriptDebugDelegate

- (void)webView:(WebView *)webView       didParseSource:(NSString *)source
 baseLineNumber:(unsigned)lineNumber
        fromURL:(NSURL *)url
       sourceId:(int)sid
    forWebFrame:(WebFrame *)webFrame
{
    NSLog(@"NSDD: called didParseSource: sid=%d, url=%@", sid, url);
}

// some source failed to parse
- (void)webView:(WebView *)webView  failedToParseSource:(NSString *)source
 baseLineNumber:(unsigned)lineNumber
        fromURL:(NSURL *)url
      withError:(NSError *)error
    forWebFrame:(WebFrame *)webFrame
{
    NSLog(@"NSDD: called failedToParseSource: url=%@ line=%d error=%@\nsource=%@", url, lineNumber, error, source);
}

- (void)webView:(WebView *)webView   exceptionWasRaised:(WebScriptCallFrame *)frame
       sourceId:(int)sid
           line:(int)lineno
    forWebFrame:(WebFrame *)webFrame
{
    NSLog(@"NSDD: exception: sid=%d line=%d function=%@, caller=%@, exception=%@", 
          sid, lineno, [frame functionName], [frame caller], [frame exception]);
}

這可能會對運行時產生很大的影響,因為調試委托還可以提供用於進入和退出堆棧幀以及執行每一行代碼的方法。

有關 WebScriptDebugDelegate 的 Objective-C++ 定義,請參見http://www.koders.com/noncode/fid7DE7ECEB052C3531743728D41A233A951C79E0AE.aspx

那些其他方法:

// just entered a stack frame (i.e. called a function, or started global scope)
- (void)webView:(WebView *)webView    didEnterCallFrame:(WebScriptCallFrame *)frame
      sourceId:(int)sid
          line:(int)lineno
   forWebFrame:(WebFrame *)webFrame;

// about to execute some code
- (void)webView:(WebView *)webView willExecuteStatement:(WebScriptCallFrame *)frame
      sourceId:(int)sid
          line:(int)lineno
   forWebFrame:(WebFrame *)webFrame;

// about to leave a stack frame (i.e. return from a function)
- (void)webView:(WebView *)webView   willLeaveCallFrame:(WebScriptCallFrame *)frame
      sourceId:(int)sid
          line:(int)lineno
   forWebFrame:(WebFrame *)webFrame;

請注意,這一切都隱藏在一個私有框架中,因此不要嘗試將其放入您提交給 App Store 的代碼中,並准備好接受一些黑客來使其工作。

我創建了一個不錯的小插件類別,您可以將其添加到您的項目中...它基於 Robert Sanders 解決方案。 榮譽。

你可以在這里下載:

UIWebView+調試

這應該會讓你更容易調試 UIWebView :)

我使用了 Robert Sanders 提出的很棒的解決方案: 我的 iPhone Objective-C 代碼如何在 UIWebView 中收到 Javascript 錯誤的通知?

webkit 的那個鈎子在iPhone 上也能正常工作。 我分配了派生的 MyUIWebView,而不是標准的 UIWebView。 我還需要在 MyWebScriptObjectDelegate.h 中定義隱藏類:

@class WebView;
@class WebFrame;
@class WebScriptCallFrame;

在 ios sdk 4.1 中的功能:

- (void)webView:(id)webView windowScriptObjectAvailable:(id)newWindowScriptObject 

棄用,而不是我使用的功能:

- (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame

另外,我收到了一些煩人的警告,比如“NSObject 可能不會響應 -windowScriptObject”,因為類接口是隱藏的。 我忽略它們,效果很好。

如果您擁有 Safari v 6+(我不確定您需要什么 iOS 版本),那么在開發過程中的一種有效方法是使用 Safari 開發工具並通過它連接到 UIWebView。

  1. 在 Safari 中:啟用開發菜單(首選項 > 高級 > 在菜單欄中顯示開發菜單)
  2. 通過電纜將手機插入計算機。
  3. 列表項
  4. 加載應用程序(通過 xcode 或直接啟動它)並轉到要調試的屏幕。
  5. 返回 Safari,打開“開發”菜單,在該菜單中查找您設備的名稱(我的名為 iPhone 5),應該就在“用戶代理”下。
  6. 選擇它,您應該會看到當前在您的應用程序中可見的 Web 視圖的下拉列表。
  7. 如果屏幕上有多個 webview,您可以嘗試通過在開發菜單中滾動應用程序名稱來區分它們。 對應的 UIWebView 會變成藍色。
  8. 選擇應用程序的名稱,開發窗口打開,您可以檢查控制台。 你甚至可以通過它發出 JS 命令。

直截了當的方式:將此代碼放在使用 UIWebView 的控制器/視圖之上

#ifdef DEBUG
@interface DebugWebDelegate : NSObject
@end
@implementation DebugWebDelegate
@class WebView;
@class WebScriptCallFrame;
@class WebFrame;
- (void)webView:(WebView *)webView   exceptionWasRaised:(WebScriptCallFrame *)frame
       sourceId:(int)sid
           line:(int)lineno
    forWebFrame:(WebFrame *)webFrame
{
    NSLog(@"NSDD: exception: sid=%d line=%d function=%@, caller=%@, exception=%@", 
          sid, lineno, [frame functionName], [frame caller], [frame exception]);
}
@end
@interface DebugWebView : UIWebView
id windowScriptObject;
id privateWebView;
@end
@implementation DebugWebView
- (void)webView:(id)sender didClearWindowObject:(id)windowObject forFrame:(WebFrame*)frame
{
    [sender setScriptDebugDelegate:[[DebugWebDelegate alloc] init]];
}
@end
#endif

然后像這樣實例化它:

#ifdef DEBUG
        myWebview = [[DebugWebView alloc] initWithFrame:frame];
#else
        myWebview = [[UIWebView alloc] initWithFrame:frame];
#endif

使用 #ifdef DEBUG 可確保它不會出現在發布版本中,但我也建議您在不使用它時將其注釋掉,因為它會影響性能。 歸功於 Robert Sanders 和 Prcela 的原始代碼

此外,如果使用 ARC,您可能需要添加“-fno-objc-arc”以防止一些構建錯誤。

我創建了一個 SDK kosher 錯誤報告器,其中包括:

  1. 錯誤信息
  2. 發生錯誤的文件名
  3. 發生錯誤的行號
  4. JavaScript 調用堆棧,包括傳遞的參數

它是sourceForge 項目提供的 QuickConnectiPhone 框架的一部分

甚至還有一個示例應用程序展示了如何向 Xcode 終端發送錯誤消息。

你需要做的就是用 try catch 包圍你的 JavaScript 代碼,包括函數定義等。 它應該是這樣的。

try{
//put your code here
}
catch(err){
    logError(err);
}

對於編譯錯誤,它不能很好地工作,但可以與所有其他錯誤一起工作。 甚至匿名函數。

開發博客在這里,包括指向 wiki、sourceForge、google group 和 twitter 的鏈接。 也許這會幫助你。

我在固件 1.x 但不是 2.x 中做到了這一點。 這是我在 1.x 中使用的代碼,它至少應該對您有所幫助。

// Dismiss Javascript alerts and telephone confirms
/*- (void)alertSheet:(UIAlertSheet*)sheet buttonClicked:(int)button
{
    if (button == 1)
    {
        [sheet setContext: nil];
    }

    [sheet dismiss];
}*/

// Javascript errors and logs
- (void) webView: (WebView*)webView addMessageToConsole: (NSDictionary*)dictionary
{
    NSLog(@"Javascript log: %@", dictionary);
}

// Javascript alerts
- (void) webView: (WebView*)webView runJavaScriptAlertPanelWithMessage: (NSString*) message initiatedByFrame: (WebFrame*) frame
{
    NSLog(@"Javascript Alert: %@", message);

    UIAlertSheet *alertSheet = [[UIAlertSheet alloc] init];
    [alertSheet setTitle: @"Javascript Alert"];
    [alertSheet addButtonWithTitle: @"OK"];
    [alertSheet setBodyText:message];
    [alertSheet setDelegate: self];
    [alertSheet setContext: self];
    [alertSheet popupAlertAnimated:YES];
}

請參閱 iOS7 中的異常處理: http : //www.bignerdranch.com/blog/javascriptcore-example/

[context setExceptionHandler:^(JSContext *context, JSValue *value) {
    NSLog(@"%@", value);
}];

首先設置WebViewJavascriptBridge ,然后覆蓋 console.error 函數。

在 JavaScript 中

    window.originConsoleError = console.error;
    console.error = (msg) => {
        window.originConsoleError(msg);
        bridge.callHandler("sendConsoleLogToNative", {
            action:action,
            message:message
        }, null)
    };

在 Objective-C 中

[self.bridge registerHandler:@"sendConsoleLogToNative" handler:^(id data, WVJBResponseCallback responseCallback) {
    NSString *action = data[@"action"];
    NSString *msg = data[@"message"];
    if (isStringValid(action)){
        if ([@"console.error" isEqualToString:action]){
            NSLog(@"JS error :%@",msg);
        }
    }
}];

對於某些情況,一個更簡單的解決方案可能是將Firebug Lite添加到網頁中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM