ネイティブ ライブラリ相互運用の作業を開始する

この記事では、セットアップを簡略化するために Maui.NativeLibraryInterop を使用してネイティブ ライブラリ相互運用の作業を開始する方法について説明します。

これらの手順では、ネイティブ ライブラリ相互運用を使用してバインドを作成するための基本的な手順、重要な決定ポイント、ガイドとなる例について概説します。 特定の API と実装の詳細に関する詳しいガイダンスについては、対象となるネイティブ SDK とライブラリのドキュメントを参照してください。

前提条件

必須コンポーネントのインストール:

Android SDK または Xcode コマンド ライン ツールはスタンドアロン方式でインストールできます。 ただし、Xcode コマンド ライン ツールのインストールは、通常 Xcode を使用して行います。 同様に、Android SDK のインストールも通常は、Android Studio または .NET MAUI VS Code 拡張機能を使用して、.NET MAUI の概要のドキュメントに従って行います。

新しいバインドを作成する

新しいバインドの作成を開始する最も簡単な方法は、Maui.NativeLibraryInterop リポジトリでテンプレートを複製し、そこから変更を行うことです。 Maui.NativeLibraryInterop が現在どのように設定されているかについて全体をより詳しく理解するには、概要のドキュメントで詳細をお読みください。

.NET バインド ライブラリを設定する

テンプレートには、Android 用のスターター .NET と iOS バインド ライブラリ用の .NET が含まれています。

お使いの .NET アプリで必要に応じて、ターゲット プラットフォームと .NET バージョンを反映するようにバインド ライブラリを更新します。

例: .NET 9 を使用して iOS バインドのみを作成しようとしている場合は、以下を行うことができます。

  1. template/android/NewBinding.Android.Binding で Android バインド ライブラリを削除します。さらに
  2. template/macios/NewBinding.MaciOS.Binding/NewBinding.MaciOS.Binding.csproj で、ターゲット フレームワークを net9.0-ios に設定されるように更新します。

ネイティブ ラッパー プロジェクトとライブラリを設定する

テンプレートには、スターター Android Studio プロジェクトと Xcode プロジェクトも含まれています。

お使いの .NET アプリで必要に応じてターゲット プラットフォームとバージョンを反映するようにネイティブ プロジェクトを更新し、次の手順で目的のネイティブ ライブラリを含めます。

セットアップ: iOS と Mac Catalyst

Xcode プロジェクトは template/macios/native/NewBinding にあります。

お使いの .NET アプリでサポートされているターゲット プラットフォームとバージョンを反映するように、Xcode プロジェクトを更新します。 Xcode プロジェクトで、最上位のフレームワークをクリックし、[ターゲット] > [全般] で次のようにします。

  1. 必要に応じて、[Support Destinations] (サポート対象) を追加または削除します。
  2. 必要に応じて iOS バージョンを調整します。

iOS または MacCatalyst のネイティブ ライブラリを、ご自分のライブラリとニーズに最適な方法 (CocoaPods、Swift パッケージ マネージャー など) で Xcode プロジェクトに取り込みます。

セットアップ: Android

Android Studio プロジェクトは template/android/native にあります。

お使いの .NET アプリでサポートされているターゲット バージョンを反映するように、Android Studio プロジェクトを更新します。

  1. build.gradle.kts (:app) ファイルに移動します
  2. 必要に応じて compileSdk のバージョンを更新します

Gradle を使用して Android ネイティブ ライブラリを取り込みます

  1. build.gradle.kts (:app) ファイルの依存関係ブロックにパッケージの依存関係を追加します。
  2. settings.gradle.kts ファイルの dependencyResolutionManagementrepositories ブロックにこのリポジトリを追加します。
  3. プロジェクトを gradle ファイルと同期します (Android Studio の右上隅にあるボタンを使用)。

API インターフェイスを作成する

次の手順で、ネイティブ プロジェクトと .NET バインド プロジェクトの間に API インターフェイスを作成します。

API 定義: iOS と Mac Catalyst

ネイティブ側で、template/macios/native/NewBinding/NewBinding/DotnetNewBinding.swift に更新を行います。

  1. import ステートメントを追加して、先ほど追加したネイティブ ライブラリをインポートします。
  2. 目的のネイティブ ライブラリ API の API 定義を記述します。
  3. Xcode プロジェクトが正常にビルドされ、API が満足できるものであることを確認します。

