Visual Studio 2017 for Mac Community をインストール - Xamarin.Forms アプリを試す

Visual Studio for Mac の正式リリース版を試してみました。

目次

Community 版が正式リリースされていた

昨年 Preview 版が登場した Visual Studio for Mac。

blog.kondoumh.com

今年5月に正式リリースされてたみたいです。ウォッチしてなくて気づいてませんでした。Windows 版同様 Community 版が提供されています。

www.visualstudio.com

インストール

早速ダウンロードしてインストールしてみました。

キャッチーなスプラッシュウィンドウです。

f:id:kondoumh:20170716144830p:plain

フルインストール。

f:id:kondoumh:20170716144933p:plain

進行中。それほど時間かからずに完了します。

f:id:kondoumh:20170716145007p:plain

起動。

f:id:kondoumh:20170716145052p:plain

プロジェクトのテンプレートは Preview 版と変わらない模様。

f:id:kondoumh:20170716145623p:plain

Xamarin.Forms などのプロジェクトで Mobile Backend の プロジェクトを追加して Web API を利用するには、.NET Core を別途インストールする必要があります。 ↓ のページにしたがってインストールします。

www.microsoft.com

事前に openssl を導入し、/user/local/lib にシンボリックリンクを作成する必要があります。あとは、ダウンロードした pkg を展開してインストールします。

f:id:kondoumh:20170716150025p:plain

インストールが終わると、pkg ファイルを削除するか聞いてくれる親切インストーラです。

f:id:kondoumh:20170716150040p:plain

これで、準備完了です。

Xamarin.Forms アプリの作成

ソリューションの作成

Xamarin.Forms アプリのソリューションを作ってみることにしました。Android / iOS にチェックを入れ、アプリの名前を入力。Mobile Backend にもチェックを入れておきます。

f:id:kondoumh:20170716151317p:plain

「作成」ボタンをクリック。

f:id:kondoumh:20170716151559p:plain

プロジェクト構築中。Nuget で何やらいっぱい取得してます。

f:id:kondoumh:20170716151638p:plain

終わりました。

f:id:kondoumh:20170716151717p:plain

おもむろに実行ボタンを押してみます。デフォルトプロジェクトは <AppName>.iOS になっており、エミュレータが起動してアプリが実行されました。

f:id:kondoumh:20170717005749p:plain

Android のプロジェクト (<AppName>.Droid) を実行すると、Android のエミュレータが起動し、同じ Forms アプリが実行されます。

f:id:kondoumh:20170717010152p:plain

ソリューション構成

ソリューション構成をみると、<AppName> プロジェクトにプラットフォーム共通のコードが配置され、<AppName>.Droid と <AppName>.iOS にプラットフォーム固有のコードが配置されているようです。

f:id:kondoumh:20170717015216p:plain

プラットフォーム共通のコードは .NET Standard の API のみに依存することになるのでしょう。

docs.microsoft.com

<AppName> プロジェクトの Services には、アプリの CRUD 処理を担うクラスのインターフェース iDataStore が定義され、モック用の実装クラス MockDataStore と REST API を呼び出す実装クラス CloudDateStore が配置されています。デフォルトでは、MockDataStore が使われます。

サービスプロジェクト(<AppName>.MobileAppService) には、サンプルの Controller / Repository / Entity などが配置されています。

Entity のコード。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SampleFormApp.Models
{
    public class Item
    {
        public string Id { get; set; }
        public string Text { get; set; }
        public string Description { get; set; }
    }
}

Repository の インタフェース。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace SampleFormApp.Models
{
    public interface IItemRepository
    {
        void Add(Item item);
        void Update(Item item);
        Item Remove(string key);
        Item Get(string id);
        IEnumerable<Item> GetAll();
    }
}

Controller のコード。REST API の EndPoint になります。

using System;
using Microsoft.AspNetCore.Mvc;

using SampleFormApp.Models;

namespace SampleFormApp.Controllers
{
    [Route("api/[controller]")]
    public class ItemController : Controller
    {

        private readonly IItemRepository _ItemRepository;

        public ItemController(IItemRepository ItemRepository)
        {
            _ItemRepository = ItemRepository;
        }

        // GET: api/values
        [HttpGet]
        public IActionResult List()
        {
            return Ok(_ItemRepository.GetAll());
        }

