LoginSignup
4
5

More than 5 years have passed since last update.

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

Posted at

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

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

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の機能を使うに当たって必要なファイルを1つのフォルダーに纏める

スクリーンショット 2017-12-22 19.04.15.png

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

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

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

スクリーンショット 2017-12-22 19.23.17.png

スクリーンショット 2017-12-22 19.26.12.png

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

スクリーンショット 2017-12-22 19.36.40.png

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

スクリーンショット 2017-12-22 19.33.49.png

スクリーンショット 2017-12-22 19.43.09.png

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

TARGETS → Build Phases のLink Binary With Libraries

スクリーンショット 2017-12-22 19.49.18.png

スクリーンショット 2017-12-22 19.52.19.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

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

STEP3-5で生成した.aファイルを追加するためにVisualStudioにてバインディングライブラリプロジェクトを生成する。
スクリーンショット 2018-01-09 10.48.57.png

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

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

スクリーンショット 2018-01-09 11.02.23.png

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

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

  • CPUのアーキテクチャーの指定
LinkTarget.ArmV7 | LinkTarget.ArmV7s | LinkTarget.Simulator,

の3つを指定すると良いとのこと。

  • 必要なフレームワークを記述する。 (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」を使って2つのファイルを生成する。

(公式ページ)
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のエラー集

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

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

@Jinshichiさんの記事によると --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);

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

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

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

### 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

STEP4-6: dllを生成する

dll生成はいたって簡単で、バインディングライブラリプロジェクトをビルドすれば、
プロジェクト内のbinフォルダーの (Debug or release)内にプロジェクト名.dllが生成される。
スクリーンショット 2018-01-09 17.16.06.png

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

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

    • 参照を右クリックし参照の編集を開き.NETアセンブリタグを開く。
    • 生成した<プロジェクト名>.dllにチェックを入れる

スクリーンショット_2018-01-09_17_55_05.png

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

heatmap.png

バインディングする際に参考にさせていただいたサイト
mattsuDev’s blog - XamarinでNativeライブラリを使用する (iOS編)
Xamarin.iOS でバインディングライブラリを作る
Binding Objective-C
Xamarin Objective Sharpie ApiDefinitionsのエラー集
Xamarin.iOSにObjective-Cライブラリをバインディングする際にハマったこと

4
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
5