XamarinでSharedProjectとNuGet参照のあれこれ

この記事は、[Xamarin Advent Calendar 2016](http://qiita.com/advent-calendar/2016/xamarin) の16日目の記事です。

---

11月に行われた [JXUGC #20 Xamarin ハンズオン大会 第二弾 名古屋支部 ＆ 学生支部](https://jxug.connpass.com/event/41648/) で私はハンズオンのメンターを務めさせていただきました。

その際、ある質問を受け、その動作がすごく気になったので調べついでにここに書き残しちゃいたいと思います。

その質問がこちら

**SharedProjectでは、NuGet参照の追加はできないが、.Droidなどで追加したもののusingができるのはなぜか
また、その際に一部のプロジェクトしか対象のNuGet参照がなかったときの動作はどうなるのか**

というものです。

ハンズオンで使用したプロジェクトはXamarin Dev Daysで使用されたこちらのプロジェクトです。

https://github.com/xamarin/dev-days-labs/
https://github.com/chomado/xamarin-dev-doc (日本語ドキュメント)

このプロジェクトはSharedProjectによってコード共通化が行われています。

どういった時にSharedProject側にNuGet参照のものが見えるようになるのか、色々実験してみました。 


## 実験過程

実験に使ったもの/バージョン

- Visual Studio for Mac Preview 2
- Xamarin.Forms 2.3.2.127
- Json.NET (ID:Newtonsoft.Json 実験で追加するライブラリ)

実験方法

```test.cs
// usingに追加
using Newtonsoft.Json;

// onStart()の部分に追加
JsonToken token = new JsonToken();
System.Diagnostics.Debug.WriteLine(token.ToString());
```

これを足してエラーが出ないで起動できるか確かめる

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> 全部のプロジェクトに足してみる

どの部分でもエラーは出なかった
文字も出力された

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> Droidだけに足して、Androidで起動

IDE上でも、起動自体も問題なし
文字も出力された

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> iOSだけに足して、Androidで起動

IDE上でエラーが表示され、起動もできなくなった

![スクリーンショット 2016-12-15 15.59.38.png](https://qiita-image-store.s3.amazonaws.com/0/29459/bb478ff7-26df-e385-c736-11c9b0f44f57.png "スクリーンショット 2016-12-15 15.59.38.png")

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> 上の状態から、スタートアッププロジェクトをiOS側に設定し、iOSで起動

IDE上でも、起動自体も問題なし
文字も出力された

--- 

大体動作がつかめてきましたね。

重要なのは、現在どのターゲットに向けてビルドしようとしているか（スタートアッププロジェクトがどれを向いているか）そうです。

だとすると、特定のプラットフォームにしか無いライブラリだけど、各プラットフォームのプロジェクトではなくShared上に書きたい・・・！ となった場合はどうすればいいでしょう。

そこで登場するのがCompilerDirectiveです。

### CompilerDirectiveを使って切り替える

CompilerDirectiveを使うと、コンパイル時にどれに向けてコンパイルしているかで使うコードを切り替えることができます。

先程のコードを、Android向けだけにビルドされるようにしてみましょう

CompilerDirectiveを使うためには、ソースコード上に以下の様な記述をします

```test.cs
#if [プラットフォーム識別子]
// コード
#else
```

`[プラットフォーム識別子]`の部分に書かれた対象のプラットフォーム時のみ、`//コード`の部分のコードが適用されます。

識別子には以下の様なものがあります。

```
__ANDROID__ : Android
__ANDROID_11__ : Androidの特定のバージョン以上（左の例ではAPI11 Honeycomb以上）
__IOS__ : iOS
__TVOS__ : tvOS
__WATCHOS__ : watchOS
WINDOWS_PHONE : Windows Phone
__MOBILE__ : iOS and Android
```
https://developer.xamarin.com/guides/cross-platform/application_fundamentals/building_cross_platform_applications/part_4_-_platform_divergence_abstraction_divergent_implementation/

では、実際のコードです。

```test.cs
// usingに追加
#if __ANDROID__
using Newtonsoft.Json;
#endif

// onStart()の部分に追加
#if __ANDROID__
JsonToken token = new JsonToken();
System.Diagnostics.Debug.WriteLine(token.ToString());
#endif
```

先ほどと同じようにNuGet参照を追加してみたりします。

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> 全部のプロジェクトに足してみる

部分でもエラーは出なかった
文字も出力された

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> Droidだけに足して、Androidで起動

IDE上でも、起動自体も問題なし
文字も出力された

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> iOSだけに足して、Androidで起動

IDE上でエラーが表示され、起動もできなくなった
（同じ画像）

### <i class="fa fa-hand-o-right" aria-hidden="true"></i> 上の状態から、スタートアッププロジェクトをiOS側に設定し、iOSで起動

IDE上でも、起動自体も問題なし
文字は出力されなかった

---

最後の、iOS側での文字が出力されないようになりました。
これは、CompilerDirectiveによるものであるといえるでしょう。

## まとめ

- SharedProject側で使えるライブラリは、現在スタートアッププロジェクトに指定されているプロジェクトで参照されているもののみ
- SharedProject内で「特定のプラットフォームだけ」をやりたいときはCompilerDirectiveを使おう