Windows 10でデスクトップ向け(Universal)アプリをビルドする


Windows Store Apps

 記憶が正しければ確か2013年くらいから対応予定とだけ宣伝されていたTitaniumのWindows(Phone)対応ですが、紆余曲折を経てSDK 4.1からサポートされるようになりました。

 Titaniumの「Write once, adopt anywhere」の性質からすると、SDKがどのようにWindowsに対応するのかは非常に重要な問題です。というのも、Windowsでは例えばWinJSのようにHTMLとCSSでUIを作ってJavaScriptからWindowsのコンポーネントにアクセスする手法がサポートされていたり(Tizenみたいですね)、その他にも.NETフレームワークやWindowsランタイム、Win32APIなどなど利用できる技術がたくさん用意されているので、アプリを作ると一口に言っても、どの技術を採用するのかでその後の展開が大きく変わってくるのです。

 さらに事態を複雑にするのが、Windows 10から本格的に登場したUniversalアプリという概念です。これはWindows 8からWindowsランタイムとして初めて導入されたものですが、タブレットやハンドヘルド機を含め今後も多様化するデバイスに対応するため、共通のランタイムが用意され、これに対応させることでアプリを複数のデバイスファミリーで利用可能にするという大胆な試みです。つまり、これまではPhone用アプリやデスクトップ用アプリだったのが、共通のランタイムをサポートしつつ、デバイスファミリー毎の機能にどう対応したかで対応プラットフォームが決まるアプリを作成することになるわけです。

Universal App device families

 それだけではありません。.NetフレームワークがMacやGNU/Linuxに移植されたことを受けて、今後マルチプラットフォームのシステムにとってはこれも魅力的な選択肢となるかもしれません。WinJSとDOM APIはTitaniumが従来からサポートするMobileWebと統合できるかもしれず、実際にPhoneGap/CordovaはWinJSに対応しているようです。

 これだけの要素がある中で、ではTitaniumは現状どのようになっているのかというと…

 Titaniumが正式にサポートしているのはWindows Phone 8.1のストア用アプリの形式です。Windows Phoneといえば、世界シェアはきっと3%くらいです。世界の人口が60億人とすれば3%は約1.8億(あれ、Windows のバージョン8.1の反対になった、めでたいな)ですから、まあ日本語並のシェアってところですかね。全体から見るとあってもなくても同じ誤差みたいなものです。国語の試験で苦労するのがバカバカしくなってしまうかもしれませんね。

 と、初期の調査はここまでで終わっていたのですが、途中正式にサポートされているWindows 8.1 + Visual Studio 2013以外の対応状況、Windows 10やVisual Studio 2015での動作を調べていたところ、ビルドした際に生成されるディレクトリの中に妙なファイルを見つけました。「Package.win10.appxmanifest.in」や「Package.win10.appxmanifest.in.ejs」という名前のファイルなのですが、拡張子ejsはおそらくテンプレートエンジンを噛ませて使うテンプレートのはず、そしてこのappxmanifestというのは、Windows 10のUniversalアプリ用のmanifestファイルに違いありません(だって名前が…)。

 「時計仕掛けのオレンジ」のアレックスなら「Ho, ho, ho!」とニヤケ顔でつぶやくところです。

ho ho ho

 そこで色々と調べてみると、まあ出るわ出るわ、Appceleratorは公式には時代遅れのWindows 8.1 + Visual Studio 2013しかサポートしていないのに、Windows版SDKの直近のコミット内容を追いかけると、Windows 10やVisual Studio 2015での動作を修正するものがザクザク見つかるではありませんか。

 そして、使いにくさに定評あるWindowsのコマンドプロンプトからappc ti help buildを実行すると、Appcelerator Studioのメニューにはないビルドオプションが見つかりました。

 そうです、赤線部の「ws-local」というターゲット名こそが、Universal Appとしてビルドして自分のマシンで実行するためのものなのです。

 では実際にやってみましょう。先に手順をまとめます:

  • Visual Studio 2013をインストールしてPhone SDK 8.1も用意する
  • Visual Studio 2013を惜しげもなくアンインストールしてVisual Studio 2015をインストールする
  • Windows 10にね。
  • Studioからストア向けにビルドして証明書を作成する
  • ミステリアスにもビルドには失敗する
  • コマンドプロンプトでビルドを実行する

 どうもCLIから証明書を作成するとうまくいかないので、まずStudioからビルドして証明書を作成しましょう。

 赤線部のPackageとWindows Storeを選択していることを確認しましょう。この時、パスワード欄は必ず空欄にします。理由?そんなものはありません。

 このまま実行すると秘密鍵パスワードを入力するダイアログが表示され、キーの作成には成功しますが、肝心のビルドには失敗します。パスワードは忘れないようにしてください。

 次にコマンドプロンプトでプロジェクトのディレクトリに移動し、以下のコマンドを実行します。

> appc ti build -p windows -T ws-local -R generated.pfx -P 作成した鍵のパスワード

 これでビルドが実行できます。しばらくするとPowerShellの画面が立ち上がり

 キーをインポートするか尋ねられますのでYを入力して続行します。変更の許可を求められるので許諾し、ビルドが完成すると、Titanium Desktop以来のデスクトップアプリ(いいえWindowsなのでUniversal Appですね)が起動します。

 ほらね!

 おそらく、最大の難関はここまで動作するように環境を構築することだと思いますので、そこは別のエントリで解説したいと思います。

 Universal Appとしてビルドできることの一番大きな意義は、これまではPhone向けだけしかターゲットとして指定できなかったのが、タブレットやその他デバイス向けのアプリを作成できるようになることで、WindowsのPhone向けだけでなく汎用的なStoreに対応できることです。今後、SurfaceやWindows 10対応のハンドヘルド機がいつくか発売されますが、それらをカバーすることでクライアントをiOS、Androidに加えてWindows 10の様々なデバイスまでカバーできるようになれば、多くのサービスのクライアントをTitaniumで一気にカバーできるようになります(大変ですけどね)。これは夢が広がるのではないでしょうか。

おまけ

未実装のプロパティはTITANIUM_PROPERTY_UNIMPLEMENTEDというマクロで処理され、呼び出しても反応がないようになっているので、ソースコードをgrepすると見つかります。

localhost:Source toshiro$ grep -r "TITANIUM_PROPERTY_UNIMPLEMENTED" ./*  | wc -l
     250
localhost:Source toshiro$ grep -r "TITANIUM_PROPERTY_UNIMPLEMENTED" ./*  | head
./Media/include/TitaniumWindows/Media/AudioPlayer.hpp:          TITANIUM_PROPERTY_UNIMPLEMENTED(allowBackground);
./Media/include/TitaniumWindows/Media/AudioRecorder.hpp:            TITANIUM_PROPERTY_UNIMPLEMENTED(compression);
./Media/include/TitaniumWindows/Media/AudioRecorder.hpp:            TITANIUM_PROPERTY_UNIMPLEMENTED(format);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(albumArtist);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(albumTitle);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(albumTrackCount);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(albumTrackNumber);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(artist);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(artwork);
./Media/include/TitaniumWindows/Media/Item.hpp:         TITANIUM_PROPERTY_UNIMPLEMENTED(composer);

いろいろありそうですね!