クロスプラットフォーム アプリのケース スタディ: Tasky

TaskyPortable は、簡単な To Do リスト アプリケーションです。 このドキュメントでは、その設計およびビルド方法について、「クロスプラットフォーム アプリケーションの構築」ドキュメントのガイダンスに従って説明します。 ここでは、次の項目について説明します。

設計プロセス

コーディングを開始する前に、達成したいことののロードマップを作成することをお勧めします。 このことは、複数の方法で公開される機能を構築するクロスプラットフォーム開発に特に当てはまります。 何を構築するのかを明確に把握することから始めると、開発サイクルの後半で時間と労力を節約できます。

要件

アプリケーションを設計する最初のステップは、必要な機能を特定することです。 これらは、大まかな目標または詳細なユース ケースにすることができます。 Tasky には、次のような単純な機能要件があります。

  • タスクの一覧を表示する
  • タスクを追加、編集、削除する
  • タスクの状態を "完了" に設定する

プラットフォーム固有の機能の使用について検討する必要があります。 Tasky は iOS ジオフェンシングや Windows Phone ライブ タイルを利用できますか? 最初のバージョンでプラットフォーム固有の機能を使用しない場合でも、ビジネスおよびデータ レイヤーがそれらに対応できるように事前に計画する必要があります

ユーザー インターフェイス デザイン

まず、ターゲット プラットフォーム全体に実装できる高レベルの設計から始めます。 プラットフォーム固有の UI の制約に注意してください。 たとえば、iOS の TabBarController ではボタンを 5 つ以上表示できますが、Windows Phone では 4 つまでしか表示できません。 任意のツールを使用してスクリーン フローを描画します (ペーパー ワーク)。

Draw the screen-flow using the tool of your choice paper works

データ モデル

保存する必要があるデータを把握することは、使用する永続化メカニズムを決定するのに役立ちます。 使用可能なストレージ メカニズムの詳細と、それらのどれを使用するかについて決定する際の参照情報については、クロスプラットフォーム データ アクセスに関する記事を参照してください。 このプロジェクトでは、SQLite.NET を使用します。

Tasky では、"TaskItem" ごとに次の 3 つのプロパティを格納する必要があります。

  • Name – 文字列
  • Notes – 文字列
  • Done – ブール値

中心的機能

要件を満たすためにユーザー インターフェイスが使用する必要がある API について検討してください。 To Do リストには、次の機能が必要です。

  • すべてのタスクを一覧表示する - 使用可能なすべてのタスクのメイン スクリーンの一覧を表示します
  • 1 つのタスクを取得する – タスク行がタッチされたとき
  • 1 つのタスクを保存する – タスクが編集されたとき
  • 1 つのタスクを削除する – タスクが削除されたとき
  • 空のタスクを作成する – 新しいタスクが作成されたとき

コードの再利用を実現するために、この API を "ポータブル クラス ライブラリ" に一度実装する必要があります。

実装

アプリケーションの設計について合意したら、クロスプラットフォーム アプリケーションとして実装する方法について検討します。 これがアプリケーションのアーキテクチャになります。 「クロスプラットフォーム アプリケーションの構築」ドキュメントのガイダンスに従って、アプリケーション コードを次の部分に分割する必要があります。

  • 共通コード – タスク データを保存するための再利用可能なコードを含む共通プロジェクト。データの保存と読み込みを管理するための Model クラスと API を公開します。
  • プラットフォーム固有のコード – 共通コードを "バックエンド" として使用して、各オペレーティング システムのネイティブ UI を実装するプラットフォーム固有のプロジェクト。

Platform-specific projects implement a native UI for each operating system, utilizing the common code as the back end

これら 2 つの部分について、次のセクションで説明します。

共通 (PCL) コード

Tasky Portable は、共通コードを共有するためにポータブル クラス ライブラリ戦略を採用しています。 コード共有オプションの説明については、コード共有のオプションに関するドキュメントを参照してください。

データ アクセス レイヤー、データベース コード、コントラクトなど、すべての共通コードがライブラリ プロジェクトに配置されます。

完全な PCL プロジェクトを次に示します。 ポータブル ライブラリ内のすべてのコードは、対象となる各プラットフォームと互換性があります。 デプロイすると、各ネイティブ アプリがそのライブラリを参照します。

When deployed, each native app will reference that library

