Xamarin.Forms にも標準で DependencyService という機能があるんですが、こいつよりも柔軟な素敵な機能が Prism.Forms には提供されています。
IPlatformInitializer
それは IPlatformInitializer です!これは、Prism.Forms のDIコンテナへのインスタンスの登録処理をプラットフォーム固有プロジェクトで実行する箇所を提供するという仕組みになります。
これによって、インターフェースをPCLプロジェクトに定義して、プラットフォーム固有処理をDroidとiOSとUWPプロジェクトに実装したものをDIコンテナに登録するといったことが出来るようになってます。
使ってみよう
ということで使ってみようと思います。Visual Studio 2017でPrism Template Packを入れてプロジェクトを新規作成するとIPlatformInitializerが使われた状態のコードが生成されるので楽です。
今回は Autofac を使ったプロジェクトテンプレートを前提にお話ししますが、他のDIコンテナでも大体同じになります。
プロジェクトを新規作成すると、Droid, iOS, UWPプロジェクトに以下のIPlatformInitializerを実装したクラスが生成されています。
- Androidプロジェクト
- MainActivity.csの中のAndroidInitializerクラス
- iOSプロジェクト
- AppDelegate.csの中のiOSInitializerクラス
- UWPプロジェクト
- MainPage.xaml.csの中のUwpInitializerクラス
こんな感じのクラスになっていると思います。(Androidのものを抜粋してます)
publicclass AndroidInitializer : IPlatformInitializer { publicvoid RegisterTypes(IContainer container) { } }
このRegisterTypesメソッドがPrismのアプリが開始するタイミングで呼び出されるので、ここにコンテナへの登録処理を行います。
本来はOSごとに違う処理をしたいときとかに使うのですが、今回はOSごとに違う文字列を返すという処理を例に作ってみようと思います。(OSごとに違う値を返したいというケースでは本来OnPlatformを使いましょう)
まず、PCLのプロジェクトにプラットフォーム固有の処理を抽象化したインターフェースを定義します。今回の例の場合は、文字列を返すだけのメソッドになります。こんな感じのコードをPCLのプロジェクトに追加します。
namespace PrismAutofacApp3.Services { publicinterface IGreetingService { string GetText(); } }
そして、プラットフォーム固有のプロジェクトに以下のようにインターフェースを実装したクラスを作成します。
Droidプロジェクト
using PrismAutofacApp3.Services; namespace PrismAutofacApp3.Droid.Services { publicclass GreetingService : IGreetingService { publicstring GetText() { return"Hello Droid project."; } } }
iOSプロジェクト
using PrismAutofacApp3.Services; namespace PrismAutofacApp3.iOS.Services { publicclass GreetingService : IGreetingService { publicstring GetText() { return"Hello iOS project."; } } }
UWPプロジェクト
using PrismAutofacApp3.Services; namespace PrismAutofacApp3.UWP.Services { publicclass GreetingService : IGreetingService { publicstring GetText() { return"Hello UWP project."; } } }
PlatformInitializer へのインスタンス登録処理の追加
Droidプロジェクト
publicclass AndroidInitializer : IPlatformInitializer { publicvoid RegisterTypes(IContainer container) { var builder = new ContainerBuilder(); builder.RegisterType<GreetingService>().As<IGreetingService>().SingleInstance(); builder.Update(container); } }
iOSプロジェクト
publicclass iOSInitializer : IPlatformInitializer { publicvoid RegisterTypes(IContainer container) { var builder = new ContainerBuilder(); builder.RegisterType<GreetingService>().As<IGreetingService>().SingleInstance(); builder.Update(container); } }
UWPプロジェクト
publicclass UwpInitializer : IPlatformInitializer { publicvoid RegisterTypes(IContainer container) { var builder = new ContainerBuilder(); builder.RegisterType<GreetingService>().As<IGreetingService>().SingleInstance(); builder.Update(container); } }
使ってみよう
使い方は、普通にDIしたクラスと同じです。Prism Template Packで生成さらたプロジェクトをちょっといじってみましょう。MainPageViewModelにIGreetingServiceをDIして使っています。
using Prism.Commands; using Prism.Mvvm; using Prism.Navigation; using PrismAutofacApp3.Services; using System; using System.Collections.Generic; using System.Linq; namespace PrismAutofacApp3.ViewModels { publicclass MainPageViewModel : BindableBase, INavigationAware { private IGreetingService GreetingService { get; } privatestring _title; publicstring Title { get { return _title; } set { SetProperty(ref _title, value); } } public MainPageViewModel(IGreetingService greetingService) { this.GreetingService = greetingService; } publicvoid OnNavigatedFrom(NavigationParameters parameters) { } publicvoid OnNavigatingTo(NavigationParameters parameters) { this.Title = this.GreetingService.GetText(); } publicvoid OnNavigatedTo(NavigationParameters parameters) { } } }
実行して確認してみましょう。今Macがない環境なのでAndroidとUWPだけですが以下のように表示されるテキストが変わっていることが確認できます。
まとめ
ということでIPlatformInitializerについて紹介しました。こいつのメリットとして、プラットフォーム固有のクラスに対して、さらに別のクラスをDIしたりといったこともできるという柔軟性は、DependencyServiceに無いですし、このようにインターフェースをDIするようにしておくと、後で単体テストするときでもいい感じにモックに差し替えたりできるので便利です。
ということで良いXamarin.Froms + Prism.Forms生活を!!