Knowlbo 開発者ブログ

株式会社Knowlboの開発者ブログです。

「Visual Studio for Mac」でXamarin Forms + ASP.NET Coreシステムを作る

Knowlbo開発部の醍醐と申します。

弊社開発部にて技術ブログを始める事になりましたので、個人的に気になっている技術、業務上得た知識や経験等々、ご紹介させていただければと思います。

Connect(); //2016が開催されました

先日、日本時間 11/16深夜 に行われた開発者向けオンラインイベント「 Connect(); // 2016」にて数々の新技術の発表が行われました。Connect()というイベントは昨年も含め、開発者にとって大きな、そして大変興味深い発表がなされる場となっています。

connectevent.microsoft.com

その中での発表の1つとして「Visual Studio for Mac Preview Release」というものがありました。
ここ数年、マイクロソフトはLinuxやMac、iOSやandroidなど、プラットフォームを超えた世界、そしてOSSとの協調に力を注いできています。
Connecti()イベント内でも「any developer / any app / any platform」というキーワードが出されておりました。

Mobile First(スマートフォン)

最近のマイクロソフトのイベントで常に謳われているキーワードとして「cloud first / mobile first」というものがあります。
今や当たり前であり、あえて声高々宣言するのが恥ずかしいかと思われるほど当たり前の事柄となりつつあります。
B to Cでは特に当たり前、B to Bでも1歩遅れつつ mobile first が具現化してきているのではないかと思います。
そんな中、弊社はワークフローシステム(ワークフローEX)という自社サービスをお客様に展開しておりますが、比較的お固めのプロダクトで有ることもあり、mobile first化は十分に行われていませんでした。しかし、弊社社長からも mobile にもっと力を入れるというような発言もあり、モバイル・スマホへの注力も行われていくものと思われます。

Visual Studio for Macで「クライアント(スマホアプリ)とサーバーサイドWebAPI」を実装する

ということで、前置きが長かったのですが、「Visual Studio for Mac」を利用してスマホアプリ+サーバーWebAPIのアプリ開発を行ってみたいと思います。
スマホアプリは「Xamarin Forms」で開発を行い、サーバーWebAPIは「ASP.NET Core」で実装します。

Visual Studio for Mac以前の世界では、MacではXamarin Studioを利用することでXamarinスマホアプリを実装する事ができました。 そして、サーバーWebAPIが必要であれば、Visual Studio Codeを利用して(もしくは別技術を利用して)実装を行う必要がありました。
つまり、Visual Studio for Macの登場により、同一の開発環境によりスマホアプリ側もサーバーWebAPI側も実装する事が可能になったのです(そしてWindowsを使わずにMacだけで)。

こんなアプリを作る

以下がスマホアプリ(Xamarinアプリ)のスクリーンショットです。

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

上部に「Get PRODUCTS!」ボタンが配置されており、これをクリックするとWebAPI呼び出しが行われます。
サーバーサイドのWebAPI(これはASP.NET Coreアプリ)は製品一覧を取得し、この情報をJSON形式でXamarinアプリ側に返却します。
Xamarinアプリは受け取ったJSONデータの内容をリスト形式で画面に表示します。

プロジェクトの構成

ソリューションは「Xamarinアプリ側」「ASP.NET Coreアプリ側」の2つをそれぞれ別々に用意する事とします。
以下が完成したプロジェクトの形です。

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

ASP.NET Core で WebAPIを実装する

1.Visual Studio for Macでプロジェクトを新規作成
New Project を選択します。

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

プロジェクトテンプレートから「.NET Core→App→ASP.NET Core→ASP.NET Core Empty Web Application」を選択します。

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

プロジェクト名・ソリューション名はここでは「FirstStepWeb」としました。場所は「/Users/daigo/Projects/vsMac/」とします。

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

2.NuGetパッケージを追加
「プロジェクトを選択→マウス右ボタンメニュー→追加→NuGetパッケージの追加」から「Microsoft.AspNetCore.Mvc」を追加します。

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

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

これはASP.NET Core MVCの機能によりWebAPIインターフェイスの実装を行う為です。

3.アプリケーション初期化処理の追記
.NET CoreのエントリーポイントはMain()ファンクションです。
Main()ファンクション内で「.UseStartup()」として構成初期化クラスとして明示されたのがStartup.csでの実装です。
Startupクラス内にて、ASP.NET Core MVCを有効にします。

// Startup.cs(ウィザードによって出力された余分なコードは削除しています)
using System;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;

namespace FirstStepWeb
{
  public class Startup
  {
    public void ConfigureServices(IServiceCollection services)
    {
      services.AddMvc();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
      app.UseMvc();
    }
  }
}

4.Productモデルを作成
製品モデルを表すProductクラスを実装するため、Product.csファイルをプロジェクトに追加します。

「プロジェクトを選択→マウス右ボタンメニュー→追加→新しいフォルダー」を選択し「Models」という名称にします。
Modelsフォルダ配下にProduct.csファイルを追加します。

// Models/Product.cs
namespace FirstStepWeb.Models
{
  public class Product
  {
    public int Id { get; set; }
    public string Name { get; set; }
    public string PicUrl { get; set; }
  }
}