次のクラス図は、レイヤーごとにグループ化されたクラスを示しています。 この SQLiteConnection クラスは、Sqlite-NET パッケージの定型コードです。 残りのクラスは Tasky のカスタム コードです。 TaskItemManager および TaskItem クラスは、プラットフォーム固有のアプリケーションに公開されている API を表します。

The TaskItemManager and TaskItem classes represent the API that is exposed to the platform-specific applications

名前空間を使用してレイヤーを分離すると、各レイヤー間の参照を管理するのに役立ちます。 プラットフォーム固有のプロジェクトには、ビジネス レイヤー用の using ステートメントを含めることのみ必要になります。 データ アクセス レイヤーとデータ レイヤーは、ビジネス レイヤーの TaskItemManager で公開される API によってカプセル化する必要があります。

リファレンス

ポータブル クラス ライブラリは、それぞれがプラットフォームとフレームワークの機能に対してさまざまなレベルのサポートを持つ、複数のプラットフォームで使用できる必要があります。 そのため、使用できるパッケージとフレームワーク ライブラリには制限があります。 たとえば、Xamarin.iOS は c# dynamic キーワードをサポートしていないため、ポータブル クラス ライブラリでは、動的コードに依存するパッケージは使用できません。このことは、そのようなコードが Android で動作する場合も同様です。 Visual Studio for Mac では互換性のないパッケージや参照を追加できませんが、後で予期しない事態が発生するのを避けるために、制限事項を念頭に置いておく必要があります。

注: プロジェクトが、まだ使用していないフレームワーク ライブラリを参照していることに気づくでしょう。 これらの参照は、Xamarin プロジェクト テンプレートの一部として含まれています。 アプリがコンパイルされると、参照されていないコードがリンク プロセスによって削除されるため、System.Xml が参照されている場合でも、XML 関数を使用していないため、それは最終的なアプリケーションには含まれません。

データ レイヤー (DL)

データ レイヤーには、データベース、フラット ファイル、他のメカニズムなどにデータの物理な保存を行うコードが含まれています。 Tasky データ レイヤーは、SQLite-NET ライブラリと、それを接続するために追加されるカスタム コードの 2 つの部分で構成されます。

Tasky は、Sqlite-net NuGet パッケージ (Frank Krueger によって公開) を使用して、オブジェクト リレーショナル マッピング (ORM) データベース インターフェイスを提供する SQLite-NET コードを埋め込みます。 TaskItemDatabase クラスは、SQLiteConnection を継承し、SQLite に対するデータの読み取りと書き込みに必要な Create、Read、Update、Delete (CRUD) メソッドを追加します。 これは、他のプロジェクトで再利用できる汎用 CRUD メソッドの単純な定型実装です。

TaskItemDatabase はシングルトンであり、すべてのアクセスが同じインスタンスに対して行われることを保証します。 複数のスレッドからの同時アクセスを防ぐためにロックが使用されます。

Windows Phone 上の SQLite

iOS と Android はどちらもオペレーティング システムの一部として SQLite を搭載していますが、Windows Phone には互換性のあるデータベース エンジンは含まれていません。 3 つのプラットフォームすべてでコードを共有するには、Windows Phone ネイティブ バージョンの SQLite が必要です。 Sqlite 用の Windows Phone プロジェクトの設定の詳細については、ローカル データベースの操作に関する記事を参照してください。

インターフェイスを使用してデータ アクセスを一般化する

データ レイヤーは、主キーを必要とする抽象データ アクセス メソッドを実装できるように、BL.Contracts.IBusinessIdentity への依存関係を持っています。 これにより、このインターフェイスを実装するすべてのビジネス レイヤー クラスは、データ レイヤーに永続化できます。

このインターフェイスは、主キーとして機能する整数プロパティを指定するだけです。

public interface IBusinessEntity {
    int ID { get; set; }
}

基底クラスはこのインターフェイスを実装し、SQLite-NET 属性を追加して、自動的にインクリメントされる主キーとしてそれをマークします。 これにより、この基底クラスを実装するビジネス レイヤー内のすべてのクラスは、データ レイヤーに永続化できます。

public abstract class BusinessEntityBase : IBusinessEntity {
    public BusinessEntityBase () {}
    [PrimaryKey, AutoIncrement]
    public int ID { get; set; }
}

以下の GetItem<T> メソッドは、このインターフェイスを使用するデータ レイヤーのジェネリック メソッドの例です。

