Developer's Blog

共有コードからネイティブ依存処理が使える!PCLを使ったXamarinライブラリ作成テクニック

Fenrir Advent Calendar 2015

こんにちは。アプリケーション共同開発部 開発担当の伊藤です。Fenrir Advent Calendar 2015 もいよいよ 20 日目。ラストスパート突入といったところでしょうか。

僕が今年グッときたのが ModernHttpClient という Xamarin 向けのライブラリの仕組みです。このライブラリ、.NET 標準の System.Net.Http.HttpClient クラスにカスタム HTTP ハンドラとして指定して、.NET の API を使いつつも各プラットフォームネイティブのクラスで通信できるようにします(iOS では NSURLSession、Android では OkHttp が使用されます)。

通常、Xamarin を使った開発ではプラットフォームに依存しないロジックをPCL(Portable Class Library)に記述して、画面表示や各種センサーなどプラットフォームに依存するコードをアプリ自体に記述します。PCLはネイティブのコードを記述できないかわりに、いろいろなプラットフォームで使うことができます。

しかし、ModernHttpClient はネイティブライブラリに依存しているにもかかわらず、PCL からでも以下のように NativeMessageHandler(ModernHttpClientの主要クラス) を直接 new することができます。

var httpClient = new HttpClient(new NativeMessageHandler());

ModernHttpClient は NSURLSession や OkHttp など、プラットフォームに依存するコードを使っているので、通常であれば .NET から直接扱うことはできないはずです。一体どういうことなのでしょうか。

完全修飾型名とアセンブリの参照

.NET ではコンパイルするときと実行するとき、型を「完全修飾型名」(Fully Qualified Type Name, 日本語の表記は完全修飾名、完全限定型名など、ぶれがあるようです)というもので解決しています。たとえば、 Fenrir.TextToSpeech.SpeechSynthesizer というクラスを含む Fenrir.TextToSpeech というアセンブリのライブラリを作るとした場合、完全修飾型名は Fenrir.TextToSpeech.SpeechSynthesizer,Fenrir.TextToSpeech となります。詳しくは MSDN ライブラリの完全修飾型名の指定の項目が参考になります。

アセンブリに署名されていない場合、完全修飾型名とアセンブリのバージョン、公開しているメソッドなどの要素が致すればコンパイル時と実行時で異なるDLLを使って動作させることができます。そこで、PCL のビルド時は「PCL で実装はダミー」というアセンブリを参照してビルド、この PCL を使ったアプリのビルド・実行時は「プラットフォーム固有ライブラリで中身の実装あり」というアセンブリを参照させることで、PCL のコードからプラットフォーム固有の処理を直接呼び出すことができます。

たとえば、先ほどの Fenrir.TextToSpeech ライブラリを使う Xamarin.iOS アプリの場合、共有コードである PCL のプロジェクトからは PCL のダミー実装のライブラリを参照してビルドします。

pcllink1

次に、アプリからは先ほどの共有コードの PCL と、プラットフォーム固有ライブラリを参照してビルドします。するとアプリの実行時はプラットフォーム固有ライブラリが使用されます。

pcllink2

ModernHttpClient の実装で確認する

では、ModernHttpClient ではどのように実装されているか確認してみましょう。GitHub にあがっているソースで確認すると、PCL プロジェクト ModernHttpClient.Portable からは Facades.cs が参照されていて、このクラスの実装での各メソッドの呼び出しは例外がスルーされるようになっています。これは誤って PCL のままアプリが実行されたときに気づくための処理です。

そして各プラットフォームではこの Facades.cs ではなく固有のコード、たとえば iOS では NSUrlSessionHandler.cs が参照されて、実際にプラットフォーム固有の NSUrlSession が使用されています。

Xamarin Plugin

この仕組みを使ってすでに様々なライブラリが作られています。特に Plugins for Xamarin には昨年のコンテストに応募された各種プラグインが揃っており、そのソースはすべて公開されています

Xamarin Plugin と呼ばれているものは、ライブラリ自体の開発の作業性も確保するためにスタティックなクラスに Current プロパティを持たせてインターフェイスの実装を返す形になっています。また、Current プロパティは Lazy<T> を用いて最初にアクセスしたときにインスタンスが作られるようになっています。Vibrate プラグインのソースがわかりやすくて良いでしょう。

NuGet に登録する

さて、この仕組みで作ったライブラリを手で参照するのはとても使いづらいので、NuGet パッケージを作って NuGet に登録すると使いやすくなります。僕は以下のような Package.nuspec ファイルをソリューションファイルと同じディレクトリに作成して、nuget pack とコマンドを入力する方法を使っています。

<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
  <metadata>
    <id>Fenrir.TextToSpeech</id>
    <title>Text-to-Speech</title>
    <version>1.0.0</version>
    <authors>Fenrir Inc.</authors>
    <owners>Nobuhiro Ito</owners>
    <!-- <projectUrl>http://PROJECT_URL_HERE_OR_DELETE_THIS_LINE</projectUrl> -->
    <!-- <licenseUrl>http://LICENSE_URL_HERE_OR_DELETE_THIS_LINE</licenseUrl> -->
    <!-- <iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl> -->
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <description>Text-to-Speech support for Xamarin apps</description>
	<releaseNotes>
		1.0.0 - First Release
	</releaseNotes>
    <copyright>Copyright (C) 2015 Fenrir Inc.</copyright>
    <tags>xamarin monoandroid monodroid monotouch</tags>
    <dependencies>
    </dependencies>
  </metadata>
  <files>
    <!-- Core -->
	<file src="Fenrir.TextToSpeech/Fenrir.TextToSpeech.PCL/bin/Release/Fenrir.TextToSpeech.dll"
		target="lib\portable-win+net45+wp8+win8+wpa81" />
  
    <!-- iOS -->
	<file src="Fenrir.TextToSpeech/Fenrir.TextToSpeech.iOS/bin/Release/Fenrir.TextToSpeech.dll"
        target="lib\Xamarin.iOS10" />
  
    <!-- Droid -->
	<file src="Fenrir.TextToSpeech/Fenrir.TextToSpeech.Android/bin/Release/Fenrir.TextToSpeech.dll"
        target="lib\MonoAndroid" />
  </files>
</package>

一般向けであれば NuGet Gallery に登録すれば他のライブラリと同じように使えるようになります。社内向けライブラリを管理する場合はファイルサーバーに置くか、NuGet Server を設置すればよいでしょう。フェンリルの Xamarin 開発チームも様々なライブラリを作って NuGet で管理しています。このおかげでいまではライブラリの追加も簡単にできるようになりました。


いかがでしたでしょうか。Xamarin Plugin を作れば、Xamarin.Forms、MvvmCross などどのフレームワークを使っていても同じように使えますので、Xamarin アプリデベロッパーのみなさまはこの年末、Xamarin Plugin 開発にもチャレンジしてみてはいかがでしょうか。

フェンリルでは Xamarin を使ったアプリ開発のご依頼・ご相談を承っております。フェンリルの Xamarin を使ったクロスプラットフォーム開発について、詳しくはこちらのページをご覧ください。

フェンリル株式会社 | クロスプラットフォームアプリ開発 (Xamarin)

フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!

Copyright © 2019 Fenrir Inc. All rights reserved.