.NET Upgrade Assistant を使って Xamarin.Forms アプリケーションを .NET MAUI へ移行してみた

2022.09.04

いわさです。

先日、Xamarin.Forms アプリケーションを手動で .NET MAUI へ移行しました。

なかなか移行手順が多く苦戦しましたが、公式の移行方法紹介ページには .NET Upgrade Assistant を使うことで Xamarin.Forms からの .NET MAUI の移行を補助してくれる機能があるとのこと。
まだこれからの機能だとは思いますが、今回はこちらを試してみました。

この記事では本日時点の開発版である0.4.345202を対象に検証しています

.NET Upgrade Assistant

.NET Upgrade Assistant は Xamarin.Forms を .NET MAUI へ移行する専用のツールではなく、様々なプロジェクトタイプを対象に .NET Framework アプリケーションを .NET へ移行することが出来るツールです。

ASP.NET や WPF を初め様々なプラットフォームが対象ですが、Xamarin.Forms もサポートされています。

.NET Upgrade Assistant インストール

.NET Upgrade Assistant は dotnet tool としてインストールして利用します。
本日時点の安定版の最新バージョンは0.4.336902でした。

C:\>dotnet tool install --prerelease -g upgrade-assistant
次のコマンドを使用してツールを呼び出せます。upgrade-assistant
ツール 'upgrade-assistant' (バージョン '0.4.336902') が正常にインストールされました。

なお、ツールが Visual Studio (MSBuild) に依存しているため macOS の場合はインストールは出来ますが、利用が出来ません。

% upgrade-assistant --version
This tool is not supported on non-Windows platforms due to dependencies on Visual Studio.

開発版をインストール

どうやら本日時点の最新バージョンでは Xamarin.Forms からの .NET MAUI 化は未サポートのようです。
ツールの実行自体は出来るのですが、共通プロジェクトが変換対象外だったり、Android/iOS 側のプロジェクトも適切な変換がされていなかったりという状況でした。

変換後の Android 側の csproj ファイル

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <AndroidApplication>True</AndroidApplication>
    <AndroidResgenFile>Resources\Resource.designer.cs</AndroidResgenFile>
    <AndroidResgenClass>Resource</AndroidResgenClass>
    <AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
    <MonoAndroidResourcePrefix>Resources</MonoAndroidResourcePrefix>
    <MonoAndroidAssetsPrefix>Assets</MonoAndroidAssetsPrefix>
    <AndroidUseLatestPlatformSdk>false</AndroidUseLatestPlatformSdk>
    <AndroidEnableSGenConcurrent>true</AndroidEnableSGenConcurrent>
    <AndroidUseAapt2>true</AndroidUseAapt2>
    <AndroidHttpClientHandlerType>Xamarin.Android.Net.AndroidClientHandler</AndroidHttpClientHandlerType>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
    <AndroidLinkMode>None</AndroidLinkMode>
  </PropertyGroup>
  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
    <AndroidManagedSymbols>true</AndroidManagedSymbols>
    <AndroidUseSharedRuntime>false</AndroidUseSharedRuntime>
  </PropertyGroup>
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.336902">
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
  <ItemGroup>
    <ProjectReference Include="..\hoge0903xam\hoge0903xam.csproj" />
  </ItemGroup>
</Project>

上記のように、一応は .NET SDK スタイルに変換はされていますが、ターゲットフレームワークがnet6.0-androidではなかったり、UseMauiも設定されていません。

Choose a command:
   1. Apply next step (Select project to upgrade)
   2. Skip next step (Select project to upgrade)
   3. See more step details
   4. Configure logging
   5. Exit
> 1
[16:59:48 INF] Applying upgrade step Select project to upgrade
Here is the recommended order to upgrade. Select enter to follow this list, or input the project you want to start with.
   [Completed] hoge0903xam
   1. hoge0903xam.Android
>

さらに、共通プロジェクトも初めからCompletedとなっており、変換対象外となります。

試してみたところ、開発版であれば .NET MAUI への変換がサポートされているようですので本日はこちらを使います。
インストール時に別のパッケージソースからインストールします。

C:\>dotnet tool install -g upgrade-assistant --add-source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json
次のコマンドを使用してツールを呼び出せます。upgrade-assistant
ツール 'upgrade-assistant' (バージョン '0.4.345202') が正常にインストールされました。

0.4.345202がインストールされました。
こちらの場合だと .NET MAUI のための変換が機能するので、この記事ではこちらのバージョンを利用してみます。

変換してみる

アプリケーションは前回と同様に Xamarin.Forms のポップアップテンプレートを使用します。
今回は Android だけを対象に確認を行っています。