public T GetItem<T> (int id) where T : BL.Contracts.IBusinessEntity, new ()
{
    lock (locker) {
        return Table<T>().FirstOrDefault(x => x.ID == id);
    }
}

同時アクセスを防ぐためのロック

データベースへの同時アクセスを防ぐために、ロックTaskItemDatabase クラス内に実装されています。 これは、異なるスレッドからの同時アクセスが確実にシリアル化されるようにするためです (そうしないと、バックグラウンド スレッドがデータベースを更新しているときに、同時に UI コンポーネントがデータベースの読み取りを試みる可能性があります)。 ロックの実装方法の例をこちらに示します。

static object locker = new object ();
public IEnumerable<T> GetItems<T> () where T : BL.Contracts.IBusinessEntity, new ()
{
    lock (locker) {
        return (from i in Table<T> () select i).ToList ();
    }
}
public T GetItem<T> (int id) where T : BL.Contracts.IBusinessEntity, new ()
{
    lock (locker) {
        return Table<T>().FirstOrDefault(x => x.ID == id);
    }
}

ほとんどのデータ レイヤー コードは、他のプロジェクトで再利用できます。 レイヤー内の唯一のアプリケーション固有のコードは、TaskItemDatabase コンストラクターの CreateTable<TaskItem> 呼び出しです。

データ アクセス レイヤー (DAL)

TaskItemRepository クラスは、TaskItem オブジェクトの作成、削除、取得、更新を可能にする厳密に型指定された API を使用してデータ ストレージ メカニズムをカプセル化します。

条件付きコンパイルの使用

このクラスは、条件付きコンパイルを使用してファイルの場所を設定します - これは、プラットフォームの相違を実装する例です。 パスを返すプロパティは、各プラットフォーム上で異なるコードにコンパイルされます。 コードとプラットフォーム固有のコンパイラ ディレクティブをこちらに示します。

public static string DatabaseFilePath {
    get {
        var sqliteFilename = "TaskDB.db3";
#if SILVERLIGHT
        // Windows Phone expects a local path, not absolute
        var path = sqliteFilename;
#else
#if __ANDROID__
        // Just use whatever directory SpecialFolder.Personal returns
        string libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); ;
#else
        // we need to put in /Library/ on iOS5.1+ to meet Apple's iCloud terms
        // (they don't want non-user-generated data in Documents)
        string documentsPath = Environment.GetFolderPath (Environment.SpecialFolder.Personal); // Documents folder
        string libraryPath = Path.Combine (documentsPath, "..", "Library"); // Library folder
#endif
        var path = Path.Combine (libraryPath, sqliteFilename);
                #endif
                return path;
    }
}

プラットフォームに応じて、出力は “<app path>/Library/TaskDB.db3” (iOS の場合)、“<app path>/Documents/TaskDB.db3” (Android の場合)、または単に “TaskDB.db3” (Windows Phone の場合) になります。.

ビジネス レイヤー (BL)

ビジネス レイヤーは、モデル クラスとファサードを実装してそれらを管理します。 Tasky では、Model は TaskItem クラスであり、TaskItemManager は、TaskItems を管理するための API を提供するファサード パターンを実装します。

ファサード

TaskItemManager は、アプリケーションおよび UI レイヤーによって参照される Get、Save、Delete メソッドを提供するために DAL.TaskItemRepository をラップします。

ビジネス ルールとロジックは、必要に応じてここに配置されます。たとえば、オブジェクトを保存する前に満たす必要がある検証規則などです。

プラットフォーム固有コード用の API

共通コードが記述されたら、それによって公開されるデータを収集して表示するためのユーザー インターフェイスを構築する必要があります。 TaskItemManager クラスは、ファサード パターンを実装して、アプリケーション コードがアクセスする単純な API を提供します。

各プラットフォーム固有のプロジェクトで記述されたコードは、通常、そのデバイスのネイティブ SDK に緊密に結合され、TaskItemManager によって定義された API を使用して共通コードにのみアクセスします。 これには、それが公開するメソッドとビジネス クラスが含まれます (例: TaskItem)。

画像はプラットフォーム間で共有されず、各プロジェクトに個別に追加されます。 このことは重要です。各プラットフォームで異なるファイル名、ディレクトリ、解像度を使用し、画像の処理方法が異なるためです。

残りのセクションでは、Tasky UI のプラットフォーム固有の実装の詳細について説明します。

iOS アプリ

