VSCodeで.NET MAUIの開発環境構築

# はじめに

株式会社ACCESS Advent Calender 2023の10日目の記事です。

## .NET MAUIについて

.NET MAUI（Multi-platform App UI）は、C#とXAMLを使用してモバイルアプリやデスクトップアプリを開発することができるクロスプラットフォームのフレームワークです。その名の通り、Microsoftの.NETシリーズとして提供されています。

これまではXamarin.Formsという名前で提供されてきましたが、2024年5月1日にサポートが終了し.NETシリーズに統合される予定となっています。

https://dotnet.microsoft.com/ja-jp/platform/support/policy/xamarin

## Visual Studio for Macのサポート終了について

.NET MAUIでiOSやMacOS向けのアプリをビルドするにはmacOSの環境が必要ですが、
公式から提供される開発環境としては、Visual Studio for Macが唯一の手段となっていました。

しかしながら、2023年8月に、MicrosoftからVisual Studio for Macの廃止が発表されました。
サポートの終了する2024年8月31日以降はSDKのアップデートなどが提供されなくなることから、
各OSのバージョンアップなどに追従することができなくなります。

https://devblogs.microsoft.com/visualstudio/visual-studio-for-mac-retirement-announcement/

現時点では、後継手段として

* Windows版Visual Studio
* Visual Studio Code + 拡張機能

の2択が提供されており、macOS上で開発を続けるためには、
実質的に2つ目のVisual Studio Codeの環境を利用する必要があります。

そこで、本記事ではmacOS上でVisual Studio Code + 拡張機能を利用し、
.NET MAUIのアプリケーションの開発環境を構築してみることにします。

公式ドキュメント

https://learn.microsoft.com/en-us/dotnet/maui/get-started/installation?view=net-maui-8.0&tabs=visual-studio-code


# 前提条件

* OS: macOS Sonoma 14.1.2
* CPU: 筆者の環境はIntelですが、Apple Siliconでも同様に構築できるはずです
* 目標: .NET MAUIでAndroid / iOS / Macのネイティブアプリをビルドできるようにします

# 事前準備

アプリをビルドするために必要な各種ツールを予めインストールしておきます。

## Xcode

iOS/iPadOSアプリをビルドするために、Xcodeおよびコマンドラインツールをインストールします。

### Xcode

https://developer.apple.com/jp/xcode/

### コマンドラインツール

以下のコマンドでインストールします。

```sh
$ xcode-select --install
```

## Android Studio

Androidアプリをビルドするためのツールやライブラリをインストールしておきます。

### Android Studio

Android Studioをインストールすることで、Androidアプリのビルドに必要なSDKなどがインストールされます。

https://developer.android.com/studio/install?hl=ja

### OpenJDK

以下のリンクからOpenJDK 11を選択してインストールします。
環境に合ったインストーラーをダウンロードしてください。

https://learn.microsoft.com/ja-jp/java/openjdk/download

インストール後、ターミナル環境変数の`JAVA_HOME`にJDKのホームディレクトリを設定しておきます。
(必要に応じて`.zshrc` などの設定ファイルにexport文を追記してください。)

```
export JAVA_HOME=/Library/Java/JavaVirtualMachines/microsoft-11.jdk/Contents/Home
```

なお、後述でインストールする.NETバージョンによってはOpenJDK 17が必要な場合があります。
.NETのバージョンとJDKの対応については以下のドキュメントを参照してください。
https://github.com/dotnet/maui/wiki/Release-Versions

# .NET / .NET MAUIのインストール

環境に合わせた.NETをインストールします。

https://dotnet.microsoft.com/ja-jp/download

インストーラーを実行するとCLIでdotnetコマンドが実行できるようになります。

本記事公開時の最新バージョンが8.0.100でしたのでそれを前提に解説していますが、
現時点での最新バージョンをインストールされることをお勧めします。

```sh
$ dotnet --version                 
8.0.100
```

次にMAUIの開発環境をインストールします。

```sh
$ sudo dotnet workload install maui maui-android android
```

上記を実行すると、`maui-android`, `maui`, `android`のワークロードがインストールされます。