.NET 側に戻ると、ネイティブ ライブラリと相互運用する準備ができています。

  1. template/macios/NewBinding.MaciOS.Binding から dotnet build を実行し、すべてが正しく組み込まれ、問題なく使用できる状態であることを確認します。
  2. Objective Sharpie を使用して、Swift API 更新用の C# バインディングを生成します。
    1. template/macios/NewBinding.MaciOS.Binding/bin/Debug/net9.0-ios/NewBinding.MaciOS.Binding.resources/NewBindingiOS.xcframework/ios-arm64/NewBinding.framework (MaciOS バインド プロジェクトの出力フォルダー内) に移動します。
    2. sharpie xcode -sdks を実行して、bind コマンドの有効なターゲット SDK 値の一覧を取得します。 次のコマンドで使用する対象のプラットフォームとバージョンに合わせて値を選択します (例: iphoneos18.0)。
    3. バインド プロジェクトによって作成された xcframework 内のヘッダー ファイルに対して sharpie bind を実行します。
      sharpie bind --output=sharpie-out --namespace=NewBindingMaciOS --sdk=iphoneos18.0 --scope=Headers Headers/NewBinding-Swift.h
      
    4. テンプレート/macios/NewBinding.MaciOS.Binding/ApiDefinition.cs の内容を template/macios/NewBinding.MaciOS.Binding/bin/Debug/net9.0-ios/NewBinding.MaciOS.Binding.resources/NewBindingiOS.xcframework/ios-arm64/NewBinding.framework/sharpie-out/ApiDefinitions.cs に置き換えて更新し、必要に応じて(例: 名前の付け方)調整します。
    5. template/macios/NewBinding.MaciOS.Binding から dotnet build をもう一度実行します。

このツールの詳細については、objective-sharpie のドキュメントも参照してください。

API 定義: Android

ネイティブ側では、template/android/native/newbinding/src/main/java/com/example/newbinding/DotnetNewBinding.java を更新します。

  1. import ステートメントを追加して、先ほど追加したネイティブ ライブラリをインポートします。
  2. 目的のネイティブ ライブラリ API の API 定義を記述します。
  3. Android Studio プロジェクトが正常にビルドされ、API が満足できるものであることを確認します。

.NET 側に戻ると、ネイティブ ライブラリと相互運用する準備ができています。

  1. template/android/NewBinding.Android.Binding から dotnet build を実行し、すべてが正しく接続され、正常に動作する準備ができていることを確認します。 (注: この手順では、JDK 17 がインストールされている必要があります)
  2. ネイティブ Android プロジェクトでバインドされている各 maven 依存関係に対して、@(AndroidMavenLibrary) 項目を template/sample/MauiSample.csproj に追加して、Android バインディング依存関係を参照します。 これにより、プロジェクトの Java 依存関係の検証が有効になり、後続のビルドで依存関係が不足している場合にビルド警告またはエラーが生成されます。 これらの警告/エラーに対処するには、バインドするネイティブ ライブラリの Java 依存関係チェーンを満たすために提案されている @(AndroidMavenLibrary) または @(PackageReference) 要素を追加します。 (注: gradle/maven の依存関係は、ライブラリに自動的にバンドルされないため、多くの場合、明示的に参照する必要があります。)
<ItemGroup Condition="$(TargetFramework.Contains('android'))">
    <AndroidMavenLibrary Include="{DependencyGroupId}:{DependencyName}" Version="{DependencyVersion}" Bind="false" />
</ItemGroup>

このプロセスの詳細については、AndroidMavenLibrary リファレンスおよび Java 依存関係の検証に関するドキュメントも参照してください。

プレースホルダーの DotnetNewBinding クラスの名前を、ラップされるネイティブ ライブラリをより適切に反映するように変更できます。 API 定義を記述するためのその他の例とヒントについては、「既存のバインドを変更する」セクションで詳細をお読みください。

.NET アプリで API を使用する

テンプレートには、template/sample/MauiSample に .NET MAUI サンプル アプリが含まれています。これは、.NET バインド プロジェクトを参照し、ネイティブ ライブラリをすぐに使用できるようにします。

ご自分の .NET MAUI、.NET for Android、.NET for iOS、または .NET for Mac Catalyst アプリを使用したい場合、それを行うには、バインド ライブラリを参照するように .NET アプリ プロジェクト ファイルを変更します。

<!-- Reference to MaciOS Binding project -->
<ItemGroup Condition="$(TargetFramework.Contains('ios')) Or $(TargetFramework.Contains('maccatalyst'))">
    <ProjectReference Include="..\..\macios\NewBinding.MaciOS.Binding\NewBinding.MaciOS.Binding.csproj" />
</ItemGroup>

<!-- Reference to Android Binding project -->
<ItemGroup Condition="$(TargetFramework.Contains('android'))">
    <ProjectReference Include="..\..\android\NewBinding.Android.Binding\NewBinding.Android.Binding.csproj" />
</ItemGroup>

既存のバインドを変更する

既存の API サーフェスで、ご自分のプロジェクトに必要な機能が公開されていない場合は、独自の変更を行ってください。

iOS と Mac Catalyst

Xcode プロジェクト内には、バインド用のパブリック API サーフェスを定義する 1 つ以上の Swift ファイルがあります。 たとえば、Firebase Messaging 用の register メソッドは、次のように定義されます。

@objc(MauiFIRMessaging)
public class MauiFIRMessaging : NSObject {

    @objc(register:completion:)
    public static func register(apnsToken: NSData, completion: @escaping (String?, NSError?) -> Void) {
        let data = Data(referencing: apnsToken);
        Messaging.messaging().apnsToken = data
        Messaging.messaging().token(completion: { fid, error in
            completion(fid, error as NSError?)
        })
    }
    // ...
}