データの保存と取得のために共通の PCL プロジェクトを使用して iOS Tasky アプリケーションを実装するために必要なクラスはほんの一部です。 完全な iOS Xamarin.iOS プロジェクトを以下に示します。

iOS project is shown here

この図では、各クラスがレイヤーにグループ化されています。

The classes are shown in this diagram, grouped into layers

リファレンス

iOS アプリは、プラットフォーム固有の SDK ライブラリを参照します。たとえば、 Xamarin.iOS や MonoTouch.Dialog-1 などです。

また、TaskyPortableLibrary PCL プロジェクトも参照する必要があります。 参照リストをこちらに示します。

The references list is shown here

アプリケーション レイヤーとユーザー インターフェイス レイヤーは、これらの参照を使用して、このプロジェクトに実装されます。

アプリケーション レイヤー (AL)

アプリケーション レイヤーには、PCL によって公開されるオブジェクトを UI に "バインド" するために必要なプラットフォーム固有のクラスが含まれています。 iOS 固有のアプリケーションには、タスクの表示に役立つ次の 2 つのクラスがあります。

  • EditingSource – このクラスは、タスクの一覧をユーザー インターフェイスにバインドするために使用されます。 MonoTouch.Dialog がタスク一覧に使用されていたため、このヘルパーを実装して、UITableView でスワイプして削除する機能を有効にする必要があります。 スワイプして削除は iOS では一般的ですが、Android や Windows Phone ではそうではないため、iOS 固有のプロジェクトがそれを実装する唯一のプロジェクトです。
  • TaskDialog – このクラスは、単一のタスクを UI にバインドするために使用されます。 これは、MonoTouch.Dialog リフレクション API を使用して、入力画面が正しくフォーマットされるように、正しい属性を含むクラスで TaskItem オブジェクトを "ラップ" します。

TaskDialog クラスは MonoTouch.Dialog 属性を使用して、クラスのプロパティに基づいてスクリーンを作成します。 クラスは次のようになります。

public class TaskDialog {
    public TaskDialog (TaskItem task)
    {
        Name = task.Name;
        Notes = task.Notes;
        Done = task.Done;
    }
    [Entry("task name")]
    public string Name { get; set; }
    [Entry("other task info")]
    public string Notes { get; set; }
    [Entry("Done")]
    public bool Done { get; set; }
    [Section ("")]
    [OnTap ("SaveTask")]    // method in HomeScreen
    [Alignment (UITextAlignment.Center)]
    public string Save;
    [Section ("")]
    [OnTap ("DeleteTask")]  // method in HomeScreen
    [Alignment (UITextAlignment.Center)]
    public string Delete;
}

OnTap 属性にはメソッド名が必要なことに注意してください。これらのメソッドは、MonoTouch.Dialog.BindingContext が作成されるクラス (ここでは、次のセクションで説明する HomeScreen クラス) に存在する必要があります。

ユーザー インターフェイス レイヤー (UI)

ユーザー インターフェイス レイヤーは、次のクラスで構成されます。

  1. AppDelegate – アプリケーションで使用されるフォントと色のスタイルを設定するための Appearance API への呼び出しが含まれています。 Tasky は単純なアプリケーションであるため、FinishedLaunching で実行されている他の初期化タスクはありません。
  2. スクリーン – 各スクリーンとその動作を定義する UIViewController のサブクラス。 スクリーンは、UI をアプリケーション レイヤーのクラスと共通 API (TaskItemManager) に結び付けます。 この例では、スクリーンはコードで作成されていますが、Xcode の Interface Builder またはストーリーボード デザイナーを使用して設計されている可能性があります。
  3. 画像 – ビジュアル要素は、すべてのアプリケーションの重要な部分です。 Tasky にはスプラッシュ スクリーンとアイコン画像があり、iOS の場合は、通常および Retina の解像度で提供する必要があります。

ホーム画面

ホーム画面は、SQLite データベースのタスクの一覧を表示する MonoTouch.Dialog スクリーンです。 これは、DialogViewController を継承し、表示用に TaskItem オブジェクトのコレクションを含むように Root を設定するコードを実装します。

It inherits from DialogViewController and implements code to set the Root to contain a collection of TaskItem objects for display