```sh
$ dotnet workload list

Installed Workload Id      Manifest Version      Installation Source
--------------------------------------------------------------------
maui-android               8.0.3/8.0.100         SDK 8.0.100        
maui                       8.0.3/8.0.100         SDK 8.0.100        
android                    34.0.43/8.0.100       SDK 8.0.100 
```

# VSCode拡張機能のインストール

VSCodeに.NET MAUI拡張をインストールします。

https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-maui

上記をインストールすると、.NET MAUIの他にC#, C# Dev Kitなどの依存ツールも同時にインストールされます。

![スクリーンショット 2023-12-09 16.38.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/da00c222-a65b-2d0e-57d8-e9d396b44b81.png)
![スクリーンショット 2023-12-09 16.38.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/22137ac8-fc7a-6b63-659a-cb1288657cb2.png)

# プロジェクトの作成

次に.NET MAUIのプロジェクトを作成していきます。
今回プロジェクト名などはデフォルトのままとします。

VSCodeのエクスプローラーにある `.NET プロジェクトを作成` からプロジェクトを作ります。

![スクリーンショット 2023-12-09 16.41.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/f3700630-c2f8-fecf-4945-a9f80cbfc739.png)

![スクリーンショット 2023-12-09 16.40.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/80b65b71-118c-623b-c63d-be5128fe2a6d.png)

![スクリーンショット 2023-12-09 16.42.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/d5b46bd5-fe66-6e20-fbd4-40ddfe6620b8.png)

## 作成完了時のディレクトリ構成

正しく作成されれば、以下のようなディレクトリ構成が見えるようになると思います。

![スクリーンショット 2023-12-09 22.48.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/0a35eb6a-9b09-316a-6cb8-a3c39801badb.png)

## 参考: CLIツールを使う場合

```sh
$ dotnet new maui
```

# アプリのデバッグ実行

アプリを実行してデバッグしてみます。

VSCodeの`実行とデバッグ`タブのボタン>`.NET MAUI`を選択すると、
macOS向けの.NET MAUIアプリが立ち上がります。

![スクリーンショット 2023-12-09 16.47.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/b1f224d6-376a-0e1f-6772-5bf3786550e8.png)

![スクリーンショット 2023-12-09 16.48.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/d0a62373-efca-111e-8a73-012b04d3fa2a.png)

![スクリーンショット 2023-12-09 16.54.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/aebf445f-a748-f5a5-0547-07e9ba87273c.png)

この状態であれば、VSCode上でブレークポイントを貼ることもできます。
例えば、`MainPage.xaml.cs`の`OnCounterClicked()`メソッドにブレークポイントを置くと、
`Click me`を押すたびに実行が止まり、コールスタックや変数の値をウォッチすることができます。

![スクリーンショット 2023-12-09 17.06.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/4a9f03fd-2db6-cbb7-8ef4-f2fb4406d381.png)

## デバッグなしで実行したい場合

VSCodeの `実行`>`デバッグなしで実行`から起動します。

![スクリーンショット 2024-01-10 14.43.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/325602bd-d1d9-bc90-3ed8-0164ca38174c.png)

# Androidアプリの実行