.NET バインドで使用されるネイティブ ラッパー API の型は public として宣言する必要があり、@objc(NameOfType) で注釈を付ける必要があります。またメソッドも、public にする必要があり、Objective Sharpie で生成されるバインドに影響を与えるのに役立つ名前とパラメーターが指定された、同様の注釈 @objc(methodName:parameter1:) から利点を得ることができます。

このメソッドでは、パブリック API サーフェスで、.NET for iOS で既に認識されている型 (NSDataStringNSError) とコールバックのみが使用されていることがわかります。

Firebase.MaciOS.Binding プロジェクトでは、ApiDefinitions.cs ファイルに、このネイティブ ラッパー API のバインド定義が含まれています。

using System;
using Foundation;

namespace Firebase
{
    // @interface MauiFIRMessaging : NSObject
    [BaseType (typeof(NSObject))]
    interface MauiFIRMessaging
    {
        [Static]
        [Export ("register:completion:")]
        [Async]
        void Register (NSData apnsToken, Action<string?, NSError?> completion);
        // ...
    }

登録解除のためのメソッドを追加するとします。 Swift のコードは次のようになります。

@objc(unregister:)
public static func unregister(completion: @escaping (NSError?) -> Void) {
    // need delegate to watch for fcmToken updates
    Messaging.messaging().deleteToken(completion: { error in
        completion(error as NSError?)
    })
}

残りの半分では、この新しいメソッドを公開するようにバインド プロジェクトの ApiDefinitions.cs ファイルを更新します。 これを行うには、次の 2 つの方法があります。

  1. 必要なコードを手動で追加できます
  2. バインディング プロジェクトをビルドした後、Objective Sharpie ツールを実行して ApiDefinitions.cs ファイルを生成できます。 このファイルから関連する変更を見つけて手動でコピーするようにするか、ファイル全体をコピーして差分を調べ、必要な部分を見つけるようにします。

この場合、ApiDefinitions.cs に対する変更は次のようになります。

[Static]
[Export("unregister:")]
[Async]
void UnRegister(Action completion);

これらの変更を行ったら、バインド プロジェクトをリビルドできます。これで、新しい API を .NET MAUI プロジェクトから使用できるようになります。

Mac/iOS 用のバインド プロジェクトではソース ジェネレーターを使用しないため、プロジェクト システムと IntelliSense では、バインド プロジェクトのリビルドとソリューションの再読み込みが済み、プロジェクト参照で新しいアセンブリが取得されるようになるまで、新しい API が認識されない可能性があります。 IntelliSense のエラーが発生しても、それに関係なく、アプリ プロジェクトはコンパイルされます。

Android

Android Studio プロジェクト内には、バインド用のパブリック API サーフェスを定義する Java ファイルを含むモジュール ディレクトリがあります。 たとえば、Facebook 用の initialize メソッドは、以下のように定義されます。

package com.microsoft.mauifacebook;

import android.app.Activity;
import android.app.Application;
import android.os.Bundle;
import android.util.Log;

import com.facebook.LoggingBehavior;
import com.facebook.appevents.AppEventsLogger;

public class FacebookSdk {

    static AppEventsLogger _logger;

    public static void initialize(Activity activity, Boolean isDebug) {
        Application application = activity.getApplication();

        if (isDebug) {
            com.facebook.FacebookSdk.setIsDebugEnabled(true);
        }

        com.facebook.FacebookSdk.addLoggingBehavior(LoggingBehavior.APP_EVENTS);

        AppEventsLogger.activateApp(application);

        _logger = AppEventsLogger.newLogger(activity);
    }

    // ...
}

このメソッドでは、パブリック API サーフェスで、.NET for Android で既に認識されている型 (ActivityBoolean) のみが使用されていることがわかります。

Facebook.Android.Binding プロジェクトでは、Transforms/Metadata.xml ファイルには、Java パッケージ名 (com.microsoft.mauifacebook) をより C# フレンドリな名前空間 (Facebook) にマップする方法を記述する xml のみが含まれています。 一般に、現時点では Android のバインドは Mac/iOS より "自動的" な度合いが高く、これらの変換ファイルに変更を行う必要はほとんどありません。

<metadata>
    <attr path="/api/package[@name='com.microsoft.mauifacebook']" name="managedName">Facebook</attr>
</metadata>

イベントをログするためのメソッドを追加するとします。 Java コードは次のようになります。

public static void logEvent(String eventName) {
    _logger.logEvent(eventName);
}

この単純な変更からは、バインド プロジェクトで Transforms/Metadata.xml またはその他のファイルに対する更新の必要は生じません。 単純にバインド プロジェクトをリビルドできます。これで、新しい API を .NET MAUI プロジェクトから使用できるようになります。

Android 用のバインド プロジェクトではソース ジェネレーターを使用しないため、プロジェクト システムと IntelliSense では、バインド プロジェクトのリビルドとソリューションの再読み込みが済み、プロジェクト参照で新しいアセンブリが取得されるようになるまで、新しい API が認識されない可能性があります。 IntelliSense のエラーが発生しても、それに関係なく、アプリ プロジェクトはコンパイルされます。