←前 |  ↑↑Blog |  ↑Category |  ↓Comment |  ↓Trackback |  次→

Dashboard Widgetのデバッグ方法


まだ、あまり実践できていませんが紹介します。
誤訳や内容のミスについてはご指摘くださると助かります。


Dashboard Widgetのデバッグ方法

この文章はAppleが提供している、Debugging Dashboard Widgetsという文章を基本にして適宜、意訳や追記や削除(Plug-in部分)をしています。

もともとalert文とdiv要素への書き込みのみでWidgetを開発した私にとって、この文章はかなりの衝撃でした。もっと早く知っておけば2,3割は効率よく開発できたのではないかなぁ、と思っています。元の文章が短めだったので、勉強がてらにこの文章を作成してみました。


はじめに

Dashboard Widgetは作るのは大して難しくないです。しかしながら、一旦足を踏み外すとまっとうに動作させるのは結構大変になります。この文章では、Widget作成の際のデバッグの方法について説明します。

実際にWidget作成する基本的な方法については対象外なので注意が必要です。デバッグをする方法のみについての説明となります。

Widget開発の際のデバッグの環境として、おすすめのものは

  • Safari
  • Dashboard

の2つです。それぞれ一長一短ありますが、後述するようにDashboardを開発モードで試す、ってのが一番開発効率はよさそうです。


Safari上でのデバッグ

Widget開発は、基本的にはWebページを作ることとなります。そして、Dashboard WidgetはSafariのようにWeb Kitを使って描画されるので、Safari上でデバッグをするというのはそんなに変な方法ではありません。

Widget内にあるhtmlファイルをSafariで開けば、Dashboardにおける変数とかはないけど、とりあえず表示することはできちゃいます。対応不可能なのはwidgetオブジェクトを利用したコード部分です。それ以外のHTML、CSS、JavaScriptなところは問題なく確認できます。

作成したWidgetのテストをSafariでするときに重要なのがJavaScriptのエラーの確認です。DashboardはJavaScriptエラーを「コンソール」アプリに出力します(!)。しかし、エラーの原因をここから掘り下げるのは難しいようです。Safariにおいてう まく動作させるためには、widget特有の部分を独立させることが重要です。なので自然と window.widget チェックをするなどの素敵な習慣が身につくことになります。というか、チェックする癖をつけましょう。

Safariにおけるデバッグにおいて、もっとも重要なのはは、Debugメニューを利用可能にすることです。そのためには次のコマンド(おまじない)を打ちます。

$ defaults write com.apple.Safari IncludeDebugMenu 1

これにより、次回のSafariの実行時から、Debugメニューが有効になります(ヘルプの隣)。

可能な限りフィードバックを得たいなら、Log JavaScript Exceptionsを選択しましょう。Tigerの新機能として、JavaScriptコンソールというものもあります。これもDebugメニューから選択可能です。選択したら準備完了です。


Debug情報を表示

JavaScriptのデバッグにとってもっとも基本かつ普遍的なことはalert関数です。この関数は、ひとつの引数(たいてい文字列)を受け取って、ダイアログ上に表示するものです。ダイアログなので、処理をとめて表示するため、ブレークポイントとして の面も持ちます。

しかし、大量のalert文は時に不幸を招きます。例えば、setIntervalなどによって時間駆動でダイアログが開かれちゃうと、閉じる処理と開く作業でえらいことになる可能性があります。

  • 長所:SafariのDebug機能が使える
  • 短所:実際の動作でない。特にwidgetオブジェクトを利用した動作が検証できない。

Dashboard上でのデバッグ

実際に全ての動作を確認できる、という点でDashboardでのデバッグは非常に有効です。alert文も解釈され、コンソールへ出力されます(こちらの場合処理はとまりません)。

また、Dashboardは開発モードがあります。開発モードは、Dashboardのオンオフ、Dashboard上のWidgetと通常のアプリケーション(エディタやコンソールとか)との切り替え等の苦痛から開発者を解放します。開発モードはDashboardが表示されていない、通常スクリーン上にWidgetを常駐させることを可能にしてくれます。

開発モードを有効にするには、

  1. 次のおまじないを実行します。
  2. $ defaults write com.apple.dashboard devmode YES
  3. Dashboardを再起動させるために、一旦ログオフしてログインする。再起動でもOKです。
  4. 開発しているWidgetをDashboardに読み込ませます(=波立たせます)。ダブルクリックの起動でも大丈夫です。
  5. 開発しているWidgetをすこしの距離だけドラッグします(ドロップしないでください)。
  6. この状態でDashboard起動用のキー(デフォルトではF12)を押すと、Dashboardはお隠れあそばすので、デスクトップ上にWidgetを置けるようになります。
