Xamarin.iOS Obj-Cライブラリをバインディングする

バインディングライブラリを作るときの備忘録的なもの

##バインディングライブラリとは

Object-cやSwift・javaで書かれているネイティブのライブラリをXamarinでも使えるようにしたライブラリのこと

今回は、Xamarin.iOSでGoogleMapUtilにある「Heatmap」の機能を使いたかったので
「google-maps-ios-utilsリポジトリ」
https://github.com/googlemaps/google-maps-ios-utils/tree/master/src/Heatmap

上記のリポジトのをバインディングしていく。

##STEP1: gitからクローンしてくる

```
git clone git@github.com:googlemaps/google-maps-ios-utils.git
```

##STEP2: 必要な.hファイルと.mファイルを纏める

HeatMapの機能を使うに当たって必要なファイルを１つのフォルダーに纏める

<img width="1248" alt="スクリーンショット 2017-12-22 19.04.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/84002/64f63db9-f00b-2a06-2f99-37f7d2ab6322.png">

※OSで用意されている奴はこのあとのSTEPでリンクするので含めなくて良いです。

##STEP3: Xcodeプロジェクトでスタティックライブラリを生成する(.aファイル)

###STEP3-1: Xcodeでスタティックライブラリプロジェクトを新規で作成する

<img width="1395" alt="スクリーンショット 2017-12-22 19.23.17.png" src="https://qiita-image-store.s3.amazonaws.com/0/84002/b9268f08-96f8-79a4-687e-a0fce95c1615.png">

<img width="1395" alt="スクリーンショット 2017-12-22 19.26.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/84002/4f05ddff-1bfb-4472-a394-a11f658c8dcb.png">

###STEP3-2: 抽出したファイルをプロジェクトに追加する

<img width="949" alt="スクリーンショット 2017-12-22 19.36.40.png" src="https://qiita-image-store.s3.amazonaws.com/0/84002/971ffdb2-16c9-1c8d-fcef-4b112aeec9a5.png">

フォルダを右クリックしてAdd Files to ＜プロジェクト名＞を選択する

<img width="602" alt="スクリーンショット 2017-12-22 19.33.49.png" src="https://qiita-image-store.s3.amazonaws.com/0/84002/0ff818d2-c594-fcc1-e185-2b4ae035e339.png">

![スクリーンショット 2017-12-22 19.43.09.png](https://qiita-image-store.s3.amazonaws.com/0/84002/a97b42d0-3de6-ebd6-4357-4ef8ce7632da.png)

###STEP3-3: 必要なライブラリをリンクさせる


TARGETS → Build Phases　のLink Binary With Libraries

![スクリーンショット 2017-12-22 19.49.18.png](https://qiita-image-store.s3.amazonaws.com/0/84002/a2d72f0b-2cb3-b9bf-9e96-2d992e9c39a9.png)

![スクリーンショット 2017-12-22 19.52.19.png](https://qiita-image-store.s3.amazonaws.com/0/84002/d85ec1b6-bf5b-8a31-6ad2-3c37e73091f1.png)

各.hファイルと.mファイルのimportされているものを確認しリンクさせる

###STEP3-4: .aファイルを生成する

.aファイルを生成するためのMakeFileを作る

```
XBUILD=/Applications/Xcode.app/Contents/Developer/usr/bin/xcodebuild
PROJECT_ROOT=./<YOUR PROJECTNAME>
PROJECT=./<YOUR PROJECTNAME>.xcodeproj
TARGET=<YOUR PROJECTNAME>

all: lib$(TARGET).a

lib$(TARGET)-i386.a:
		$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphonesimulator -configuration Release clean build
		-mv ./build/Release-iphonesimulator/lib$(TARGET).a $@

lib$(TARGET)-armv7.a:
		$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch armv7 -configuration Release clean build
		-mv ./build/Release-iphoneos/lib$(TARGET).a $@

lib$(TARGET)-arm64.a:
		$(XBUILD) -project $(PROJECT) -target $(TARGET) -sdk iphoneos -arch arm64 -configuration Release clean build
		-mv ./build/Release-iphoneos/lib$(TARGET).a $@

lib$(TARGET).a: lib$(TARGET)-i386.a lib$(TARGET)-armv7.a lib$(TARGET)-arm64.a
		xcrun -sdk iphoneos lipo -create -output $@ $^

clean:
		-rm -f *.a *.dll
```

次にMakefileのあるディレクトリでターミナル上からmakeコマンドを実行する

実行に成功すると以下のようにファイルが生成される。

![スクリーンショット 2018-01-09 10.35.52.png](https://qiita-image-store.s3.amazonaws.com/0/84002/2a74d216-52b6-deca-bdee-e1952f58ac34.png)



##STEP4 バインディングライブラリプロジェクトを作る

STEP3-5で生成した.aファイルを追加するためにVisualStudioにてバインディングライブラリプロジェクトを生成する。
![スクリーンショット 2018-01-09 10.48.57.png](https://qiita-image-store.s3.amazonaws.com/0/84002/30d0d62f-1de0-d1c5-4841-f5e8909f6e58.png)

###STEP4-1: .aファイルをプロジェクトに追加する

生成した.aファイルをプロジェクトに追加する。 ←(arm64とか付いていないプロジェクト名だけの.aファイル)

![スクリーンショット 2018-01-09 11.02.23.png](https://qiita-image-store.s3.amazonaws.com/0/84002/54a14bd5-a01b-eab0-a96a-be24fd2466d5.png)

###STEP4-2: .aファイルの中のlinkwith.csファイルを編集する

linkwith.csにはライブラリに含まれるCPUアーキテクチャ・依存するフレームワークを記述し、
指定することができる。

*  CPUのアーキテクチャーの指定

```
LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator,
```
の３つを指定すると良いとのこと。

*  必要なフレームワークを記述する。 (Githubのreadmeやheaderファイルを確認し必要なフレームワークを確認する)

```
Frameworks = "GoogleMapsBase, GoogleMaps, GoogleMapsCore, Security,UiKit, CoreLocation, Foundation"
```

*  ライブラリのリンクを記述する。

.dylibで終わるライブラリはここで指定する

```
LinkerFlags = "-lz",
```

* IsCxxについて

これはC++を含むライブラリがある時だけtrueにすれば良い。


```　linkwith.cs
using ObjCRuntime;

[assembly: LinkWith("libGoogleMapiOSHeatmapUtility.a",
                     LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator,
                     SmartLink = true,
                     ForceLoad = true,
                     Frameworks = " GoogleMapsBase GoogleMaps GoogleMapsCore Security, UiKit, CoreLocation, Foundation",
                     LinkerFlags = "-lz",
                     IsCxx = true)]

```

###STEP4-3: API定義ファイルとAPIに必要な構造体と列挙体のファイルを作成する

Xamarinの公式で出ているバインディングツール、「Objective Sharpie」を使って２つのファイルを生成する。

(公式ページ)
https://developer.xamarin.com/guides/cross-platform/macios/binding/objective-sharpie/

STEP2で必要な.hファイルが入っているフォルダーを作ってあると思うので(作っていなかったら作ってください。)そのフォルダーのディレクトリーにターミナルで移動する。

移動したら、次のコマンドをターミナルで実行する。

```
sharpie bind --output=GoogleMapiOSHeatmapUtility --namespace=GoogleMapiOSHeatmapUtility --sdk=iphoneos11.1 --verbose  ./GoogleMapiOSHeatmapUtility/*.h
```

実行後、ApiDefinitions.csとStructsAndEnums.csが生成される。

中身を開いてみると何万行にもなる定義が書かれていているので、気合いで必要な部分だけ残し
不要なものを削る。

※[Verify]みたいなのがたくさん出てきてエラーになりますがこれは削除すれば問題ないそうで。
↓
@tanaさんの記事を見ながらやると良さそう [Xamarin Objective Sharpie ApiDefinitionsのエラー集](https://qiita.com/tana_/items/9434eda5f26295d36c26)

※ StackOverflowであってないような英語スキルで質問を投げてみたら、
新しいバージョンになってから起こったことらしく、
「必要なものは最後らへんにあるのでそこだけ残してあげればいいよ。 あと cocoapodsで配布されているのであれば cocoapodsを使用して作った方が楽だよ」的な答が返ってきた。

自分が質問したStackOverflowのページ
https://stackoverflow.com/questions/47710739/xamarin-object-sharpie-obj-c-library-binding-why-output-huge-file/47747957#47747957

※[@Jinshichiさんの記事](https://qiita.com/Jinshichi/items/e27b04f7ee27555b2f33)によると --scope　オプションをつけると巨大ファイルが生成されないらしいが自分はまだ試していない

今回の場合、GoogleMaps SDK for iOS(Xamarin)に含まれているメソッドや構造体・列挙体のもあるので、それも削ってあげる。

###STEP4-4: GoogleMaps SDK for iOS(Xamarin)に型などを合わせる

当たり前かもしれないが、本体の方で使われている型に合わせないと使い物にならないので、
ApiDefinitions.csとStructsAndEnums.csの中身を適切に変更してあげる。

(例)
GoogleMaps SDK for iOS(Xamarin)側

```
[Register ("GMSPath", true)]
	public class Path : NSObject, INSCopying, INativeObject, IDisposable, INSMutableCopying
	{
		//
		// Static Fields
		//
		[CompilerGenerated]
		private static readonly IntPtr class_ptr;

		//
		// Static Properties
		//
		[CompilerGenerated]
		public static Path GetPath {
			[Export ("path")]
			get;
		}
```

バインドしてきたUtilityLibrary側(一部)

```
// extern BOOL GMSGeometryIsLocationOnPathTolerance (CLLocationCoordinate2D point, GMSPath * _Nonnull path, BOOL geodesic, CLLocationDistance tolerance);
		[DllImport ("__Internal")]
		[Verify (PlatformInvoke)]
		static extern bool GMSGeometryIsLocationOnPathTolerance (CLLocationCoordinate2D point, GMSPath path, bool geodesic, double tolerance);

```
本体側では　 `GMSPath` → `Path`となっているので
GMSPathになっている箇所を直してあげる。 
他にも違う型で同じところがあったら同様に直す。


###STEP4-5: (謎)Enumがあるとコンパイルエラーになる

ApiDefintionsの方にenumが出力されるがそのままビルドすると　Error CS0101 The namespace '' already contains a definition for~~~~　と、すでに定義されてるよ？というエラーが出るが同じenumを定義してもいないのに発生する。

https://bugzilla.xamarin.com/show_bug.cgi?format=multiple&id=35231

```
### Alternate workaround (that works on both Mac and Windows)

Move the `StructsAndEnums.cs` file to a separate small iOS class library project, set the build action of the file to "Compile", and then reference that class library project from the binding project.
```

対処法としては別プロジェクトに分けてやる

![スクリーンショット 2018-01-09 17.06.22.png](https://qiita-image-store.s3.amazonaws.com/0/84002/ca9c4536-a924-52f9-6839-9a7a4267d841.png)

###STEP4-6: dllを生成する

dll生成はいたって簡単で、バインディングライブラリプロジェクトをビルドすれば、
プロジェクト内のbinフォルダーの (Debug or release)内にプロジェクト名.dllが生成される。
![スクリーンショット 2018-01-09 17.16.06.png](https://qiita-image-store.s3.amazonaws.com/0/84002/594c85de-8620-ad5c-5177-47ea6c66ff30.png)

##STEP5: 実際にアプリに組み込んでみる

* iOSの参照に生成したdllを追加する。

  * 参照を右クリックし参照の編集を開き.NETアセンブリタグを開く。

  * 生成した<プロジェクト名>.dllにチェックを入れる

![スクリーンショット_2018-01-09_17_55_05.png](https://qiita-image-store.s3.amazonaws.com/0/84002/0847a9e1-6d82-42b6-3e79-d960cd992fdf.png)

あとは、Googleの公式のリファレンスをC#風に置き換えればOK
(公式リファレンス)　尚、日本語版は更新されていない模様)
https://developers.google.com/maps/documentation/ios-sdk/utility/heatmap


![heatmap.png](https://qiita-image-store.s3.amazonaws.com/0/84002/0473fcfe-a4fb-25a2-65fd-c324bb166b33.png)


バインディングする際に参考にさせていただいたサイト
[mattsuDev’s blog - XamarinでNativeライブラリを使用する (iOS編)](http://mattsudev.hatenablog.com/entry/2015/09/26/140222 ) 
[Xamarin.iOS でバインディングライブラリを作る](https://qiita.com/iseebi/items/36c4fe2bd0c996163db5)
[Binding Objective-C](https://developer.xamarin.com/guides/cross-platform/macios/binding/)
[Xamarin Objective Sharpie ApiDefinitionsのエラー集](https://qiita.com/tana_/items/9434eda5f26295d36c26)
[Xamarin.iOSにObjective-Cライブラリをバインディングする際にハマったこと](https://qiita.com/Jinshichi/items/e27b04f7ee27555b2f33)