はじめに・・
Xamarin向けに以下の要件でライブラリを作る必要がありました。
- IF部分はPCLじゃなくて、.NET Standard2.0対応してほしい
- プラットフォーム依存な部分はDIじゃなくて、Plugins for Xamarinみたいな機構を使って欲しい
- nugetパッケージ化してほしい
ほぅ、、、全然わからんぞ・・。
ということで、四苦八苦しながら、nugetパッケージ化までしたので、やった事とか調べたことを書きます。
Plugins for Xamarin?
そもそも「Plugins for Xamarinってなんぞ?」と思ったので、
google先生に聞きました。
Plugins for Xamarinは「Xamarin.AndroidやXamarin.iOS向けの固有機能を上手くIF化して、抽象化レイヤーで利用できるようにしたライブラリ」となります。
※ライブラリというかプロジェクトのテンプレートかな。
Plugins for Xamarinのリポジトリはこちら。
利用するためにプロジェクトテンプレートをVisual Studio(※1)に取り込む必要があります。
※こちらから、テンプレートをダウンロード出来ます。
vsixのため、Visual Studio for Macではインポート出来ません。。(まずここにはまる・・)
Windows版でライブラリ開発はしましょう!って事ですかね・・。
Plugins for Xamarinで試しにプロジェクトを作ってみる
試しに「SamplePlugin」という名前でPlugins for Xamarinのテンプレートを作って、サンプルのプロジェクトを作ると以下の構成になります。
- Plugin.SamplePlugin (PCL)
- Plugin.SamplePlugin.Abstractions (PCL)
- Plugin.SamplePlugin.Android
- Plugin.SamplePlugin.iOS ※本当はUWPとかも出来ますが、今回は不要だったので、削除しました。
とりあえず自分が思った感想は、
「ほぅ、、こんな感じなのか。そもそも、これどうやってプラットフォームごとにAPI(の中身)変わってるんだ?」
と思ったので、調べてみました。
Plugins for XamarinのAPI切り替えについて
平たく言うと「Bait and Switch」という仕組み?を使って、APIの切り替え(正確にはdllを切り替え)をしています。
前述したサンプルのプロジェクトを例にあげると、以下のようになっています。
* 2のAbstractionsはIFのみを提供(もしくは抽象化したクラス)
* 1はPCLなプロジェクトから呼び出し可能
* 3はXamarin.Androidなプロジェクトから呼び出し可能
* 4はXamarin.iOSなプロジェクトから呼び出し可能
1.3.4ではIFの実体を提供するCrossSamplePluginというクラスが定義されていて、同じファイルを参照しています。
実体については、ビルド時のオプションで変わるようになっています。
※1の場合は実体はなしとし、3,4の場合はプラットフォームに応じたIFを実装したImplementationクラスを返すようになっている。
その結果何が起こるかというと、1.3.4のdllは全て同じ名前(完全修飾名が同じ)となります。
実行時はプラットフォーム毎に見るDLLを変えることが出来ます。
※上手く説明出来ないので、、こことかここらへんを参考にしてみてください。
Plugins for Xamarinの.NET Standard対応について
さて、ここまででベースとなるプロジェクトは出来ました。
でも、Plugins for Xamarinの抽象化部分(サンプルで言う所の1と2)はPCLで出来ているため、まだ要件をみたせていません。
.NET Standard2.0へ置き換えが必要となります。
.NET Standard?
ここが参考になりました。
PCLを.NET Standard2.0に置き換える
「ふむ、プロパティ変えるだけで済むやろ」
と、思った時期が私にも有りました・・・・涙
単純にプロジェクトのプロパティからTargetFrameworkの変更では出来ませんでした・・。
なので、以下の手順を踏みます
- プロジェクトファイルをいじって、.NET Standardなプロジェクトに変更する
- 不要なファイルやフォルダを削除する
ちなみにいじるプロジェクトはサンプルで言う所の1と2になります。
※1と2を.NET Standard2.0にすれば良いです。
プロジェクトファイルの変更
それぞれ以下のように直しました。
※自分は直接いじりましたが、.NETStandardなクラスのプロジェクトを作って、差し替えるほうが楽かもです。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<DefineConstants>TRACE;DEBUG;NETSTANDARD2_0;PORTABLE</DefineConstants>
</PropertyGroup>
<ItemGroup>
<!-- A reference to the entire .NET Framework is automatically included -->
<ProjectReference Include="..\Plugin.SamplePlugin.Abstractions\Plugin.SamplePlugin.Abstractions.csproj">
<Project>{6edb0588-ffc5-4ef5-8a99-9e241d0f878d}</Project>
<Name>Plugin.SamplePlugin.Abstractions</Name>
</ProjectReference>
</ItemGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DefineConstants>TRACE;DEBUG;NETSTANDARD2_0;</DefineConstants>
</PropertyGroup>
</Project>
不要ファイルの削除
.NET StandardなプロジェクトではAssemblyInfoとかは不要そうなので、削除しました。
※削除しないとビルドエラーになっちゃう。
nugetパッケージの作成
先程あげたこのページを見ると、Plugins for Xamarinのテンプレートを入れた段階で「Plugins for Xamarin NuSpec」があるらしいのですが、、私は見つからず。。
血の涙を流しながら、一からパッケージを作ろう!と決意しました。
nugetパッケージってどうやって作る?
色々とgoogle先生に聞いて、作ってみました。
こことかここ
簡単な構成のプロジェクトであれば、nuspecを作るまでもなく、実は以下でパッケージ化出来ます。
nuget pack SamplePlugin.csproj
ただ上記だと、Plugins for Xamarinの構成ではパッケージ化しても、上手く動作しません。
※手元でやった感じではダメでした・・。実は出来るのだろうか・・?
そのため、nuspecを書いて、明示的にnugetパッケージの構成を決めるようにしました。
nuspec?
nuspecとは「nugetパッケージ化する際に、どのようにパッケージ化するか?」という情報を記載した仕様ファイルとなります。(たとえば、バージョン情報とか依存関係とか)
今回のサンプルの場合だと以下のようなnuspecファイルを作成しました。
<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
<metadata>
<id>Plugin.SamplePlugin</id>
<version>1.0.0</version>
<title>SamplePlugin</title>
<authors>shota.sakamoto</authors>
<owners>shota.sakamoto</owners>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>sample plugin</description>
<releaseNotes>Test</releaseNotes>
<copyright>Copyright 2018</copyright>
<tags>sampleplugin</tags>
<dependencies>
</dependencies>
</metadata>
<files>
<!-- Cross-platform reference assemblies -->
<file src="Plugin.SamplePlugin\bin\Debug\netstandard2.0\Plugin.SamplePlugin.dll" target="lib\netstandard2.0\Plugin.SamplePlugin.dll" />
<file src="Plugin.SamplePlugin\bin\Debug\netstandard2.0\Plugin.SamplePlugin.deps.json" target="lib\netstandard2.0\Plugin.SamplePlugin.deps.json" />
<file src="Plugin.SamplePlugin.Abstractions\bin\Debug\netstandard2.0\Plugin.SamplePlugin.Abstractions.dll" target="lib\netstandard2.0\Plugin.SamplePlugin.Abstractions.dll" />
<file src="Plugin.SamplePlugin.Abstractions\bin\Debug\netstandard2.0\Plugin.SamplePlugin.Abstractions.deps.json" target="lib\netstandard2.0\Plugin.SamplePlugin.Abstractions.deps.json" />
<!-- iOS reference assemblies -->
<file src="Plugin.SamplePlugin.iOS\bin\iPhone\Debug\Plugin.SamplePlugin.dll" target="lib\xamarinios\Plugin.SamplePlugin.dll" />
<file src="Plugin.SamplePlugin.Abstractions\bin\Debug\netstandard2.0\Plugin.SamplePlugin.Abstractions.dll" target="lib\xamarinios\Plugin.SamplePlugin.Abstractions.dll" />
<!-- Android reference assemblies -->
<file src="Plugin.SamplePlugin.Android\bin\Debug\Plugin.SamplePlugin.dll" target="lib\MonoAndroid\Plugin.SamplePlugin.dll" />
<file src="Plugin.SamplePlugin.Abstractions\bin\Debug\netstandard2.0\Plugin.SamplePlugin.Abstractions.dll" target="lib\MonoAndroid\Plugin.SamplePlugin.Abstractions.dll" />
</files>
</package>
このnuspecを使って、一応パッケージ化出来ました!
最後に
今回やってみて、nugetパッケージ化するまで結構はまったので、
情報共有の意味も込めて、記事にしました。