PiyoTrackbackPing.widgtで試すと

のようになり、普通のアプリケーションと同じ階層に常駐されます。

Widgetを常駐化させることで、直して・再読み込みして・確認する、といった作業が非常にスムーズになります。

注意:しれっと書いてますが、再読み込みは、対象Widgetにフォーカスして、Command+Rを押せば実施されます。独特の渦巻きアニメーションは再読み込みの証です(たぶんCoreImage対応の場合)。

  • 長所:実際と同じ動作環境で検証できます。開発モードとコンソールというアドバンテージがあります。
  • 短所:JavaScriptの挙動が把握にしくいです。

SafariでもDashboard上でも素敵なデバッグ環境を作る

SafariでもDashboardでもうまいことデバッグするためには、debug要素をWidgetに加えて、Safariのときに表示できるようにすると良いです。どちらの場合でも利用するメソッド(例えばdebugとする)は同じにして、利用するメソッド側で、debug要素への追記[Safari]かalert文による出力[Dashboard](Dashboard上でコンソールに出力される)かを環境により切り替えればよいのです。常にdebug用の情報を出力することをさけるため、大域変数でフラグを用意するとよいでしょう。

ちなみに、私は某Widget作成時に垂れ流す方式を思いつかず、単純にdivタグに表示される文字列を置き換える方式をとっていました。そのおかげで、見たい値より後に実行されるデバッグ処理をいちいちコメントアウトする、という不毛な作業を続けたため、この部分の説明を読んだとき自分のおろかさに涙しました。

CSSで場所決められるのでdebug要素は好きなところにdivタグで突っ込めばよいです。次のリストはデバッグ情報を表示するメソッドとDebug表示を切り替えるメソッドを含んでいます。

<html>
     <head>
     <style type="text/css">
          #debugDiv {
               border-style: dotted;
               border-color: black;
               background: gray;
               position: absolute;
               bottom: 0px;
               left: 0px;
               height: 200px;
               width: 90%;
               overflow: scroll;
               display:none;
          }
     </style>
     <script language="JavaScript" type="text/javascript">
          var debugMode = false;

          // Write to the debug div when in Safari.
          // Send a simple alert to Console when in Dashboard.
          function DEBUG(str) {
               if (debugMode) {
                    if (window.widget) {
                         alert(str);
                    } else {
                         var debugDiv = document.getElementById('debugDiv');
                         debugDiv.appendChild(document.createTextNode(str));
                         debugDiv.appendChild(document.createElement("br"));
                         debugDiv.scrollTop = debugDiv.scrollHeight;
                    }
               }
          }

          // Toggle the debugMode flag, but only show the debugDiv in Safari
          function toggleDebug() {
               debugMode = !debugMode;
               if (debugMode == true && !window.widget) {
                    document.getElementById('debugDiv').style.display
                       = 'block';
               } else {
                    document.getElementById('debugDiv').style.display
                       = 'none';
               }
          }
     </script>
     </head>

     <body onload='toggleDebug();DEBUG("loaded!");'>

          <!-- All our existing content... -->
          Hello World!
          <!-- Declare the debug div anywhere.  CSS handles the positioning
-->
          <div id='debugDiv'></div>

     </body>
</html>
PiyoTrackbackPing.widgtでは同様の処理を追加すると

のようになり、便利に使えそうです。

代替案としては、window.openによって別ウィンドウを開いて、そいつのdocumentオブジェクトを使って表示する方法もあります。その際にはSafariのポップアップ防止機能をオフする必要があります。

なお、この文章では、JavaScript自身のデバッグ手法については触れません。専門書を読んで下さい。


先生!Widgetが動きません

作成したWidgetが

  • Dashboardから起動しても、ダブルクリックしても何も起こらない。
  • Widgetが表示されない。
  • 表示しても数秒で消えちゃう。

なんて症状がでた場合、さまざまな理由が考えられます。


Dashboardが表示されない

WidgetをダブルクリックしてもDashboardが表示されない・・。そんな場合はバンドルしている中身に問題がある可能性が高いです。

  • Default Image:Widgetバンドルの一番上のフォルダに"Default.png"という名前の画像ファイルが必要です。DashboardはWidgetの中身を読み込んでいる間、このファイルを表示します。このファイルがないとWidgetはロードされません。

  • Info.plist:Widgetバンドルの一番上のフォルダに含まれている必要があります。必要となる情報はいくつかありますが、最も重要なのは次の二つです。
    • CFBundleIdentifler key:Info.plistにあるこの情報はMacOS Xの全てのアプリケーションにとって必要なものです。Widgetも例外ではありません。こいつが定義されていないとWidgetは読み込まれません。

    • MainHTML key:Widgetによって読み込まれる内容を定義する情報です。この情報がないとWidgetは読み込まれません。このキーの値は大文字小文字を区別します。正確なファイル名である必要があります。