変換は以下のようにupgrade-assistant upgradeコマンドを使用します。
コマンドのパラメータはソリューションファイルかプロジェクトファイルを指定する形です。

C:\>upgrade-assistant upgrade hoge0903xam.sln
-----------------------------------------------------------------------------------------------------------------
Microsoft .NET Upgrade Assistant v0.4.336902+3a2177acaa53d719e5da4ccde275f84ff18f0611

We are interested in your feedback! Please use the following link to open a survey: https://aka.ms/DotNetUASurvey
-----------------------------------------------------------------------------------------------------------------

[16:55:07 INF] Loaded 8 extensions

Telemetry
----------
The .NET tools collect usage data in order to help us improve your experience. The data is collected by Microsoft and shared with the community. You can opt-out of telemetry by setting the DOTNET_UPGRADEASSISTANT_TELEMETRY_OPTOUT environment variable to '1' or 'true' using your favorite shell.

Read more about Upgrade Assistant telemetry: https://aka.ms/upgrade-assistant-telemetry
Read more about .NET CLI Tools telemetry: https://aka.ms/dotnet-cli-telemetry

[16:55:07 INF] Using MSBuild from C:\Program Files\dotnet\sdk\6.0.400\
[16:55:07 INF] Using Visual Studio install from C:\Program Files\Microsoft Visual Studio\2022\Community [v17]
[16:55:09 INF] Initializing upgrade step Select an entrypoint

Upgrade Steps

1. [Next step] Select an entrypoint
2. Select project to upgrade

Choose a command:
   1. Apply next step (Select an entrypoint)
   2. Skip next step (Select an entrypoint)
   3. See more step details
   4. Configure logging
   5. Exit
>

なお、upgrade-assistant analyzeを使うと変換は行わずに分析まで行ってくれます。
JSON 形式での出力になりますが、別途 Visual Studio 拡張のMicrosoft SARIF Viewer 2022をインストールのうえ出力ファイルを読み込むことで、変換対応が必要な箇所を Visual Studio の GUI 上でリストすることが出来ます。

この記事ではupgradeについて試していきます。
流れとしては、まずエントリポイントとなるプロジェクトを選択します。
Xamarin.Forms の場合だと Android あるいは iOS プロジェクトとなります。

[16:57:46 INF] Applying upgrade step Select an entrypoint
Please select the project you run. We will then analyze the dependencies and identify the recommended order to upgrade projects.
   1. hoge0903xam
   2. hoge0903xam.Android
> 2
[16:58:55 INF] Upgrade step Select an entrypoint applied successfully
Please press enter to continue...

続いて、変換対象のプロジェクトを選択します。
共通プロジェクト → Android プロジェクトの順番で変換しました。

その後も対話形式で変換内容を指定していきますがすべて1. Apply next stepを選択しています。(非対話形式のオプションもあり)
流れに沿うだけで特に指定するオプションもないので抜粋ですが、例えば共通プロジェクトだと以下の内容をツールで変換してくれています。

1. [Complete] Back up project
2. [Complete] Convert project file to SDK style
3. [Complete] Add TargetFramework for .NET MAUI Project
4. [Complete] Clean up NuGet package references
    a. [Complete] Duplicate reference analyzer
    b. [Complete] Package map reference analyzer
        1. [Complete] Remove package 'Xamarin.Forms'
        2. [Complete] Remove package 'Xamarin.Essentials'
    c. [Complete] Target compatibility reference analyzer
    d. [Complete] Upgrade assistant reference analyzer
        1. [Complete] Add package 'Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers'
    e. [Complete] Windows Compatibility Pack Analyzer
    f. [Complete] MyDotAnalyzer reference analyzer
    g. [Complete] Newtonsoft.Json reference analyzer
    h. [Complete] Windows App SDK package analysis
    i. [Complete] Transitive reference analyzer
5. [Complete] Update TFM
6. [Complete] Update NuGet Packages
    a. [Complete] Duplicate reference analyzer
    b. [Complete] Package map reference analyzer
        1. [Complete] Remove package 'Xamarin.Forms'
        2. [Complete] Remove package 'Xamarin.Essentials'
    c. [Complete] Target compatibility reference analyzer
    d. [Complete] Upgrade assistant reference analyzer
    e. [Complete] Windows Compatibility Pack Analyzer
    f. [Complete] MyDotAnalyzer reference analyzer
    g. [Complete] Newtonsoft.Json reference analyzer
    h. [Complete] Windows App SDK package analysis
    i. [Complete] Transitive reference analyzer
