Xamarin + Prism 超入門(とりあえず動かしてみよう!)

今回はXamarinで本格的な(?)開発を行う際の「gettting started」的な記事を書きたいと思います。
要点としては「Xamarin + Prism」で開発する為の「はじめの一歩」の記事となります。
Macで Xamarin Studio を利用するケース」、「WindowsVisual Studio を利用するケース」毎に、誰にでも分かる様に画面キャプチャ付きで手順を説明したいと思います。

同様のブログがすでにいくつも書かれていると思いますが、自分自身の整理と、本記事がどなたかのお役に立てればと思います。

Prismって何?

Xamarinに限らず、ある程度しっかりしたシステムの開発を行う際には、レイヤー化アーキテクチャが導入されます。
Web開発だと「MVC (Model-View-Controller」や「MVP (Model-View-Presenter」などがありますね。
Wikipediaによると、MVCに関する最初の論文が発表されたのが1988年のことだそうです。そう、このようなソフトウェアアーキテクチャは、遥か昔から存在しています。

で、Xamarinにおけるメジャーなレイヤー化アーキテクチャは「MVVM (Model-View-ViewMode)」となります。
まあ、Xamarinというか、XAML関連技術においてMVVMが標準技術として利用されてきました。
Xamlの始まりはWPFですね。続いてSilverlightWindowsストアアプリ、UWP、とXaml技術は継続的進化を果たしています(私の様なベテランエンジニアに対しては、優しい、積み上げ型の知識が非常に役に立つ正常進化です)。
これらにMVVMアーキテクチャを適用するためのライブラリ(フレームワーク)がいくつか世の中には存在します。
代表的な(そしてXamarinに対応している)フレームワークとして以下のようなものがあります。

  • Prism
  • MVVM Light

今回紹介するのは Prism になります。
ネット上の情報を見る限り Prism がディファクトスタンダードとなりつつあるように感じます。
Xamarinの神様(Miguel de Icaza氏)が、Prism使おうよって仰ってますしね。

Prism公式サイトは以下です。

github.com

何故 Prism を使うのか?

それは Prism 公式サイトの以下の言葉に集約されています。

Prism is a framework for building loosely coupled, maintainable, and testable XAML applications in WPF, Windows 10 UWP, and Xamarin Forms.

ここでは、これ以上深く踏み込まず、次に進みます・・・

Xamarin Studio で Prism を使おう(Mac)

(1) Prism Template Pack のインストール

Xamarin Studioを起動します。
メニュー「Xamarin Studio Community→アドイン」を選択します。

f:id:daigo-knowlbo:20161211150641p:plain

以下の「アドインマネージャー」ウィンドウが表示されます。
「ギャラリー」タブを選択、右上の検索テキストボックスに「Prism」と入力、アドインリストから「Prism Template Pck」を選択し、右下の「インストール」ボタンをクリックします。

f:id:daigo-knowlbo:20161211150933p:plain

[2016/12/31 追記]
アドインマネージャーで検索して Prism Template Pack が表示されない場合は、Prism Template Packから直接アドインをダウンロードして、アドインマネージャーウィンドウ左下の「ファイルからのインストール」ボタンからインストールできます。

(2) Prismプロジェクトの作成

メニュー「ファイル→新しいソリューション」を選択します。

f:id:daigo-knowlbo:20161211151135p:plain

プロジェクトテンプレートとして「Xamarin.Forms→Prism Unity App」を選択を選択して、「次へ」ボタンをクリックします。

f:id:daigo-knowlbo:20161211151236p:plain

App Name等を適当に設定します。ここではApp Nameは PrismExample、Organization Identifierは jp.co.knowlbo としました。

f:id:daigo-knowlbo:20161211151401p:plain

続いてプロジェクトの構成を設定します。プロジェクト名・ソリューション名は共に PrismExample としました。

f:id:daigo-knowlbo:20161211151539p:plain

しばらく待っているとソリューション・プロジェクトが構成されます。
作成されたソリューションは以下の様になります。

f:id:daigo-knowlbo:20161211151741p:plain

通常のXamarin Formsと基本的には似た構成になります。

  • PCLとしてのXamarin Formsプロジェクトである「PrismExampleプロジェクト」。
  • Android固有実装用プロジェクトである「PrismExample.Droidプロジェクト」。
  • iOS固有実装用プロジェクトである「PrismExample.iOSプロジェクト」。

1つ大きく異なるのはPrismExampleプロジェクトに ViewModels / Views フォルダがあることです。
まあ、PrismはMVVMフレームワークですからこれがキモですね。

(3) ビルドエラーが出るから修正

自動生成されたソリューションは、いわゆる HelloWorld 的な実装が出力されています。
とりあえずビルドして実行してみよう!ということで、メニュー「ビルド→すべてリビルド」を選択します。

が・・・2016/12/11段階のPrism Template Packにおいては、ビルドエラーにその道を阻まれます(泣)
ビルドエラーは以下の通りです。

f:id:daigo-knowlbo:20161211152549p:plain

エラーメッセージは以下ですね。

CS0246: The type or namespace name `IPlatformInitializer' could not be found. Are you missing `Prism.Unity' using directive?    
CS0246: The type or namespace name `IUnityContainer' could not be found. Are you missing `Microsoft.Practices.Unity' using directive?  

「型か名前空間が見つからないんだけど・・・usingディレクティブ足りてないんじゃね?」って言ってますね。
なのでエラーが出ている「PrismExample.Droid¥MainActivity.cs」と「PrismExample.iOS¥AppDelegate.cs」に、以下の using を追加します。

using Prism.Unity;
using Microsoft.Practices.Unity;

ビルドは無事通るはずです。
※これはそもそも提供されているPrism Templata Packの問題かな?皆さんが利用する際には治っているかも・・・

(4) 実行してみる

メニュー「実行→デバッグなしで開始」を選択します。デフォルトだと iOS emulator での実行が構成されています。
実行結果は以下の通りです。

f:id:daigo-knowlbo:20161211153650p:plain

(5) せっかくなので、ちょっといじろうか・・・

ただ実行するだけでは、あまりにもつまらないので、以下の様な実装を付け加えようと思います。
以下の様な入力フォームとします。

f:id:daigo-knowlbo:20161211164101p:plain

名前を入れて・・・

f:id:daigo-knowlbo:20161211164354p:plain

決定ボタンを押すと、メッセージが表示されます。

f:id:daigo-knowlbo:20161211164551p:plain

実装コードは以下の通りです。

--- MainPage.xaml ---
<?xml version="1.0" encoding="utf-8"?>
<ContentPage 
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" 
    prism:ViewModelLocator.AutowireViewModel="True" 
    x:Class="PrismExample.Views.MainPage" 
    Title="MainPage">
  <StackLayout HorizontalOptions="Center" VerticalOptions="Center">
    <Label Text="あなたの名前" />
    <Entry Text="{Binding YourName}" />
    <Button Text="決定" Command="{Binding DecisionCommand}" />
    <Label Text="{Binding Message}" />
  </StackLayout>
</ContentPage>
--- MainPageViewModel.cs ---
using Prism.Commands;
using Prism.Mvvm;
using Prism.Navigation;

using System;
using System.Collections.Generic;
using System.Linq;

namespace PrismExample.ViewModels
{
  public class MainPageViewModel : BindableBase, INavigationAware
  {
    // 名前入力Entry項目にバインドします
    private string _yourName;
    public string YourName
    {
      get { return _yourName; }
      set { SetProperty(ref _yourName, value); }
    }

    // メッセージ表示Label項目にバインドします
    private string _message;
    public string Message
    {
      get { return _message; }
      set { SetProperty(ref _message, value); }
    }

    // 決定ButtonのCommandにバインドします。
    private DelegateCommand _decisionCommand;
    public DelegateCommand DecisionCommand
    {
      get
      {
        return this._decisionCommand = this._decisionCommand ??
          new DelegateCommand(DecisionCommandExecute);
      }
    }

    public MainPageViewModel()
    {
    }

    private void DecisionCommandExecute()
    {
      this.Message = string.Format("{0}さん こんにちは", this.YourName);
    }

    public void OnNavigatedFrom(NavigationParameters parameters)
    {
    }

    public void OnNavigatedTo(NavigationParameters parameters)
    {
    }
  }
}

実装をずらずらと載せてしまいましたが、要点は以下になります。
(すみません、実装を載せておきながら、なのですが、中身の説明は本投稿の軸となる趣旨からはずれるので、要点のみの概略で、PrismやXamarin Formsの詳細技術は別投稿で取り上げさせていただきたいと思います。m( _ _ )m)

  • MainPage.xamlに 名前入力用 Entry 要素を追加
    EntryコントロールのTextプロパティの値、つまり入力された値は {Binding} 構文により MainPageViewModelクラス の YourNameプロパティ にデータバインディングします。

  • MainPage.xamlに 決定 Button 要素を追加
    Buttonコントロールの Commandプロパティ に、MainPageViewModelクラスの DecisionCommandプロパティ をデータバインドします。
    Button.Clickedイベントではなく、Commandにバインドする理由は「BindablePropertyが・・・・」などの技術的バックボーンがありますが、このお話はまた別のところで・・・

  • MainPage.xamlに メッセージ Label 要素を追加
    LabelコントロールのTextプロパティの値に、MainPageViewModeクラスの Messageプロパティ をデータバインディングします。
    MainPageViewModel側でMessageプロパティを変更すると、自動的にUIの表示も切り替わります。

  • MainPageViewModelにDelegateCommandプロパティを追加
    MainPageViewModelクラスに、DelegateCommand型のDecisionCommandプロパティを追加し、イベントハンドラとしてDecisionCommandExecute()メソッドを実装します。
    以下の様にMessageプロパティ・YourNameプロパティを扱うことでロジックを処理しています。xaml側とのデータバインディング処理によってViewModelクラス上でのロジック処理結果がUIに自動的に反映されます。

--- MainPageViewModel.csのコードスニペット ---

private void DecisionCommandExecute()
{
  this.Message = string.Format("{0}さん こんにちは", this.YourName);
}

こんな感じでView - ViewModelが疎結合に実装されました。

Visual Studio 2015 で Prism を使おう(Windows)

(1) Prism Template Pack のダウンロード & インストール

Visual Studio を起動し、メニュー「ツール→拡張機能と更新プログラム」を選択します。

f:id:daigo-knowlbo:20161211215627p:plain

以下の「拡張機能と更新プログラム」ウィンドウが表示されます。
左のツリーから「オンライン」を選択し、右上の検索テキストボックスに「Prism」と入力します。中央のリストに「Prism Template Pack」が表示されるので「ダウンロード」ボタンをクリックします。

f:id:daigo-knowlbo:20161211220650p:plain

ダウンロードが完了すると以下のインストールウィンドウが表示されるので「インストール」ボタンをクリックします。

f:id:daigo-knowlbo:20161211220915p:plain

インストールが完了すると、Visual Studioの再起動を求められますので、再起動を行います。

(2) Prismプロジェクトの作成

メニュー「ファイル→新規作成→プロジェクト」を選択します。

f:id:daigo-knowlbo:20161211221152p:plain

「新プロジェクト」ウィンドウが表示されるので、プロジェクトテンプレートとして「Prism→Prism Unity App(Xamarin.Forms)」を選択します。
プロジェクト名は「PrismExample」としました。

f:id:daigo-knowlbo:20161211221445p:plain

プロジェクトに含めるプラットフォーム選択ウィンドウが表示されます。今回は「ANDROID / iOS / UWP」を選択することにします。
MacのXamarin studioでは作成する事は出来なかった「UWP / STORE 8.1 / PHONE 8.1」が選択可能です。逆にiOSアプリはMacに接続しないとデバッグ・実行する事が出来ません。

f:id:daigo-knowlbo:20161211221715p:plain

プロジェクトの用意が完了するとMacへの接続情報設定ウィンドウが表示されますが、ここではそのまま設定せずに進みます。
次にUWPのターゲットバージョン選択ウィンドウが表示されます。ここでは、デフォルトのままOKをクリックする事とします。

f:id:daigo-knowlbo:20161211222233p:plain

以上でプロジェクトの生成が完了しました。

(3) ビルド

メニュー「ビルド→ソリューションのリビルド」を選択します。

f:id:daigo-knowlbo:20161211223006p:plain

しばらく時間がかかると思いますが、ビルドは正常終了するはずです。

(4) 実行

せっかくWindows環境なので、UWPアプリケーションとして実行してみましょう。
ソリューションエクスプローラ上で、「PrismExample.UWP」をマウス右クリックし、「スタートアッププロジェクトに設定」を選択します。

f:id:daigo-knowlbo:20161211230729p:plain

実行する前にアプリケーションを配置する必要があります。
メニュー「ビルド→PrismExample.UWPの配置」を選択します。

f:id:daigo-knowlbo:20161211223549p:plain

対象のPCが開発者モードになっていなかった場合、以下のウィンドウが表示されます。

f:id:daigo-knowlbo:20161211224020p:plain

UWPとして実行する場合、Windows上での実行になりますので、Windows設定を開発者モードにする必要があります。
「開発者モード」を選択しましょう。

f:id:daigo-knowlbo:20161211224205p:plain

改めて、メニュー「ビルド→PrismExample.UWPの配置」を選択します。
配置が完了したら、ツールバーから「▶︎ローカルコンピューター」をクリックしましょう。

f:id:daigo-knowlbo:20161211224345p:plain

デバッグモードで PrismExample.UWP が実行されました。

f:id:daigo-knowlbo:20161211224551p:plain

まとめ

Xamarinに限らずアーキテクチャを考慮せず、ただただコードビハインドにロジックを書き連ねるような実装方法が行われる事があります。
レイヤー化アーキテクチャに限らず、testableである事、maintainableである事、適切な責務の分離が行われている事などは非常に重要です。
XamarinにおけるPrismの導入に限らず、システムソリューションを構築する際は、一度立ち止まってベストプラクティスなアーキテクチャを検討したいものです。

今回は Xamarin + Prism の入り口の説明でしたので、今後はより技術的に踏み込んだトピックも取り上げていければと思います。