5.WebAPIを実装
WebAPIのI/Fを実装する為、ProductController.csを追加します。
先程のModelsと同じ要領で、プロジェクトフォルダー直下に「Controllers」フォルダーを追加し、更にControllersフォルダー配下に「ProductController.cs」ファイルを追加します。

// Contorllers/ProductController.cs
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc;
using FirstStepWeb.Models;

namespace FirstStepWeb.Controllers.Api
{
  [Route("api/product")]
  public class ProductController : Controller
  {
    [HttpGet("list")]
    public IActionResult List()
    {
      return Ok(this.GetProducts());
    }

    // テスト用データを作成するサンプルメソッド
    private List<Product> GetProducts()
    {
      List<Product> result = new List<Product>();

      result.Add(new Product() { Id = 0, Name = "ワークフローEX", PicUrl = "./WorkflowEX.jpg" });
      result.Add(new Product() { Id = 1, Name = "タイムカードEX", PicUrl = "./TimeCardEx.jpg" });
      result.Add(new Product() { Id = 2, Name = "Office View", PicUrl = "./OfficeView.jpg" });
      result.Add(new Product() { Id = 3, Name = "VisitView", PicUrl = "./VisitView.jpg" });

      return result;
    }
  }
}

本実装はサンプルである為、GetProducts()メソッドに固定のサンプルデータを作成するロジックを実装しています。実プロジェクトでは、データベースに対するクエリーであり、サービスクラス・ドメインクラス・DAOクラスもしくはRepositoryクラス等の呼び出しに置き換わります。

また「Rout属性」を付与する事で WebAPI のルーティング定義を行なっています。つまり「api/product/list」というリクエストが行われると、ProductController.GetProducts()メソッドの呼び出しにルーティングが行われます。

「OK()メソッド」に取得したProductリストを渡しています。これによりProductオブジェクトリストが自動的にJSON形式に変換され呼び出し元クライアントにレスポンスとして送信されます。

6.外部IPからの受け口の用意
ここで実装したWebAPIに対して、外部IPからアクセス可能にするように実装を調整します。
自動生成された本プロジェクトは、デフォルトでは「http://localhost:5000」にてHTTPリクエストをホストする構成となっています。
スマホアプリ(ローカルPC外)から、本WebAPIへのHTTPリクエストを受け入れる為に、Program.csに「.UseUrls("http://0.0.0.0:5000")」の1文を追記します。

// Program.cs
using System;
using System.IO;
using Microsoft.AspNetCore.Hosting;

namespace FirstStepWeb
{
  public class Program
  {
    public static void Main(string[] args)
    {
      var host = new WebHostBuilder()
        .UseKestrel()
        .UseContentRoot(Directory.GetCurrentDirectory())
        .UseIISIntegration()
        .UseStartup<Startup>()
        .UseUrls("http://0.0.0.0:5000")
        .Build();

      host.Run();
    }
  }
}

以上でWebAPI側の実装は完了しました。左上の▶︎の実行ボタンをクリックしてみましょう。

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

Buildが行われ、エラーがなければ実行されます。
ブラウザが自動的に起動しますが、「http://localhost:5000/api/product/list」とURLを入れ直してEnterを押すと、以下のように、製品一覧を表すJSONデータが返却されてきます。

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

FirstStepWebは実行したまま、次の作業に入りましょう。

Xamarin Formsスマホアプリを実装する

