2014年5月10日土曜日

バージョン3.2.1以降で、なぜか実機でのみエラー発生

久しぶりに書きます。その前に、少し謝罪します。1年以上前にコメントによる質問をいただいたようですが、まったくアクセスしてなくて、しかもコメントが来た知らせをメール等で教えてくれないようで、回答せずに過ぎてしまいました。もう見てないと思いますが、ごめんなさい。知らせてくれる設定があるかどうか、あとで調べてみます。

 

さて本題です。以前に開発したiPad向け業務アプリですが、機能追加の依頼があり、久しぶりに手を入れることになりました。せっかくなので、Titaniumu MobileもXcodeも最新版に入れ替えておこうと、それぞれバージョンアップして開発を始めました。開発は順調に進み、エミュレータでのテストもクリアーしました。でも、実機に転送して動かしてみたら、ある機能だけが動きません。今回の機能追加とはまったく関係のない部分なので、何が起きたのか分かりませんでした。

とりあえず、エラーの内容を調べました。手順も含めて、少し詳しめに書いてみますね。iPadをMacに接続し、XcodeでiPadのコンソールログを見ます。開発したアプリ名で、しっかりエラーが発生していました。しかし、このままでは、エラーの箇所が特定できません。iOS用バッケージを作るときに、JavaScriptのminificationを設定しているので、エラー箇所がline 1になってしまってます。minificationをオフしてバッケージを生成し、実機に転送してエラー箇所を特定する必要があります。

エラーの内容は「'undefined' is not an object」でした。これは参照した変数にオブジェクトが入ってないときなどに発生するエラーです。エラーが発生したJavaScriptは以下のとおりです。

xxx = fileMix.read().text; // xxxは実際の名前ではない

このままでは、メソッド「read()」で発生しているのか、プロパティ「text」への参照で発生しているのか判断できません。そこで以下のように分割しました。

DebugX01 = fileMix.read();
xxx = DebugX01.text; // xxxは実際の名前ではない

ここで追加した変数「DebugX01」は、デバッグ用の変数であるため「Debug」という文字列で始めています。削除し忘れたりしても後から明確に分かります。また病気で突然倒れて開発を中断したとしても、この部分はデバッグのために追加したことが後から明確に思い出せます。こういう工夫は意外に大事ですね。

本題に戻りましょう。分割して実機で実行したら、「text」プロパティへの参照でエラーが発生していました。エミュレータではエラーが発生しないのに、実機だけで発生するというのも奇妙です。また、この部分は今回の修正範囲ではなく、以前は正常に動いていた箇所です。JavaScriptのコードが原因とは考えられません。別な原因で、何か問題が発生したと推測できます。たとえば、Titanium Mobileの仕様が変わって、以前のコードが動かなくなったとかです。

バージョン2.xでは正常に動いていたので、エラーが出る最新バージョンから順番に戻ってみることにしました。iOS用バッケージを生成し、実機に転送してテストするので、けっこう大変です。3.0、3.1、3.2などと大きな区切りで試し、変化したバージョンを特定するのが一番効率的です。そうして調べた結果、バージョン3.2.0では正常に動作し、3.2.1からエラーが発生していました。つまり、3.2.0から3.2.1への変更内容を調べれば、何か原因が分かりそうです。リリースノートを調べたのですが、関係しそうな機能が見付かりませんでした。

エラーが発生した箇所の機能を説明していませんでしたね。UI部品のWebViewを使って、グラフを表示する機能です。使いたいグラフ機能がJQueryを前提としているので、HTMLと組み合わせてWebViewで表示したというわけです。HTMLやJavaScriptやグラフ用データをテキストとして合体させ、WebViewのtextプロパティに設定して表示しています。変数fileMixには、Ti.Filesystem.Fileオブジェクトを入れ、readメソッドでファイルを読み込みます。読み込まれた内容はTitanium.Blobオブジェクトとなり、textプロパティで内容を参照できます。

エラーの意味ですが、DebugX01変数のtextプロパティを参照したら「DebugX01変数にはオブジェクトが入ってない」と怒られたという感じでしょうか。エラー発生箇所の一連の処理では、HTMLやJavaScriptなど何種類かのフィアルを呼んでいます。これら全部でエラーが発生してるわけではありませんでした。よく調べたら、JavaScriptを読み込むコードだけでエラーが発生していました。これが何かのヒントになりそうです。

試しに、JavaScriptのファイル名を変更してみました。ファイル名「xxx.js」を「xxx.js.txt」に変えただけです。そうして実機で実行してみると、な、な、何と、エラーが発生しないではありませんか。原因は、何なのでしょう。エミュレータでは動いて、実機ではエラーになるというのも不可解です。同じiOS7上で動作させ、Titanium Mobileのバージョン3.2.0以前では正常に動き、3.2.1以降ではエラーになります。いったい何が原因なのでしょうか。分かりません。原因は不明ですが、対処方法は見つけました。

少しだけ、原因につながりそうな考察をしましょうか。3.2.1以降のTitanium Mobileでアプリを作った場合、Ti.Filesystem.Fileオブジェクトで「.js」拡張子のJavaScriptテキストを読み込んだとき、それはTitanium.Blobオブジェクトにはならず、オブジェクトではない何かになってしまうということ。しかも、エミュレータではオブジェクトになり、実機ではオブジェクトにならないという奇妙な現象。また、同じファイルを「.txt」拡張子に変更すると、実機でもオブジェクトになってくれます。おそらく特定の条件を満たしたときだけ、JavaScriptテキストがコードとして解釈されて、オブジェクトではなくなるのでしょうね。その現象が、3.2.1以降のTitanium Mobileで、しかも実機だけで発生するという。

まあ、原因は不明ですが、対処方法が見付かって良かったです。同じ現象にあたった人がいたら、今回の事例を参考にしてください。そういう思いで、久しぶりに書きました。でも、また冬眠します。