タスク一覧の表示と操作に関連する 2 つのメイン メソッドは次のとおりです。

  1. PopulateTable – ビジネス レイヤーの TaskManager.GetTasks メソッドを使用して、表示する TaskItem オブジェクトのコレクションを取得します。
  2. Selected – 行がタッチされると、新しいスクリーンにタスクが表示されます。

[タスクの詳細] 画面

[タスクの詳細] は、タスクを編集したり削除したりできるようにする入力画面です。

Tasky では、MonoTouch.Dialog のリフレクション API を使用してスクリーンを表示するため、UIViewController の実装はありません。 代わりに、HomeScreen クラスがアプリケーション レイヤーの TaskDialog クラスを使用して、DialogViewController のインスタンスを作成して表示します。

このスクリーンショットは、[名前][メモ] フィールドの透かしテキストを設定する Entry 属性を示す空のスクリーンを示しています。

This screenshot shows an empty screen that demonstrates the Entry attribute setting the watermark text in the Name and Notes fields

[タスクの詳細] 画面の機能 (タスクの保存や削除など) は、HomeScreen クラスに実装する必要があります。MonoTouch.Dialog.BindingContext が作成される場所がここであるためです。 次の HomeScreen メソッドは、[タスクの詳細] 画面をサポートしています。

  1. ShowTaskDetails – スクリーンをレンダリングする MonoTouch.Dialog.BindingContext を作成します。 これは、リフレクションを使用して入力画面を作成し、TaskDialog クラスからプロパティ名と型を取得します。 入力ボックスの透かしテキストなどの追加情報は、プロパティの属性を使用して実装されます。
  2. SaveTask – このメソッドは、OnTap 属性を介して TaskDialog クラスで参照されます。 これは、[保存] が押されると呼び出され、MonoTouch.Dialog.BindingContext を使用してユーザーが入力したデータを取得してから、TaskItemManager を使用して変更を保存します。
  3. DeleteTask – このメソッドは、OnTap 属性を介して TaskDialog クラスで参照されます。 これは、TaskItemManager を使用して、主キー (ID プロパティ) を使ってデータを削除します。

Android アプリ

完全な Xamarin.Android プロジェクトを以下に示します。

Android project is pictured here

クラス ダイアグラム。クラスがレイヤー別にグループ化されています。

The class diagram, with classes grouped by layer

リファレンス

Android アプリ プロジェクトは、Android SDK のクラスにアクセスするために、プラットフォーム固有の Xamarin.Android アセンブリを参照する必要があります。

また、共通データとビジネス レイヤー コードにアクセスするために、PCL プロジェクト (たとえば、TaskyPortableLibrary) も参照する必要があります。

TaskyPortableLibrary to access the common data and business layer code

アプリケーション レイヤー (AL)

前に確認した iOS バージョンと同様に、Android バージョンのアプリケーション レイヤーには、コアによって公開されたオブジェクトを UI に "バインド" するために必要なプラットフォーム固有のクラスが含まれています。

TaskListAdapter – オブジェクトの List<T> を表示するには、ListView にカスタム オブジェクトを表示するアダプターを実装する必要があります。 アダプターは、リスト内の各項目にどのレイアウトを使用するかを制御します。ここでは、コードは Android の組み込みレイアウト SimpleListItemChecked を使用します。

ユーザー インターフェイス (UI)

Android アプリのユーザー インターフェイス レイヤーは、コードと XML マークアップの組み合わせです。

  • リソース/レイアウト – AXML ファイルとして実装されたスクリーン レイアウトと行セルのデザイン。 AXML は、手動で記述することも、Android 用 Xamarin UI デザイナーを使用して視覚的にレイアウトすることもできます。
  • リソース/ドローアブル – 画像 (アイコン) とカスタム ボタン。
  • スクリーン – 各スクリーンとその動作を定義する Activity サブクラス。 UI をアプリケーション レイヤーのクラスと共通 API (TaskItemManager) に結び付けます。

ホーム画面

ホーム画面は、Activity サブクラス HomeScreen と、レイアウト (ボタンとタスク リストの位置) を定義する HomeScreen.axml ファイルで構成されます。 画面は次のようになります。

The screen looks like this

ホーム画面のコードは、ボタンをクリックしてリスト内の項目をクリックするためと、OnResume メソッド内のリストを設定するため ([タスクの詳細] 画面で行われた変更が反映されるように) のハンドラーを定義します。 データは、ビジネス レイヤーの TaskItemManager と、アプリケーション レイヤーの TaskListAdapter を使用して読み込まれます。