では次に上記で作成したWebAPI(FirstStepWebプロジェクトにより実装された htp://xxx/api/product/list )を呼び出して、得られたJSONデータを画面にリスト表示するXamarin Fromsアプリケーションを実装します。
実装イメージは本投稿の最初の方で紹介した画面になります。

〜〜〜 Tips 〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
1点、Tipsとして、MacのランチャーやDockからVisual Studio for Macを選択すると、アプリケーションが同時に1つしか起動する事ができません。今回のようにサーバーサイドとクライアントサイドを実装する場合、実行・デバッグの都合上、同時に2つのVisual Studio for Macを起動させたいと思うでしょう。
こんな時。ターミナルから以下のようにコマンドを実行する事で、同一アプリケーションを複数起動する事が可能です。

 open -n "/applications/Visual Studio.app"

〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜

では、新規に2つ目のVisual Studio for Macを起動します。

1.プロジェクトの作成
先程と同じく New Project を選択します。
プロジェクトテンプレートとして「MultiPlatform→App→Xamarin.Forms→Forms App」を選択します。

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

App Nameは「FirstStepXamarin」とします。

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

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

2.NuGetパッケージを追加
必要な機能参照を追加する為、NuGetの追加を行います。

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

Xamarinアプリ側では以下の2つの処理を行う為に、2つのNuGetパッケージの参照追加を行います。

  • 「HTTPによるWebAPI呼び出し」を行う為に「Microsoft.net.http」への参照を追加
  • 「WebAPI呼び出しの結果として得られたJSONデータ」解析する為に「Newtonsoft.json」への参照を追加

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

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

上記はいずれもXamarin FormsプロジェクトへのNuGet参照追加です。

3.モデルクラスへのリンクの追加
モデルクラス(Productクラス)へのリンクをプロジェクトに追加します。
サーバーサイドの処理では Productクラス をデータとして扱い、そのJSONイメージをWebAPIの返却値として扱いました。
Xamarinクライアント側でも、得られたJSONデータをProduct型にデシリアライズして扱おうと思います。
その為に、サーバーサイド実装で用意した Product.cs を、Xamarinプロジェクト上でもソースリンクを行う事で共有する事とします。
「プロジェクトを選択→マウス→ボタンメニュー→追加→既存のフォルダーを追加」を選択します。
Modelsフォルダを選択します。

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

プロジェクトに含めるファイルをチェックしてOKをクリックします。

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

「ファイルに対するリンクを追加する」を選択してOKをクリックします。

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

4.UI実装
FirstStepXamarin.xamlを開き以下のように Button と ListView を配置します。

<?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:local="clr-namespace:FirstStepXamarin" x:Class="FirstStepXamarin.FirstStepXamarinPage">
  <StackLayout>
    <Button Text="get products!" Clicked="getProductsClicked" />
    <ListView x:Name="productList" >
      <ListView.ItemTemplate>
        <DataTemplate>
          <TextCell Text="{Binding Name}"
                     Detail="{Binding PicUrl}" />
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackLayout>
</ContentPage>

Xamarin Formsは、WPF→Silverlight→UWPと続く XAML によるUI定義を受け継いでいます。各技術でXAMLのサポート機能の相違はありますが、過去にWindows開発を行ってきた開発者にとっては、感覚的にスマホアプリのUI定義が可能になっています。

5.イベントハンドラメソッドの実装追加
UI上でのボタンクリックイベントハンドラメソッドをコードビハインドファイル(FirstStepXamarinPage.xaml.cs)に実装します。
(サンプルアプリなのでイベントハンドラメソッドにベタ書きします)

// FirstStepXamarinPage.xaml.cs
using Xamarin.Forms;

using System;
using System.Collections.Generic;
using System.Net.Http;

using HelloVsWeb.Models;

using Newtonsoft.Json;

namespace FirstStepXamarin
{
  public partial class FirstStepXamarinPage : ContentPage
  {
    public HelloVsXamarinPage()
    {
      InitializeComponent();
    }

    // get prodcuts!ボタンラメソッド。
    public async void getProductsClicked(object sender, System.EventArgs e)
    {
      // Httpクライアントを作成
      HttpClient client;
      client = new HttpClient();
      client.MaxResponseContentBufferSize = 256000;

      // URLは私の環境に固定化してあります。適時IPを修正してください。
      var uri = new Uri("http://192.168.11.15:5000/api/product/list");

      // HTTPリクエスト
      var response = await client.GetAsync(uri);
      if (response.IsSuccessStatusCode)
      {
        var content = await response.Content.ReadAsStringAsync();
        // 得られたJSONデータを Product 配列にデシリアライズ
        var products = JsonConvert.DeserializeObject<List<Product>>(content);

        // リストビューにデータバインド
        this.productList.ItemsSource = products;
      }
    }
  }
}

6.実行する
では完成したXamarin Fromsアプリケーションを実行してみます。
実行方法は、iOS / Android、それぞれエミュレータ / 実機の選択がありますが、ここではandroidの実機及びiOSエミュレータで実行してみましょう。

まずandroid実機での実行です。
Visual Studio for Macウィンドウの左上から「FirstStepXamarin.droid 」を選択します。つまり、android版の実行を選択するという意味。
続いて、「Asus_Z017DA(API 23)」を選択しています。これは私がUSB接続している実機android端末Zenfone3になります。

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

実行の三角マーク(▶︎)をクリックすると、ビルドから実機へのランタイム及びアプリケーションイメージのデプロイが行われ、実行が行われます。
以下のような画面が実機android端末に表示されるはずです。

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

次にiOSエミュレータで実行します。
先程と同様に左上のUIから「FirstStepXamarin.iOS 」を選択します。適当なエミュレータ端末を選択し、実行の三角マーク(▶︎)をクリックします。

まとめ

数年前、異なる言語やライブラリが氾濫するスマホアプリ開発の世界に、Xamarinは一つの選択肢を提案しました。
マイクロソフトによるXamarin社の買収、フレームワークのOSS化や開発環境のオープンな解放により、Xamarinはより身近なものになりました。
しかしiOSアプリの開発は、Xamarinを利用しても、アップルの方針により必ずMac(Xcodeバックグラウンド)によるビルドを行う必要があります。

今回のVisual Studio for Macの登場により .NET & C# による開発がクライアント(スマホ)アプリに留まらず、サーバーサイドの処理も同様に記述・実装できることは非常に大きなことだったのではないかと考えています。
勿論、従来通り、Window & Visual Studio 2015/2017という選択肢も、いわば「鉄板」の選択肢として残っています。

私たち開発者は、ソフトウェア開発において自由な選択肢をまた1つ得たと言えるのではないかと思います。