web-dev-qa-db-ja.com

iPhoneのObjective-CコードでUIWebViewのJavaScriptエラーを通知するにはどうすればよいですか?

IPhoneのObjective-CコードでUIWebViewのJavaScriptエラーをキャッチする必要があります。これには、キャッチされない例外、ファイルのロード時の構文エラー、未定義の変数参照などが含まれます。

これは開発環境用なので、SDK-kosherである必要はありません。実際、それは本当にシミュレータ上で動作する必要があるだけです。

私はすでに、隠されたWebKitトリックのいくつかを使用していることを発見しました。 Obj-CオブジェクトをJSに公開し、アラートポップアップをインターセプトしますが、これはまだ私を避けています。

[注:これを投稿した後、デバッグデリゲートを使用して1つの方法を見つけました。エラーコンソール/ウェブインスペクターを使用して、オーバーヘッドを減らす方法はありますか?]

46
Robert Sanders

WebViewでスクリプトデバッガフックを使用する方法を1つ見つけました(注: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に送信するコードにこれを入れないでください。ハッカーが動作するように準備してください。

28
Robert Sanders

プロジェクトに追加できる素敵な小さなドロップインカテゴリを作成しました...これはRobert Sandersソリューションに基づいています。称賛。

ここからダウンロードできます。

IWebView + Debug

これにより、UIWebViewのデバッグが非常に簡単になります。

13
Pablo

Robert Sandersから提案された優れたソリューションを使用しました: どのように私のiPhone Objective-CコードはUIWebViewのJavascriptエラーを通知されますか?

WebkitのフックはiPhoneでも正常に機能します。標準のUIWebViewの代わりに、派生したMyUIWebViewを割り当てました。 MyWebScriptObjectDelegate.h内に隠しクラスを定義する必要もありました。

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

IOS SDK 4.1内の関数:

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

isdeprecatedそして、代わりに私は関数を使用しました:

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

また、クラスインターフェイスが非表示になっているため、「NSObjectが-windowScriptObjectに応答しない可能性があります」などの迷惑な警告が表示されます。私はそれらを無視し、それはうまくいきます。

11
Prcela

Safari v 6以降(必要なiOSのバージョンは不明)がある場合、開発中に機能する方法の1つは、Safari開発ツールを使用し、それを介してUIWebViewにフックすることです。

  1. Safariの場合:[開発]メニューを有効にします([設定]> [詳細]> [メニューバーに[開発]メニューを表示])。
  2. ケーブルを介して電話をコンピュータに接続します。
  3. リストアイテム
  4. (xcodeを介して、または単に起動して)アプリをロードし、デバッグする画面に移動します。
  5. Safariに戻り、[開発]メニューを開き、そのメニューでデバイスの名前を探します(私の名前はiPhone 5と呼ばれます)。これは、ユーザーエージェントの下にあります。
  6. それを選択すると、アプリに現在表示されているWebビューのドロップダウンが表示されます。
  7. 画面に複数のWebビューがある場合は、開発メニューでアプリの名前にカーソルを合わせると、それらを区別することができます。対応するUIWebViewが青色に変わります。
  8. アプリの名前を選択すると、開発ウィンドウが開き、コンソールを検査できます。 JSコマンドを発行することもできます。
9
bobc

まっすぐ進む方法: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」を追加する必要がある場合があります。

8
psy

以下を含むSDKコーシャーエラーレポーターを作成しました。

  1. エラーメッセージ
  2. エラーが発生したファイルの名前
  3. エラーが発生した行番号
  4. 渡されたパラメーターを含むJavaScriptコールスタック

sourceForgeプロジェクト から入手できるQuickConnectiPhoneフレームワークの一部です。

Xcodeターミナルにエラーメッセージを送信する方法を示すサンプルアプリケーションさえあります。

関数定義などを含むJavaScriptコードをtry catchで囲むだけです。このようになります。

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

コンパイルエラーではうまく機能しませんが、他のすべてでは機能します。匿名関数も。

開発 ブログはこちら はこちらで、wiki、sourceForge、googleグループ、Twitterへのリンクが含まれています。多分これはあなたを助けるでしょう。

4
Lee Barney

最初のセットアップ 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);
        }
    }
}];
0
Patrick

ファームウェア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];
}
0
kdbdallas

IOS7での例外処理を参照してください: http://www.bignerdranch.com/blog/javascriptcore-example/

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