[タスクの詳細] 画面

[タスクの詳細] 画面も、Activity サブクラスと AXML レイアウト ファイルで構成されます。 レイアウトは入力コントロールの場所を決定し、C# クラスは TaskItem オブジェクトを読み込んで保存するための動作を定義します。

The class defines the behavior to load and save TaskItem objects

PCL ライブラリへのすべての参照は、TaskItemManager クラスを介して行われます。

Windows Phone アプリ

完全な Windows Phone プロジェクトは次のとおりです。

Windows Phone App The complete Windows Phone project

次の図は、レイヤーにグループ化されたクラスを示しています。

This diagram presents the classes grouped into layers

リファレンス

プラットフォーム固有のプロジェクトは、有効な Windows Phone アプリケーションを作成するために必要なプラットフォーム固有のライブラリ (Microsoft.PhoneSystem.Windows など) を参照する必要があります。

また、TaskItem クラスとデータベースを利用するために、PCL プロジェクト (例: TaskyPortableLibrary) を参照する必要もあります。

TaskyPortableLibrary to utilize the TaskItem class and database

アプリケーション レイヤー (AL)

ここでも、iOS と Android のバージョンと同様に、アプリケーション レイヤーは、ユーザー インターフェイスにデータをバインドするのに役立つ非ビジュアル要素で構成されています。

ViewModel

ViewModel は PCL (TaskItemManager) のデータをラップし、Silverlight/XAML データ バインディングで使用できる方法でそれを表示します。 これは、プラットフォーム固有の動作の例です (クロスプラットフォーム アプリケーションに関するドキュメントで説明しています)。

ユーザー インターフェイス (UI)

XAML には、マークアップで宣言して、オブジェクトを表示するために必要なコードの量を減らすことができる固有のデータ バインディング機能があります。

  1. ページ – XAML ファイルとそのコードビハインドは、ユーザー インターフェイスを定義し、ViewModel と PCL プロジェクトを参照してデータを表示および収集します。
  2. 画像 – スプラッシュ スクリーン、背景およびアイコンの画像は、ユーザー インターフェイスの重要な部分です。

MainPage

MainPage クラスは、XAML のデータ バインディング機能を使用してデータを表示するために TaskListViewModel を使用します。 ページの DataContext は、ビュー モデルに設定され、非同期的に設定されます。 XAML の {Binding} 構文によって、データの表示方法が決まります。

TaskDetailsPage

各タスクは、TaskDetailsPage.xaml で定義されている XAML に TaskViewModel をバインドすることによって表示されます。 タスク データは、ビジネス レイヤーの TaskItemManager を介して取得されます。

結果

結果として得られるアプリケーションは、各プラットフォームで次のようになります。

iOS

このアプリケーションは、[追加] ボタンがナビゲーション バーに配置されたり、組み込みのプラス (+) アイコンを使用するなど、iOS 標準のユーザー インターフェイス デザインを使用します。 また、既定の UINavigationController の [戻る] ボタンの動作を使用し、テーブルでの "スワイプして削除" をサポートします。

It also uses the default UINavigationController back button behavior and supports swipe-to-delete in the tableIt also uses the default UINavigationController back button behavior and supports swipe-to-delete in the table

Android

Android アプリは、"ティック" の表示が必要な行のための組み込みレイアウトを含む、組み込みコントロールを使用します。 スクリーン上の戻るボタンに加えて、ハードウェア/システムの戻る動作がサポートされています。

The hardware/system back behavior is supported in addition to an on-screen back buttonThe hardware/system back behavior is supported in addition to an on-screen back button

Windows Phone

Windows Phone アプリは標準レイアウトを使用し、上部のナビゲーション バーではなく、スクリーンの下部にアプリ バーを設定します。

The Windows Phone app uses the standard layout, populating the app bar at the bottom of the screen instead of a nav bar at the topThe Windows Phone app uses the standard layout, populating the app bar at the bottom of the screen instead of a nav bar at the top

まとめ

このドキュメントでは、iOS、Android、Windows Phone の 3 つのモバイル プラットフォームでコードの再利用を容易にするために、レイヤー化されたアプリケーション設計の原則が単純なアプリケーションにどのように適用されているかについて詳しく説明しました。

また、アプリケーション レイヤーの設計に使用されるプロセスについて説明し、各レイヤーに実装されているコードと機能について紹介しました。

このコードは github からダウンロードできます。