2016年4月27日水曜日

Xamarin.FormsでiOSのネイティブコードを使用して苦労した件について

Xamarin.FormsでiOSの自作ネイティブライブラリを使用しようとしてハマった話。
普通にiOSでやれば簡単だったけど、Cross-platformにしようとしたら面倒だったという話。

環境


  • mac book pro/El Capitan
  • Xamarin 5.10.3

手順


  1. 新規ソリューションの作成でCross-platform / Xamarin.Forms Appを作る
  2. ネイティブバインディング用のプロジェクトとして、iOS / Bindings Libraryを作る
  3. Sharpieなどを使ってバインディングコードを書く
    ここあたりにやり方が描かれている。
  4. 1で作られたiOS用のプロジェクトに2のネイティブバインディングのアセンブリを参照設定で追加しようとする。
    → プロジェクト参照では、Xamarin.iOSとmonotouchで互換性がないのでNGと怒られるので、「.Net Assembly」タブの「ブラウズ」で無理くり参照設定する。
  5. ビルド
    → エラーになってビルドが通らなくなる。

対応

結局、csprojファイルを直接書き換えることで対応した。
いじる対象のcsprojファイルはバインディング側のプロジェクトファイルだ

/Project/PropertyGroup/ProjectTypeGuids要素を修正する。


    <ProjectTypeGuids>{8FFB629D-F513-41CE-95D2-7ECE97B6EEEC};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

となっている箇所を

    <ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

に変更する。
この要素は、プロジェクトのタイプを示していて、<ProjectTypeGuids>[作ったプロジェクトの種類(例えばiOSのアプリとかAndroidのアプリとか)を示す。]:[C#ということを示す。]</ProjectTypeGuids> という意味のようだ。
もともとのプロジェクトの種類が「Xamarin.iOS」だったところを、「Monotouch」へ変更する。なぜならXamarin.FormsのiOS側のプロジェクトのタイプが「Monotouch」で「Xamarin.iOS」と互換性がないからだ。

またこれに伴って、Xamarin.iOSの参照設定が使えなくなったので、参照設定で「Xamarin.iOS」を削除して、代わりに「monotouch」を追加する。
こうすることで、ネイティブバインディングプロジェクトのビルド自体は通るようになる。

次に、参照設定する側も同様にビルドをすると、エラーが発生しって相変わらずうまくいかない。
  • ネイティブバインディングから.aファイルが取り出せない。
というエラーになる。
実は本来であれば、コンパイルされたネイティブコードの.aファイルはリソースとして丸々入るのだが、今回の修正で、.aファイルはリソースに入らないという問題が発生し、そのため、参照先で.aファイルが取り出せないエラーが発生するようである。

仕方がないので、無理やりリソースに入れることとする。やり方は以下の通りだ。
  • ソリューションウインドウの、埋め込みする.aファイルで右クリックする。
  • 表示されたコンテキストメニューからビルドアクション > EmbeddedResourceを選択する。
これによって、.aファイルがリソースに含まれるようになる。

再度、参照設定する側でリビルドを行うが、今度は「MT5210」と言ったエラーが発生する。これは、ここによるとリンクしたライブラリのシンボル解決ができないということのようだ。
調べた結果、.aファイルのリソースの埋め込み名称と.aファイルをプロジェクトにドロップするときに自動生成される「〜.linkwith.cs」の中の参照先名の記述を変えてやれば良いことがわかった。

例えば、Hoge.aというネイティブライブラリをプロジェクトにドロップすると、linkwith.csでは

 [assembly: LinkWith("Hoge.a", SmartLink = true, ForceLoad = true)]

という記述が自動生成されるが、上記の「Hoge.a」という記述が生成されるアセンブリのリソース名になっていないと、参照設定する側で前述のリンクエラーが発生してしまう。
デフォルトだと、プロジェクトの名前空間名が修飾されるので、例えば「namaekuukan」というデフォルトネームスペースだったら、

 [assembly: LinkWith("namaekuukan.Hoge.a", SmartLink = true, ForceLoad = true)]

に変更する必要がある。
実際にどのようなリソース名で埋め込まれているかを調べるにはILSpyなどを使うと捗る。
この状態でソリューションビルドすることで(念のためクリーンアップしたほうが良い)、ビルドエラーが発生せずに、参照設定する側からネイティブライブラリを呼び出すことができるようになる。

なお、細かいところでは、
  • 「monotouch」に参照設定を変更したらusingしている箇所も「Monotouch.」と変更が必要になる
  • [assembly: LinkWithにリンカーフラグをつけたりする必要があったり
    →自分のところでは、「LinkerFlags = "-lstdc++"」を追記しました。
と言ったことが起きるので注意されたい。

0 件のコメント: