iPhone/Android両対応アプリを作る際にハイフンを含んだApp IDを使うとき

あなたの会社のドメインには、ハイフンが含まれていますか？

ドメイン名にハイフンを含めるのはSEO的に良くないとか、外人さんからはカッコ悪いと言われるらしい（逆に日本人はハイフンを入れたがるらしい）のですが、実は私の所属している組織のドメインには、残念なことにハイフンが含まれています。

だから何だという話ですが、これがTitanium MobileでiPhone/Android両対応のアプリを開発しようとしたときに、ちょっと面倒臭いことになりました。


## App Idのハイフン問題

問題となったのは**App Id**です。

何かしらアプリを作ろうとしてTitanium Mobileのプロジェクトを作成しようとした場合、初めにApp Idと呼ばれるアプリを識別するためのIDを付けます。

このApp Idは一般的に、ドメインを逆から並べたもの＋アプリケーションを表す文字列という組み合わせにすることが慣例となっています。

これはiPhoneでもAndroidでも同じルールなのですが、ハイフンの扱いに違いがあります。

Androidの場合、ハイフンを使っているとコンパイルできないのです。
これはJavaのパッケージ名のルールに寄るもので、Titanium Studioでプロジェクト作成時にハイフンを含んだApp Idを入力すると、不正な形式というエラーがでてしまいます。
こういった場合、Android(Java)では一般的にハイフンをアンダースコアに置き換えて使います。


じゃあアンダースコアに置き換えれば良いじゃん、と思いきや、その場合、今度はiPhoneで問題がでます。

iPhoneではApp Id（Bundle identifier）にはアンダースコアが使えないのです。
試しにXcodeで、Bundle identifierのドメイン部分になるCompany identifierにアンダースコアを含んだ文字列を入力すると、その下に自動的に表示されるBundle identifierの該当部分がハイフンに置き換えられています。


まさに、あちらを立てればこちらが立たず、というわけです。


## 単純な解決方法

とりあえず、どちらかのパターンで作っておいて、ビルドするプラットフォームに合わせて都度書き換えることで、ほぼ同じApp Idを使うことができます。
ある程度どちらかのプラットフォームに集中して作業ができる場合は、これでも良いですね。

例）example-japan.comというドメインだった場合

**iPhone向け**

```tiapp.xml
<ti:app xmlns:ti="http://ti.appcelerator.org">
    <id>com.example-japan.appid</id>    <!-- ← ハイフン使用 -->
    <name>App Id Test</name>
	<!-- 以下省略 -->
</ti:app>
```

**Android向け**

```tiapp.xml
<ti:app xmlns:ti="http://ti.appcelerator.org">
    <id>com.example_japan.appid</id>    <!-- ← アンダースコア使用 -->
    <name>App Id Test</name>
	<!-- 以下省略 -->
</ti:app>
```


## ハイフンとアンダースコアを自動的に書き換える for Alloy

並行開発をしていて、頻繁にプラットフォームを切り替えて動作確認などをしたい場合は、上記の方法は面倒です。

そこで、Alloyで導入されたalloy.jmk内で、プラットフォームに応じてtiapp.xml内の<id>を書き換えるようにしてみました。
単純にプラットフォームがiOSの場合はアンダースコアをハイフンに、Androidの場合はハイフンをアンダースコアに書き換えているだけです。

なお、XMLの解析にlibxmljsというモジュールを使っているので、必要に応じてnpmでインストールしてください。

書き換えた場合、念のため古いtiapp.xmlをtiapp.xml.prevという名前でリネームしています。

```alloy.jmk
task("pre:compile", function(event, logger) {
  var fs = require('fs'),
      path = require('path'),
      libxmljs = require('libxmljs');

  var STR_HYPHEN = '-';
  var STR_UNDERSCORE = '_';

  var tiappXmlPath = path.join(event.dir.project, 'tiapp.xml');
  var xmlStr = fs.readFileSync(tiappXmlPath, 'utf8');
  var xmlDoc = libxmljs.parseXmlString(xmlStr);

  var targetStr = '';
  var repStr = '';

  switch (event.alloyConfig.platform) {
  case 'ios':
    targetStr = STR_UNDERSCORE;
    repStr = STR_HYPHEN;
    break;
  case 'android':
    targetStr = STR_HYPHEN;
    repStr = STR_UNDERSCORE;
    break;
  default:
    break;
  }

  var regexp = new RegExp(targetStr, 'g');
  var idStr = xmlDoc.root().get('id').text();

  if (idStr.match(regexp)) {
    idStr = idStr.replace(regexp, repStr);

    xmlDoc.root().get('id').text(idStr);

    try {
      fs.renameSync(tiappXmlPath, tiappXmlPath + '.prev');
    } catch (e) {
      logger.error('Failed to rename current tiapp.xml.');
      throw e;
    }

    try {
      fs.writeFileSync(tiappXmlPath, xmlDoc.toString(), 'utf8');
    } catch (e) {
      logger.error('Failed to rewrite tiapp.xml');
      throw e;
    }
  }
});
```

まあ、そもそもドメインにハイフンが使われていない人たちや、入っていてもiPhoneもしくはAndroidのどちらか向けにしか開発しない人には関係ない、とてもニッチな問題ですね。

## Titanium CLIプラグイン「dev.tiapp」による対応 ※2015/04/24追記

以下のエントリで、Titanium CLIプラグインを使った対応方法についても紹介しています。

[製品版と開発版で設定を分離できるTitanium CLIプラグイン「dev.tiapp」 - Qiita](http://qiita.com/umi_uyura/items/a344db0fc7c90f4fd17a#%E6%B4%BB%E7%94%A8%E4%BE%8B%E3%83%8F%E3%82%A4%E3%83%95%E3%83%B3%E3%82%92%E5%90%AB%E3%82%93%E3%81%A0app-id%E3%82%92%E4%BD%BF%E3%81%86%E3%81%A8%E3%81%8Ddevtiapp%E7%89%88)