【Xamarin.Forms】iBeacon発信アプリをクロスプラットフォーム開発する

#ソースの大部分が共通化されたiBeacon発信アプリを作ろう

iOSとAndroidの両プラットフォーム向けのネイティブアプリを開発する際、Xamarinを使用するとそれぞれのアプリを個別に開発するよりも少ない手間で開発を進めることができます。
特にXamarin.Formsであれば、プラットフォーム固有の処理以外のソースを共通化できるため、アプリ開発を随分効率的に進めることができます。

ビーコンの送受信のようなBluetooth Low Energy（BLE）を扱う処理は、プラットフォームに依存するため、プラットフォームごとに処理を実装する必要があります。
逆に言えば、**この部分のみを切り出して、UIを含む他の部分を共通化してしまう形で**アプリを作れます。

#サンプルアプリ

サンプルアプリをGitHubに公開しました。
https://github.com/microwavePC/iBeaconTransmitter
Prism for Xamarin.Forms（Prism.Forms）のテンプレートを使用して作成しています。
####対応プラットフォーム
* iOS
* Android
* Windows Phone

####機能
* ユーザーが入力したUUID、Major、Minorを持つ、iBeaconに準拠したフォーマットのアドバタイズメントパケットの発信
* 発信時のTxPower（電波強度）の調整

####画面構成
| iOS | Android | UWP |
|:-:|:-:|:-:|
|![](https://github.com/microwavePC/iBeaconTransmitter/blob/master/ScreenShot/ScreenShot_iOS.png?raw=true)|![](https://github.com/microwavePC/iBeaconTransmitter/blob/master/ScreenShot/ScreenShot_Android.png?raw=true)|![](https://github.com/microwavePC/iBeaconTransmitter/blob/master/ScreenShot/ScreenShot_WindowsPhone.png?raw=true)|

# プラットフォーム固有部分の実装

iBeaconの発信と停止を制御する処理は、**dependency service** としてプラットフォームごとに切り出して作成しています。
ネイティブのサンプルコード（iOSはObjective-CやSwift、AndroidはJava）をC#に置き換えていく感覚で作成することができます。

* iOS

```C#:iBeaconTransmitService.cs　から抜粋
// BLEの発信を制御するクラスのインスタンス
private CBPeripheralManager _peripheralManager = new CBPeripheralManager();

// 発信
public void StartTransmission(Guid uuid, ushort major, ushort minor, sbyte txPower)
{
	// BLE発信制御クラスに渡すためのiBeacon定義を作成する。
	NSUuid nsUuid = new NSUuid(uuid.ToString());
	CLBeaconRegion region = new CLBeaconRegion(nsUuid, major, minor, uuid.ToString());
	NSNumber nsnumTxPower = new NSNumber(txPower);
	NSDictionary peripheralData = region.GetPeripheralData(nsnumTxPower);

	// iBeaconの発信を開始する。
	_peripheralManager.StartAdvertising(peripheralData);
}

// 停止
public void StopTransmission()
{
	_peripheralManager.StopAdvertising();
}
```

* Android

```C#:iBeaconTransmitService.cs　から抜粋
// iBeaconのバイト列のフォーマット
// m：ビーコンのタイプ
// i：identifier（UUID、Major、Minorの場所の指定）
// p：パワーキャリブレーションの値
private const string IBEACON_FORMAT = "m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24";

// BLEの発信を制御するクラスのインスタンス
private BeaconTransmitter _beaconTransmitter;

// 発信
public void StartTransmission(Guid uuid, ushort major, ushort minor, sbyte txPower)
{
	// Android用のビーコン定義を作成
	Beacon beacon = new Beacon.Builder()
	                    .SetId1(uuid.ToString())
	                    .SetId2(major.ToString())
	                    .SetId3(minor.ToString())
	                    .SetTxPower(txPower)
	                    .SetManufacturer(Const.COMPANY_CODE_APPLE)
	                    .Build();

	// iBeaconのバイト列フォーマットをBeaconParser（アドバタイズ時のバイト列定義）にセットする。
	BeaconParser beaconParser = new BeaconParser().SetBeaconLayout(IBEACON_FORMAT);

	// iBeaconの発信を開始する。
	_beaconTransmitter = new BeaconTransmitter(Android.App.Application.Context, beaconParser);
	_beaconTransmitter.StartAdvertising(beacon);
}

// 停止
public void StopTransmission()
{
	_beaconTransmitter.StopAdvertising();
}
```

* UWP

```C#:iBeaconTransmitService.cs　から抜粋
// BLEの発信を制御するクラスのインスタンス
private BluetoothLEAdvertisementPublisher blePublisher = new BluetoothLEAdvertisementPublisher();

// 発信
public void StartTransmission(Guid uuid, ushort major, ushort minor, sbyte txPower)
{
	// BLE発信用のデータを格納する箱を作成
	BluetoothLEManufacturerData bleManufacturerData = new BluetoothLEManufacturerData()
	{
		CompanyId = Const.COMPANY_CODE_APPLE
	};

	// データを格納できるようにするため、UUID、Major、Minor、TxPowerをbyteに変換する。
	// TxPowerはオフセットをかけてから変換する。
	byte[] uuidByteArray = uuid.ToByteArray();
	byte[] majorByteArray = BitConverter.GetBytes(major);
	byte[] minorByteArray = BitConverter.GetBytes(minor);
	byte txPowerByte = (byte)((int)txPower + 256);

	// UUID、Major、Minorを、iBeaconのフォーマットに準拠する形に梱包する。
	byte[] ibeaconAdvertisementDataArray = new byte[]
	{
		// iBeaconと識別するための固定値
		0x02, 0x15,
		// UUID
		uuidByteArray[0], uuidByteArray[1], uuidByteArray[2], uuidByteArray[3],
		uuidByteArray[4], uuidByteArray[5], uuidByteArray[6], uuidByteArray[7],
		uuidByteArray[8], uuidByteArray[9], uuidByteArray[10], uuidByteArray[11],
		uuidByteArray[12], uuidByteArray[13], uuidByteArray[14], uuidByteArray[15],
		// Major
		majorByteArray[0], majorByteArray[1],
		// Minor
		minorByteArray[0], minorByteArray[1],
		// TxPower
		txPowerByte
	};

	// データをBLEで発信する。
	bleManufacturerData.Data = ibeaconAdvertisementDataArray.AsBuffer();
	blePublisher.Advertisement.ManufacturerData.Add(bleManufacturerData);
	blePublisher.Start();
}

// 停止
public void StopTransmission()
{
	blePublisher.Stop();
}
```

# おまけ

Windows Phone 向けのアプリは UWP なので、このサンプルアプリは Windows 10 PC にもちゃっかりインストールすることができます。

![iBeaconTransmitter_UWP.png](https://qiita-image-store.s3.amazonaws.com/0/140394/76a3e283-b225-2160-4cca-8831724ae7b7.png)

未検証なので動作の保証はできませんが、BLE の送受信が可能な Windows 10 PC を iBeacon にすることができるかもしれません。