Meditation Noteアップデート作業中(2)

リリース済みのアプリのアップデート作業ということでサクっと終わると思っていたのですが、ことあるごとにはまってしまい悪戦苦闘しています。さぼってるわけではないんですが、スキル不足というか集中力が続かないというか。。。
アプリを制作する上でAngularJSと英語がわからないことの不便さも痛感します。いまさらですがAngularJSの勉強をしようかと思っています(英語は来世で)。

進捗

とりあえず、このアップデート作業で何をしていたのか、制作日記から抜粋し振り返ってみます。

  • 10/25 アップデート作業を開始する
  • 10/28 v1.0.0のファイルデータの変換関数完成。前機能の削除、管理テーブルの変更
  • 11/3  日付・時間表示形式設定機能完成
  • 11/8  グラフ機能完成。ブログにAdSense広告が表示されるようになってうかれる
  • 11/9  メモ機能完成。アメリカ大統領選挙。アンドロイダーの公認デベロッパー登録が完了してうかれる
  • 11/14 ニフティバックエンドを利用した機能追加
  • 11/15 レイジーリピート導入
  • 11/16 Onsen UI v2に移行

グラフはずっとflotr2を使っているのですが、今回はじめて棒グラフと線グラフを一緒に表示することにしたのでちょっと時間がかかってしまいました。あと、ニフティバックエンドを使ってみたくて、いろいろ調べたりサンプルを作って勉強したりもしてました。
現在Onsen UI v2対応とマテリアルデザイン化を行っているところです。
この作業に意外と手間取っています。

Onsen UI v2への移行

Onsen UI v1からv2への移行自体は割とスムーズにいきました。Onsen UIドキュメント「Onsen UI 1 系からの移行」や、Monaca公式ブログ「Onsen UIをバージョン1から2へ移行するには」にわかりやすい説明があります。ただ私の場合(localkit)、「JS/CSSコンポーネントの設定画面で Onsen UI をいったん削除する」とあったのですが、インストールしたコンポーネントの一覧に Onsen UI がなかったため、そのまま Onsen UI v2.0.3をインストールしました。そうしたところ、アプリの画面がへんてこに。調べてみると、なぜか v1.3.8 の CSS ファイルを参照しているらしく、Onsen UI を削除してから再度インストールをしたら正常に動作するようになりました。

マテリアルデザイン

Onsen UI v2をインストール後アプリを起動すると、自動的にマテリアルデザイン化されるようです(IDEのプレビューは ons.platform.select(‘android’); を実行すると対応します)。最初、マテリアルデザインに違和感があり、元のデザインに戻そうかと考えたのですが、何度も見ているうちに慣れてきたというか逆にシンプルさが気に入り、今回アプリをマテリアルデザインに対応させることにしました。
Onsen UIコンポーネント(ons-xxx)は自動的にマテリアルデザイン化してくれるのですが、それ以外の要素(div, li, …)は手動で対応させる必要があります。今、この作業をOnsen UI Theme Rollerを参考にしながら行っています。例えば、ulリストは以下のような記述になります(Material List引用)。

<ul class="list list--material">
  <li class="list__item list__item--material">
    <div class="list__item__center list__item--material__center">
      <div class="list__item__title list__item--material__title">Orange</div>
      <div class="list__item__subtitle list__item--material__subtitle">Sweet fruit that grows on trees.</div>
    </div>
  </li>
  <li class="list__item list__item--material">
    <div class="list__item__center list__item--material__center">
      <div class="list__item__title list__item--material__title">Pear</div>
      <div class="list__item__subtitle list__item--material__subtitle">Funny-shaped fruit.</div>
    </div>
  </li>
</ul>

クラス名はBEM(Block Element Modifier)を適用しているので慣れてない私にはどこで切れてるか見分けるのが大変。目がしょぼしょぼしてきますが、これを自分で作成するのはもっと大変なわけで、こういうサンプルは非常にありがたいです。

なお、マテリアルデザインについてはグーグルがガイドラインを公開しています。
・公式サイト: Material design
・日本語のドキュメントはこちらからダウンロードできます。

おわり

来週リリースを目標に明日から本気出します。

リリースしたアプリの起動がある日突然遅くなった話

昨日 11/2 お昼前、いっぷくしようと思い、自作アプリ Smoking Note を立ち上げようとしたときのことです。
… アプリの起動が妙に遅い
スプラッシュスクリーンが消えた後も、時刻などの動的に描画する要素がなかなか表示されない。ちょっと許容できない遅さで、たまたまかなと思い何度か立ち上げなおしても状況は変わらず。アプリの起動後の動作には問題がなく、広告自体もちゃんと表示されるのですが、ただただ遅い。