コマンドパレット(`⌘+Shift+P`) →`.NET MAUI: スタートアップ デバイスの選択`から、実行したいAndroidエミュレータ(もしくは実機)を選択し、[アプリのデバッグ実行](#アプリのデバッグ実行)と同様の手順で起動します。

![スクリーンショット 2024-01-10 14.41.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/841464f8-e2af-d15e-b45d-6bfcf093b544.png)

なお、起動デバイスはコマンドパレット(`⌘+Shift+P`) →`.NET MAUI: Android デバイスの選択`から、アプリを実行するデバイスからも選択することができます。

## 実行結果

ビルドが完了すると、エミュレータが立ち上がってアプリが起動します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/376b3b66-60cd-b536-0e4b-b402c7e7cafd.png" width=300px/>

## 参考: CLIツールを使う場合

```sh
$ dotnet build -t:Run -f net8.0-android
```

# iOS(iPadOS)アプリの実行

コマンドパレット(`⌘+Shift+P`) →`.NET MAUI: スタートアップ デバイスの選択`から、実行したいiOSシミュレータ(または実機)を選択し、[アプリのデバッグ実行](#アプリのデバッグ実行)と同様の手順で起動します。

![スクリーンショット 2024-01-10 14.34.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/9398191e-c241-c40f-a514-ea0f4b31f286.png)

ビルドが完了すると、シミュレータが立ちあがってアプリが起動します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/b58d2079-de4d-dbe0-45c2-c747280a943c.png" width=300px/>

## 参考: CLIツールを使う場合

```sh
$ dotnet build -t:Run -f net8.0-ios
```

# トラブルシューティング

## Android

### Android SDK: インストールが必要です

![スクリーンショット 2023-12-09 16.42.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189802/d58cbb2d-5711-cc0a-be1e-b1f204965919.png)

VSCodeの画面に上のようなエラーが表示される場合、
**MAUIプロジェクトのディレクトリ**内で以下のコマンドを実行します。
なお、`$ANDROID_HOME`はAndroid SDKのパスです。

```sh
$ dotnet build -t:InstallAndroidDependencies -f:net8.0-android -p:AndroidSdkDirectory=$ANDROID_HOME  -p:AcceptAndroidSDKLicenses=True
```

以下のようなエラーが出た際は、カレントディレクトリがプロジェクト配下にあることを確認してください。

```
MSBuild のバージョン 17.8.3+195e7f5a3 (.NET)
MSBUILD : error MSB1003: プロジェクト ファイルまたはソリューション ファイルを指定してください。現在の作業ディレクトリはプロジェクト ファイルまたはソリューション ファイルを含んでいません。
```

### error XA0010: No available device.

```
$ dotnet build -t:Run -f net8.0-android
...
/usr/local/share/dotnet/packs/Microsoft.Android.Sdk.Darwin/34.0.43/tools/Xamarin.Android.Common.Debugging.targets(678,5): error XA0010: No available device. [/Users/user/MauiApp1.csproj::TargetFramework=net8.0-android]

Build FAILED.
```

上記のエラーが出た場合は、有効なエミュレータが作成されているか(実機を接続している場合はADBに認識されているか)を確認します。

## iOS

### Xcodeのライセンス未同意時エラー

```log
... 
 
  You have not agreed to the Xcode license agreements. Please run 'sudo xcodebuild -license' from within a Terminal window to review and agree to the Xcode and Apple SDKs license.

Exception: System.Xml.XmlException: Root element is missing.
   at System.Xml.XmlTextReaderImpl.Throw(Exception e)
   at System.Xml.XmlTextReaderImpl.ThrowWithoutLineInfo(String res)
   at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
   at System.Xml.XmlReader.ReadToDescendant(String name)
   at Xamarin.MacDev.PropertyListFormat.XmlFormat.StartReading(Stream input) in 
   
...

Build FAILED.
```

Xcodeをインストールしてから一度も起動していないと、iOSアプリのビルドで上記のようなエラーが出る場合があります。
その場合は、下記のコマンドでライセンスを承諾する必要があります。

```sh
$ sudo xcodebuild -license
```

### Failed to locate any simulator runtime matching options

```
$ dotnet build -t:Run -f net8.0-ios               
...
                        <key>description</key>
                        <string>The operation couldn’t be completed. Failed to locate any simulator runtime matching options: {
      BuildVersionString = 21A326;
      Platforms =     (
          "com.apple.platform.iphonesimulator"
      );
      VersionString = "17.0";
  }</string>
...
  ```

Xcodeをインストールしてから一度も起動していないと、
必要なSimuratorなどがインストールされていないために上記のようなエラーが出ます。
Xcodeを一度起動してiOSのSDKやシミュレータをダウンロードしておく必要があります。

### その他のエラー

その他原因不明なエラーが出た場合は、
Xcodeのコマンドラインツールのバージョンなどが正しく指定されているかを確認します。

筆者の環境では古いバージョンのXcodeが共存しており、
コマンドラインツールが最新バージョンのXcodeを向くように設定し直す必要がありました。

```sh
$ sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
```

```sh
$ xcode-select -p
/Applications/Xcode.app/Contents/Developer
```

# おわりに

本記事では、VSCodeで.NET MAUIの開発環境を構築する手順を紹介しました。
.NET MAUIの拡張機能は、本記事の執筆時点ではプレビュー版であり、
今後GAに向けて機能が改善されていくことが期待できます。