WindowsのUnityからiPhoneにボタン一発自動デプロイ

新しいパソコン買わずに、有料サービスも使わずに、手持ちのWindowsだけでiOS開発できればなー、と思ってたら上手くいったのでお伝えします。

コードとか公開するほど複雑なシステムでもないので、大体の処理の流れとか、自分がハマったところとかを中心に書いていきます。詳しいことは英語版のほうに書くかもしれません：[英語版](https://medium.com/@ryoichirooka/unity-ios-on-windows-deploying-to-iphone-from-within-unity-editor-on-windows-machine-using-vmware-15eb7cbcbf83)

概要
=======

ユーザ体験（？）としては、macOSでUnity Editorから「Build & Run」するのと同じことを、Windowsだけで出来るようにしました。

1. まずVMWareでmacOSを起動、専用のシェルスクリプトを立ち上げる。
2. iPhoneをUSBで接続しておく。
3. WindowsのUnity Editorで開発する。
2. 実機デバグがしたいときに、エディタ上でBuildボタンを押す。
3. しばらく待つ。
4. アプリがiPhoneで起動するので、デバグを始める。
4. 用が済んだらアプリを閉じて3に戻る。

要するに、Windowsの開発環境で、iPhoneへのデプロイがボタンひとつでできるのです。内側ではシェルスクリプトが仮想macOS上で共有フォルダを監視していて、新しいビルドフォルダを発見するたびにXCodeのコマンドラインツールなどで自動的にビルド・デプロイします。これを手動でやると非常に面倒くさいので今回自動化してみた次第です。

あと、仮想macOSはGUIが普通に使えるので、一応XCodeでアプリのリリースもできます。今のところTestFlightが普通に動いてるのでAppStoreもできるはず。。

ただし、ボタン押下からiPhoneで確認できるまでは、普通のサイズのプロジェクトで5~10分かかります。これはもう仕方ないと思ってます。

お断り
=======

- サンプルは準備できてないです(インフラなので)
- 仮想macOSはグレーなので会社ではやらんといてください。
- __Apple Developer Programは有料です__。
- 各ビルドは5~10分ほどかかります。

必須項目
=======

- Cドライブの他に物理ディスク。HDDでOK。
- macOSのコンソールについて基本的な知識。
- XCode周辺の土地勘。
- それなりの時間。

使うもの・入手方法
=======
VMWareとmacOS
-------
これはぐぐったら普通に配布されてます（いいのか？）
https://www.pcsteps.com/2157-mac-os-x-virtual-machine-vmware-player/

Shared Folder
--------
VMWareの機能です。ホスト(Windows)のファイルにゲスト(macOS)からアクセスするために必要になります。
https://www.vmware.com/support/ws5/doc/ws_running_shared_folders.html

xcodebuild
----
XCodeプロジェクトをビルドするためのコマンドラインツールです。XCodeに同梱されてます。
https://developer.apple.com/library/archive/technotes/tn2339/_index.html

ios-deploy
------
非公式ですが、実機デプロイ用のコマンドラインツールです。インストールはbrew→npmで非常に簡単。
https://github.com/ios-control/ios-deploy


実装
====

基本的には、macOS側のシェルスクリプトでShared Folderを監視して、変更があり次第ビルド・デプロイを始める感じです。監視方法としてはタイムスタンプをファイルで読み書きするのが簡単・柔軟だと思います。

Windows側（Unity Editor）：

- ビルド毎にポストプロセスでタイムスタンプを更新する。
- Shared Folderに成果物を置く。

macOS側（シェル）：

- 起動時にShared Folderのマウントをしておく。
- 5秒置きぐらいにShared Folderのタイムスタンプを監視、
- 変更があったらプロジェクトをデスクトップにcp、
- xcodebuildでビルド、
- ios-deployでiPhone実機にInstall & Run、
- アプリが終了（orクラッシュ）したらメインループに戻る。

Shared Folderをシェルから読み書き可能に
----
https://github.com/vmware/open-vm-tools/issues/199

     mount -t vmhgfs .host:/ <path>

Shared Folder内はそのままだとネットワーク上のフォルダと見做されてターミナルからRead/Writeできないので、このコマンドで適当な場所にマウントしておきます。これでWindows側のファイルの操作ができるようになります。ただしOS終了時にアンマウントされるので、OS起動と同時に実行しておきます。

Unityのビルドスクリプトでタイムスタンプを更新
----
ビルドのたびにタイムスタンプファイルをShared Folderに作成します。Unityにはビルド完了時に関数を呼べる仕組みがあるので、それを使ってファイル操作します。
https://docs.unity3d.com/ScriptReference/Callbacks.PostProcessBuildAttribute.html

プロジェクトをShared Folderからcp
----

上記のmountコマンドでShared Folder内のファイルの読み書きは出来るようになるんですが、XCodeでのビルドは通りません（ややこしい）。なので、変更のたびにまずShared Folderをデスクトップに丸々cpして、そこでビルドすることにします。

ビルド
---

    xcodebuild -project <project_path> -target <target_name> -configuration Debug build

具体的には`-project Unity-iPhone.xcodeproj -target Unity-iPhone`になると思います。成果物はプロジェクトフォルダ直下の`build`に入ります。GUIでビルドするときとは宛先が異なるので、macOS環境での作業に慣れてる人は注意。
https://stackoverflow.com/questions/27291398/xcodebuild-specifying-target-and-linking-a-framework

デプロイ
----

    ios-deploy --noninteractive --uninstall --debug --bundle <app_path>

- `--noninteractive` - アプリ終了時にメインループに戻す用
- `--uninstall` - とりあえず古いバージョンを消してから新しいのを入れる

具体的には`--bundle プロジェクトフォルダ/build/debug-iphoneos/foobar.app`。機器IDは勝手に選択してくれます。ランタイムのログはターミナルに直接出てきます。


不具合・改善案
===

- このVMはメモリが全部ページングファイルに載るので、ハードディスクの使用率が常に90%ぐらい行きます。ホストOSと同じディスクに載せるのは危なっかしいので注意。
- VMがスリープ状態だともちろん動きません。ビルドのたびに起きてるか確認する必要があります。たまに忘れます。とはいえ、スリープを禁止すると今度はディスク使用率が痛い。。
- タイムラグがよく気になります。普通のmacOS環境でデプロイすると2分ぐらいですからね。

VMWareがRAMを使ってくれれば全部解消するんですが、実装されてないっぽいですね。方法があったらぜひ教えてください。

まとめ
===
WindowsのUnityからボタン一発でiPhoneにデプロイする方法でした。Macマシンが手元にない人にとっては便利なんじゃないでしょうか。でもちゃんとしたパソコンを買うお金が貯まり次第、ぜひそうすることをおすすめします。

以上