Default.pngは表示されるけど、コンテンツ(中身)が表示されない

DashboardにWidgetが表示されたら、コンテンツが読み込まれているあいだにデフォルトの画像(Default.png)が表示されることを確認してください。もし、デフォルト画像が消えたり、何も置き換わらなかったら、たいていMainHTMLキーの値が実際のHTMLファイル名にマッチしなかったことを意味します。

注意:もしこの状況になったら、Widgetを閉じることすら難しくなります。この場合、Widgetバーを表示させて、全てのWidgetに対して閉じるためのボタンを表示させて対応するのが簡単です。

コンテンツは表示されるけど、レスポンスがない場合は、きっとJavaScriptエラーが発生しているので、コンソールにてエラーを確認してください。

最初の段階はともかくとして、開発時にはこのエラーが一番発生します。背景画像のみで中身が表示されないのです。コンソールやJavaScriptコンソールの存在を知らなかった頃の私は新規作成の関数部分に適当に当りをつけて削除しては実行を繰り返していました。

なお、これらケースの場合、少なくとも直す前にもう一回実行しなおしてみることをお勧めします。たまに正常なものもこれらのようにうまく表示されない場合があり、再実行によりうまく表示される場合がありました。

iボタンを押すとDashboardから抜ける、などの症状の場合も再実行をお試しくださ い。


Widgetバーに表示されない

Widgetバーに表示されるためには、/Library/Widgetsか ̄/Libarary/WidgetsのどちらかにWidgetが存在しないといけません。もしWidgetがIcon.pngを持たなかった場合は、一般的なアイコンが利用されます。

どっちかのフォルダにいれられたWidgetは次回のDashboard表示時に出現します。もし表示されなければ、リストをグルグルしてください。だいたいスクリーン外にあったりします。WidgetバーはWidgetのローカライズされたCFBundleDisplayNameにより、アルファベット順で表示されます。

注意:MacOS Xの他のものと同じように、WidgetバーはローカライズされたCFBundleDisplayNameにマッチしなかった場合、ファイル名を優先します。もし、Widgetバンドルが改名されたり、CFBundleDisplayNameが定義されなかったりした ら、ファイル名が表示されます。

Webコンテンツ:ネットワーク、埋め込み、ファイルI/O、Java

プログラミングガイドのセキュリティにて記載されているように、特定のWebコンテンツ機能はは特別なInfo.plistキーを要求します。それは以下のものです。

  • ネットワークアクセス:ネットワークを利用したアクセス
  • インターネットプラグイン:QuickTimeのようなEMBEDタグによるWebプラグイン
  • ファイルアクセス:widgetバンドル外のファイルシステムへのアクセス
  • Java Applet:APPLETタグによるアプレットの使用

適切なInfo.plistキーなしに、これらのカテゴリのいずれかに属したコンテンツは、機能しません。プログラミングガイドのセキュリティの部分をよく読みましょう。


Widgetプリファレンス

良くある間違いは、widget.setPreferenceForKey関数における引数の順序です。最初の引数が値で、次の引数がキーです。Javaの開発者はよくあるsetterメソッドと順番が逆なので注意しましょう。逆にすると、正しく保存されないので、 widget.preferenceForKeyがnullを返します。

保存するにはキーと値が両方とも文字列にした方が良いことも重要です。。プリミティブな値やオブジェクトはたいていうまく保存されません。このような試みは次のように「コンソール」アプリでエラーとして出力される場合が多いので確認しましょ う。

DashboardClient[869] CFLog (15): Could not generate XML data for property
list

WidgetプリファレンスによりWidgetの状態を保存することができますが、いついかなるときでもユーザはログアウトしたり再起動したりすることを認識する必要があります。再びDashboardが表示されたときに状態を復活させたいならば、状態が変わったら値を保存し、Widgetが読み込まれたら値を参照する必要があります。

以上。

( ・∀・)つ〃∩ ヘェーヘェーヘェー">
投稿時間: 2005年06月07日 (火) at 10:29       
 

←前  |  ↑↑Blog |  ↑Category |  ↑Entry top |  ↑Comment |  次→
←前  |  ↑↑Blog |  ↑Category |  ↑Entry top |  ↑Comment |  次→