7. [Complete] Add Project Properties for .NET MAUI Project
8. [Complete] Add template files
9. [Complete] Update WCF service to CoreWCF (Preview)
10. [Complete] Update source code
    a. [Complete] Apply fix for UA0002: Types should be upgraded
    b. [Complete] Apply fix for UA0012: 'UnsafeDeserialize()' does not exist
    c. [Complete] Apply fix for UA0014: .NET MAUI projects should not reference Xamarin.Forms namespaces
    d. [Complete] Apply fix for UA0015: .NET MAUI projects should not reference Xamarin.Essentials namespaces
11. [Next step] Move to next project

出力された Android プロジェクトファイルを確認してみましょう。
先程の安定版とは違って、ターゲットフレームワークなどが適切なものになっていますね。

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <ImplicitUsings>enable</ImplicitUsings>
    <UseMaui>true</UseMaui>
  </PropertyGroup>
  <PropertyGroup>
    <TargetFramework>net6.0-android</TargetFramework>
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\hoge0904xam3\hoge0904xam3.csproj" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Microsoft.DotNet.UpgradeAssistant.Extensions.Default.Analyzers" Version="0.4.336902">
      <PrivateAssets>all</PrivateAssets>
    </PackageReference>
  </ItemGroup>
</Project>

さらに、安定版では追加されていなかった、MauiProgram.csMainApplication.csなども追加対応されています。

ビルドして手動対応してみる

ツールによって SDK スタイルにプロジェクトが変換されていたり、.NET MAUI で必要なクラスが追加されていることが確認出来ました。
しかし、ツールの変換のみではまだ実行は出来ず手動での修正も必要です。

変換対象のプロジェクトによって作業量は変わると思いますが、私が対応した内容を紹介したいと思います。

まず変換した場合は Xamarin.Forms のころの自動生成ファイルを参照したままで以下のようなエラーが消えない場合がありますので、クリーンビルドを行うようにしています。

XAML ファイルの変換

手動対応の際にはいくつかの Xamarin.Forms 名前空間を .NET MAUI のものに変換しました。 ツールでは cs ファイルのusing Xamarin.Formsなどは自動変換してくれるようでしたが、XAML ファイルまでは変換してくれないようでした。

どうやら XAML の変換はまだ未サポートのようですね。

XAML ファイルのxmlns="http://xamarin.com/schemas/2014/forms"xmlns="http://schemas.microsoft.com/dotnet/2021/maui"に置換します。
ここは機械的な対応で済むと思うので将来的にツールがやってくれそうな気がします。

また、前回手動対応した際にはclr-namespace:Xamarin.Forms.PlatformConfiguration.iOSSpecific;を使っている箇所も手動変換が必要でしたが今回も同様に手動修正が必要です。

using を省略してクラス利用している部分

ここも前回と同様でXamarin.Forms.Shellを指定している箇所が全く同じ状態でエラーになっていました。
Shellのみに変換します。

Assembly.cs, Xamarin.Android.Resource.designer.cs のコメントアウト

ここも前回手動対応した部分です。
ソースコードの更新は本日時点ではusing Xamarin.Formsの置換とusing Xamarin.Essentialsの削除のみ対応しているようです。

ここでは前回同様に Assembly.cs も Xamarin.Android.Resource.designer.cs をコメントアウトしました。
ただし、前回も触れましたが Resource.designer.cs についてはおそらく正しい対応ではないと思います。

MainActivity.cs の修正

MainActivity.cs は変換対象外のようでした。
前回と同様に以下のように修正しています。

MainActivity.cs

using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;

namespace hoge0904xamhoge.Droid
{
    [Activity(Label = "hoge0904xamhoge", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
    //public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    public class MainActivity : MauiAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            //Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            //global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            //LoadApplication(new App());
        }
        //public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        //{
        //    Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

        //    base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        //}
    }
}

試してないですが、おそらく iOS でもAppDelegate.csの手動修正が必要なのではないかなと思います。

実行

サイドパーテンプレートの場合はここまでで .NET MAUI として実行することが出来ました。

前回の全て手動対応した部分から考えると以下が自動化されたかなという感じです。

  • プロジェクトファイルの変換
  • NuGet パッケージ周りの削除・置換
  • cs ファイルのusingの置換
  • MauiProgram.csMainApplication.csなどの必須クラスの追加

さいごに

本日は .NET Upgrade Assistant を使って Xamarin.Forms アプリケーションを .NET MAUI へ移行してみました。

まだ開発版ということもありツールで自動化されるサポート範囲が狭いですが個人的に一番触りたくなかったプロジェクト変換をしてくれるだけでも結構ありがたいなと思いました。
完全な自動化はさすがに難しいだろうなとは思っていますが、どこまでこのツールがサポートしてくれるようになるのか今後のアップデートをチェックしていきたいと思います。