アプリのキャッシュをクリアしても、スマホを再起動してもだめ。
「いやいや、今日ついさっきまで普通に起動してたのに、冗談でしょう?」と軽く現実逃避しながら昼食をとり、気を取り直して再度アプリを立ち上げてみても、やっぱり起動が異常に遅い。

このとき、めっちゃ焦りました。さーっと血の気が引く感じ。

突然動作が変わる原因として、広告配信かファイル関連くらいしか思いつかなくて、私が使用している広告 admob について現在なにか起こっていないか、ネットでずっと調べてました。admob の表示には cordova プライグインの cordova-admob-pro を使っているので、これについても調べたのですが、それらしい情報はどこにもなく、admob への問い合わせ先も見つからない。

で、デバッグ開始です。
実機での起動時の動作確認のため、ちょっとめんどいけど仕方なし。
まず、ソースを変更せず(アプリ名だけ変更)デバッグビルドで構築して実機にインストールし、現象が再現するかを確認。
うん、再現します。間違いなく。
この時点でファイルなしでも発生することが確認できました。
また、広告をテスト広告に変更しても同様に発生しました。
次に、ブレークポイントは貼れないのでコンソール文とアラート関数を埋め込み、ビルド・インストールし動作確認。
これを繰り返してやっとのことで発生個所を特定しました。

/*** test 2016/11/02 ***/
window.alert("init_ad in");
  if(window.AdMob) AdMob.prepareInterstitial({
                     adId: admobid.interstitial,
//                     isTesting: true,
                     autoShow: false
                   });
window.alert("init_ad out");  // 上記の alert 表示後の
                              // この alert 表示が異常に遅い!

上記のインタースティシャル広告作成関数でプログラムがしばらく止まるんです
この関数自体は非同期関数のようにすぐに返ってくるはずなんですが、この日このとき、なぜかすぐに返ってこない。

で、特定はできたけどこれからどうしようと考えながらいっぷくしようと Smoking Note を立ち上げたら …
なおってる!
なんかしらないけど、普通に起動しました!
このとき、午後 18:16 。
まるで夢のような出来事でした。
こうゆうケースが現実にありましたということで、ご参考まで。

クラス操作関数

以前に正規表現を使用したことを思い出し調べていたときにでてきたソースです。実際、正規表現は一か所でしか使用していないのですが、こうゆう使い方もあるということで、クラス操作関数とあわせて備忘録として残しておこうと思います。
クラス操作は classList のメソッドで容易に行うことができますが、Can I use で調べてみると Android 4.4 からの対応になっています。それ以前のバージョンに対応する場合は classList の代用が必要になりますが、そのようなときに使えるかと思います。(indexOf()を使用しているのでクラス名の重複部分には注意が必要になります)

/***
 * クラス追加
 */
function add_class(elem, cls) {
    if (!elem || !cls) return;
    elem.className = add_str(elem.className, cls);
}
/***
 * クラス削除
 */
function remove_class(elem, cls) {
    if (!elem || !cls) return;
    elem.className = remove_str(elem.className, cls);
}
/***
 * クラス確認
 */
function has_class(elem, cls) {
    if (!elem || !cls) return;
    return has_str(elem.className, cls);
}
/***
 * クラス追加・削除
 */
function toggle_class(elem, cls) {
    if (!elem || !cls) return;
    elem.className = toggle_str(elem.className, cls);
}

// 文字列 strings に文字列 str を追加する
function add_str(strings, str) {
    var i = strings.indexOf(str);
    if (i === -1) {
        var len = strings.length;
        if (len !== 0 && strings.charAt(len -1) !== " ") {
            strings += " ";
        }
        strings += str;
    }
    return strings;
}

// 文字列 strings から文字列 str を削除する
function remove_str(strings, str) {
    var i = strings.indexOf(str);
    if (i !== -1) {
        strings = strings.substr(0, i)
                + strings.substr(i + str.length + 1);
    }
    return strings;
}

// 文字列 strings に文字列 str が含まれているか検査する
function has_str(strings, str) {
    var regex = new RegExp("(^|\\s)" + str + "(\\s|$)");
    if (regex.test(strings)) return true;
    else return false;
}

// 文字列 strings に文字列 str があれば str を削除し、
// str がなければ追加する
function toggle_str(strings, str) {
    if (has_str(strings, str)) {
        return remove_str(strings, str);
    } else {
        return add_str(strings, str);
    }
}