Unity2018.2 + il2cppでgRPCアプリをAndroid/iOS向けにビルドする

# はじめに

「[UnityでgRPCアプリ(iOS, Android)をビルドできるようになった](https://qiita.com/muroon/items/2115c2c72be8b0c3f5f6)」でUnityのiOS, AndroidでもgRPCアプリを動かせるようになりました。
ここから更にil2cppでビルドする際に起きる問題の対処をまとめました。

# 共通

`*.proto`で`oneof`か`enum`を使っている場合は`System.Reflection.Emit`が走って実行時エラーになってしまいます。
`protoc`で生成したコード中の`enum`すべてを以下の`T`に指定すると回避できます。
`oneof`で`enum`を使った場合はそのフィールドを含むクラスもこの対策が必要みたいです。RPC周りの例外メッセージに`System.Reflection.Emit`が出てきたら対象のクラスを指定すれば大丈夫だと思います。

一度だけ実行すればいいので静的コンストラクタなどに書いておくといいでしょう。

```csharp
using Google.Protobuf.Reflection;

FileDescriptor.ForceReflectionInitialization<T>();
```

# Android向け

[C#: Fix il2cpp build on Unity Android. #18039](https://github.com/grpc/grpc/pull/18039)でダミー関数がマージされたのでコピー不要になりました。

~~DLLImportの名前が合わずにリンク時にビルドエラーが発生してしまいます。
Unity2018.2からPluginsフォルダにcppを配置するとビルドされるようになったので、以下のダミー関数のソースを配置しInclude Platformsから`iOS`と`Editor`を除外します。~~

~~[/Assets/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_stub.c](https://github.com/jsmouret/grpc-unity-package/blob/master/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_stub.c)
[/Assets/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_stub.c.meta](https://github.com/jsmouret/grpc-unity-package/blob/master/Plugins/Grpc.Core/runtimes/grpc_csharp_ext_stub.c.meta)~~

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8995/e927f21e-a051-5267-5cb6-df4ab352ef9d.png)

ネットワークアクセスがgRPCのみだと`AndroidManifest.xml`に`INTERNET`パーミッションが付与されません。
確実に有効にするために`PlayerSettings > Other Settings > Internet Access`を`Require`にします。


![image.png](https://qiita-image-store.s3.amazonaws.com/0/8995/c1b98a0d-39b0-41b1-c7fe-463d367205df.png)

# iOS向け

iOS向けのライブラリのアーキテクチャはx86_64とarm64だけなので`PlayerSettings > Other Settings > Architecture`を`ARM64`にします。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/8995/368bf343-52ce-132f-ac80-5046adf9cfc6.png)

また、bitcode非対応なのでXCodeの`Build Settings`で`ENABLE_BITCODE`を`NO`にします。
~~[Enable bitcode for ios native libraries #20113](https://github.com/grpc/grpc/pull/20113)でbitcodeに対応するビルドオプションが追加されたので対処不要になりました。~~
bitcode対応すると`libgrpc.a`が250MB以上増えたので[v1.24.x](https://github.com/grpc/grpc/tree/v1.24.x)ブランチは[Revert "Enable bitcode for ios native libraries" in v1.24.x #20233](https://github.com/grpc/grpc/pull/20233)で、masterブランチは[a2dbf0a99d1fc1c0059dbb28c4b530a58584cd71](https://github.com/grpc/grpc/commit/a2dbf0a99d1fc1c0059dbb28c4b530a58584cd71)で差し戻されてしまいました。

リンク対象のライブラリに`libz`が必要なので`Build Phases > Link Binary With Libraries`から追加します。

この操作は以下のエディタ拡張を配置すればビルド時に自動的に反映できます。

~~参考にした[BuildHelper.cs](https://github.com/jsmouret/grpc-unity-package/blob/master/example/UnityGrpcClient/Assets/Scripts/Editor/BuildHelper.cs)は`libresolv.tbd`を追加していますがビルドエラーにならなかったので今も必要なのかよく分かりません…~~
参考にした先と公式ではビルドオプションが異なり、公式のライブラリを使うなら`libresolv.tbd`は不要でした。

```csharp:/Assets/Editor/BuildHelper.cs
#if UNITY_IPHONE

using System.IO;
using UnityEngine;
using UnityEditor;
using UnityEditor.Callbacks;
using UnityEditor.iOS.Xcode;

public class BuildHelper
{
    // Add libz.tbd and disable bitcode when building for iOS
    // Thanks to https://github.com/jsmouret/grpc-unity-package/blob/master/example/UnityGrpcClient/Assets/Scripts/Editor/BuildHelper.cs

    [PostProcessBuildAttribute(1)]
    public static void OnPostProcessBuild(BuildTarget target, string path)
    {
        if (target == BuildTarget.iOS)
        {
            var projectPath = PBXProject.GetPBXProjectPath(path);
            var project = new PBXProject();
            project.ReadFromString(File.ReadAllText(projectPath));
            var targetGUID = project.TargetGuidByName(PBXProject.GetUnityTargetName());

            project.AddFrameworkToProject(targetGUID, "libz.tbd", false);

            project.SetBuildProperty(targetGUID, "ENABLE_BITCODE", "NO");

            File.WriteAllText(projectPath, project.WriteToString());
        }
    }
}

#endif
```

# 最後に

2018/10/15現在、このような対策が必要ですが今後不要になったり対策が変わるかもしれないので[grpc/grpc](https://github.com/grpc/grpc)や[protocolbuffers/protobuf](https://github.com/protocolbuffers/protobuf)のissuesなどで動向を追ってください。

※追記
Experimentalパッケージが出る前のバージョンは「[UnityプロジェクトでのgRPC導入方法について](https://blog.applibot.co.jp/2018/10/31/grpc-in-unity/)」の手順でビルドすれば利用できるようです。

# 参考リンク

* [UnityでgRPCアプリ(iOS, Android)をビルドできるようになった](https://qiita.com/muroon/items/2115c2c72be8b0c3f5f6)
* [Unity(IL2CPP環境)でのprotobufの使い方](http://sassembla.github.io/Public/2018:08:05%201-07-27/2018:08:05%201-07-27.html)
* [UnityプロジェクトでのgRPC導入方法について](https://blog.applibot.co.jp/2018/10/31/grpc-in-unity/)
* [jsmouret/grpc-unity-package](https://github.com/jsmouret/grpc-unity-package)
* [protocolbuffers/protobuf - Fix to allow AOT compilers to play nicely with reflection #4559](https://github.com/protocolbuffers/protobuf/pull/4559)
* [grpc/grpc - Unity3D package #15013](https://github.com/grpc/grpc/issues/15013#issuecomment-417714017)
* [grpc/grpc - Compile error on Unity Android (when using il2cpp scripting backend) #16012](https://github.com/grpc/grpc/issues/16012#issuecomment-404995041)
* [grpc/grpc - C#: Fix il2cpp build on Unity Android. #18039](https://github.com/grpc/grpc/pull/18039)
* [grpc/grpc - Xcode reports libgrpc_csharp_ext.a does not contain bitcode #16839](https://github.com/grpc/grpc/issues/16839)
* [grpc/grpc - Enable bitcode for ios native libraries #20113](https://github.com/grpc/grpc/pull/20113)
* [grpc/grpc - Revert "Enable bitcode for ios native libraries" in v1.24.x #20233](https://github.com/grpc/grpc/pull/20233)