Unityでアプリを作る時にネイティブの機能を使いたい場合に、どうやったらいいのか調べた。Objective-Cでプラグインを作って、C#から呼び出してやることでできる。

Android編はこちら

C# からObjective-Cコードの呼び出し

  • C#側で[DllImport(__Internal)]アノテーションでstatic externすると、普通のメソッドのように呼び出せる:
// HogeClass.cs
using System.Runtime.InteropServices;

public class HogeClass {
  [DllImport("__Internal")]
  private static extern void hogeNative();

  public void Hoge() {
    hogeNative();
  }
}
  • Unityプロジェクト内のAssets/Plugins/iOS以下にObjective-Cのソース(.m または .mm)を配置し、Cリンケージで関数を定義する
// hoge.mm
extern "C" {
void hogeNative() {
  // Do something.
  NSLog(@"hogeNative called");
}
}
  • BuildするとObjective-Cのソースがリンクされ、C#から呼び出すことができる
  • 呼び出されるネイティブコードはObjective-CというかCです
  • スタティックライブラリ(.a)を作って組み込んでもよい
  • 受け渡せる引数や戻り値の型は、intやstringなど

Objective-CからC#にコールバックを呼び出す

ネイティブ側でイベント駆動や非同期処理など行わせる場合、なにか起こった時にObjective-CからC#のメソッドを呼び出してもらいたい(コールバック)。そのためにC#のメソッドをObjective-Cに渡して、呼び出せるような機構が必要。

  • C#のdelegateでコールバックの関数の型を宣言し、その呼び出される関数をAOT.MonoPInvokeCallbackAttribute とやらでアノテートしたメソッドとして定義し、ネイティブに渡す:
// HogeClass.cs
using System.Runtime.InteropServices;
using UnityEngine;

public class HogeClass {
  private delegate void HogeCallbackDelegate();

  [DllImport("__Internal")]
  private static extern int hogeNative(HogeCallbackDelegate callback);

  public void Hoge() {
    hogeNative(HogeCallback);
  }

  [AOT.MonoPInvokeCallbackAttribute(typeof(HogeCallbackDelegate))]
  static void HogeCallback() {
    Debug.Log("HogeCallback called");
  }
}
  • C(Objective-C)側でも同じ型のコールバック関数をtypedefなどで定義してやって、それを引数とする関数を作ってやる。呼び出しもそのまま行えばOK:
// hoge.mm
extern "C" {
typedef void (*HogeCallbackDelegate)();
void hogeNative(HogeCallbackDelegate callback) {
  NSLog(@"hogeNative called");
  callback();
}
}
  • Unity5.5.2p1で試したところ、Scripting BackendがMono2xだと(IL2CPPじゃないと)ビルドに失敗する
  • コールバックに渡せるのはstaticメソッドだけなのでインスタンスを受け渡したいんだけど、手段がまだわかってない…(MarshalDirectiveExceptionが発生する)

参考