        [HttpGet("{Id}")]
        public Item GetItem(string id)
        {
            Item item = _ItemRepository.Get(id);
            return item;
        }

        [HttpPost]
        public IActionResult Create([FromBody]Item item)
        {
            try
            {
                if (item == null || !ModelState.IsValid)
                {
                    return BadRequest("Invalid State");
                }

                _ItemRepository.Add(item);

            }
            catch (Exception)
            {
                return BadRequest("Error while creating");
            }
            return Ok(item);
        }

        [HttpPut]
        public IActionResult Edit([FromBody] Item item)
        {
            try
            {
                if (item == null || !ModelState.IsValid)
                {
                    return BadRequest("Invalid State");
                }
                _ItemRepository.Update(item);
            }
            catch (Exception)
            {
                return BadRequest("Error while creating");
            }
            return NoContent();
        }

        [HttpDelete("{Id}")]
        public void Delete(string id)
        {
            _ItemRepository.Remove(id);

        }
    }
}

ASP.NET Web API のコードですね。このプロジェクトを実行すると、localhost でおそらく macOS 版の IISExpress が起動し、アセンブリが配置され、ブラウザに Swagger のページが表示されます。Swagger も統合されているんですね。

f:id:kondoumh:20170717013203p:plain

もちろん、API を試すことができます。

f:id:kondoumh:20170717013303p:plain

Web API との接続

ということで、アプリから REST API 経由でデータを取得したいので、MockDataStore ではなく CloudDataStore で Web API に接続してみます。

<AppName>.MobileAppService の Properties フォルダにある launchSettings.json で URL の設定をします。

{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:61407",
      "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "launchUrl": "api/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "MasterDetail.MobileAppService": {
      "commandName": "Project",
      "launchBrowser": true,
      "launchUrl": "http://localhost:61407/api/values",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

<AppName> の App.xaml.cs で UseMockDateStore を false にして、BackendUrl を設定します。

using System.Collections.Generic;

using Xamarin.Forms;

namespace SampleFormApp
{
    public partial class App : Application
    {
        public static bool UseMockDataStore = false;
        public static string BackendUrl = "http://localhost:61407";

        // 省略
}

Mock じゃないことをわかりやすくするため、Service の Repository の実装クラスのコンストラクタで、2件の データを追加しています。

namespace SampleFormApp.Models
{
    public class ItemRepository : IItemRepository
    {
        private static ConcurrentDictionary<string, Item> _items =
            new ConcurrentDictionary<string, Item>();

        public ItemRepository()
        {
            Add(new Item { Id = Guid.NewGuid().ToString(), Text = "Sharp", Description = "C# is very sharp!" });
            Add(new Item { Id = Guid.NewGuid().ToString(), Text = "dotNet", Description = "dotNet is very cool!" });
         }

iOS アプリを実行してみると、ちゃんと Web API でデータが取得されました。

f:id:kondoumh:20170717012538p:plain

デバッグ

ソリューション構成にクライアントとサーバを含んで同時に実行できるため、クライアントのコードはもちろんのこと、サーバー側のコードにブレークポイントを設定してデバッグすることも可能です。サーバープロセスにアタッチしてデバッグとかしなくていいので楽ですね。

f:id:kondoumh:20170717013538p:plain

デバッグ時にクライアントとサーバをプロジェクトのコンテキストメニューで「項目のデバッグを開始する」から実行するのですが、Windows 版のように同時に実行してくれるような構成方法がわかりませんでした。未実装なのでしょうか。

終わりに

Xamarin.Forms をちょっとだけ触ってみました。Android / iOS で動作するクロスプラットフォームアプリ開発を手軽に開始できます。.NET 開発に慣れた人なら違和感なく入っていけそうです。プロダクションレベルのコードを書いていくと、プラットフォームに依存するコードが増えてマルチプラットフォームサポートは大変だとは思いますが。

本家 Windows 版の Visual Studio と同じようにコード作成、デバッグ可能なので、手持ちのマシンが Mac だけっていう人も C# 書きまくれます。 Xamarin 使わない場合でも ASP.NET アプリの開発も可能ですし。

Visual Studio for Mac が本家 Windows 版と同様、末長く提供されることを祈ります。