6️⃣

Xamarin.Forms プロジェクトを .NET6 へアップグレードする

2021/12/12に公開
2

警告文

本文

この記事は祝 .NET 6 GA!(中略) Advent Calendar 2021 8 日目の記事です。
まあこの書き出しを書いている時点で既に 11 日なんですけどね……


日本時間 2021/11/08 に .NET 6 と Visual Studio 2022 がリリースされましたが、一方で Xamarin の余命宣告がされました。
サポート期限は 2 年後の 2023/11 まで 2024/5/1 まで[1]、それまでにまだリリースもされていない後継フレームワーク MAUI への移行を済ませる必要があります。
https://devblogs.microsoft.com/xamarin/whats-new-in-xamarin-and-visual-studio-2022/

MAUI は .NET 6 以降のみのサポートとなる[2]ため、従来の Xamarin.Forms から MAUI へ移行する場合、実行環境とフレームワークを同時に更新することとなり、両者の破壊的変更に悩まされることが予想されます。
この記事では MAUI への移行に備え、フレームワークを Xamarin.Forms のまま実行環境のみを .NET 6 に移行する手順を紹介します。


.NET 6 に移行すると何が変わる?

実際に移行する前に、.NET 6 に移行することで何が変わるのか確認しましょう。

従来の Xamarin.Forms .NET 6 の Xamarin.Forms MAUI
フレームワーク Xamarin.Forms Xamarin.Forms MAUI
フレームワークのサポート期限 2024/5/1 2024/5/1 ✅ 未発表
実行環境 Xamarin (Mono) ⚠️ .NET 6 ✅ .NET 6
実行環境のサポート期限 2024/5/1 2024/11/08 2024/11/08
.csproj の形式 Non-SDK-style / SDK-style ⚠️ SDK-style SDK-style
既定の C# バージョン C# 8.0 C# 10.0 C# 10.0
サポートされる Visual Studio のバージョン 2019 / 2022 ⚠️ 2022 2022

🟦 フレームワークは Xamarin.Forms のまま

.NET 6 に移行したからといって Xamarin.Forms 側で特に変わることはありません。
サポート期限も変わらず 2024/5/1 です。
ちなみに次の LTS バージョンである .NET 8 のリリース予定は 2023/11 です。

⚠️ 実行環境が Mono から .NET 6 に

これにより、従来の netstandard2.1 やプラットフォーム固有のライブラリに加え、netcoreapp net5.0 net6.0 のライブラリを使用できます。
ただし冒頭に記述した通り xamarinios xamarinmac xamarintvos のライブラリは .NET 6 で使用できなくなってしまっています。
.NET 6 向けビルドが提供されていない Xamarin.Forms も使用できないため、iOS / Mac / tvOS 向けビルドが必要な場合は MAUI への移行も同時に行いましょう。
また互換性のあるはずの monoandroid などのライブラリであっても netcoreapp 向けのバイナリを参照してしまう場合があるため、ライブラリの扱いには注意が必要です。

https://github.com/xamarin/XamarinCommunityToolkit/issues/985

その他にも細かい破壊的変更が多数含まれているため注意が必要です。

筆者が特に注意が必要と考えている破壊的変更は以下の 2 つです。
アプリケーションコードで使用していない場合でもサードパーティ製ライブラリで利用している可能性もあるため気を付けましょう。

https://docs.microsoft.com/ja-jp/dotnet/core/compatibility/breaking-changes

⚠️ .csproj は SDK-style のみサポート

Visual Studio 2017 以降 .csproj に SDK-style と呼ばれる新しい書き方が増え、これまで冗長で人の読めるものではなかった .csproj が整理されました。
この書き方は .NET Standard や .NET Core のプロジェクトではデフォルトで反映され、.NET Framework でも自分で書き換えれば利用できましたが、Xamarin の各プラットフォーム向けプロジェクトでは利用できませんでした。
.NET 6 へ移行する場合は SDK-style への移行が必須になります。
と言ってもほとんど自動で直してくれるので、手で直す箇所はほんの数行です。

https://ufcpp.net/blog/2017/5/newcsproj/

✅ 既定の C# バージョンが 8.0 から 10.0 に

従来の Xamarin.Forms でも手順を踏めば C# 10.0 の機能を利用できましたが、その手順が少々面倒であったり、ランタイム側の修正を伴うクラスの共変戻り値が利用できなかったりしていました。
.NET 6 ではデフォルトで C# 10.0 の機能が全て利用できます。
レコード型not パターンファイル スコープ名前空間など便利な機能がたくさん増えているので、積極的に使っていきましょう。

https://ufcpp.net/study/csharp/cheatsheet/ap_ver9/
https://ufcpp.net/study/csharp/cheatsheet/ap_ver10/

⚠️ Visual Studio 2019 でのビルドが不可能に

Visual Studio 2019 では .NET 6 アプリケーションのビルドはサポートされていないので、.NET 6 に移行すると当然ビルドができなくなってしまいます。
しかも厄介なことに 2019 でビルドしようとすると bin obj フォルダが壊れてしまうようで、壊れてしまったこれらのフォルダを削除しないと 2022 でもビルドが通らなくなります。
気を付けましょう。(1 敗)


Xamarin.Forms プロジェクトを .NET 6 へ移行する

違いがわかったところで .NET 6 への移行を始めましょう。

0. 前提条件

下記の手順は Visual Studio 2022 v17.4.1 及び .NET v6.0.202 で動作確認をしています。
Visual Studio for Mac での動作確認はしていないため、注意してください。
https://visualstudio.microsoft.com/

また実際に移行した際のソースコードは github.com/proudust/XamarinSandbox にあります。

1. dotnet workload コマンドで Android / iOS ワークロードをインストール

MAUI リリース前のバージョンの Visual Studio 2022 を使用する場合、.NET 6 の Android / iOS サポートを別途インストールする必要があります。
以下のコマンドでインストールできます。

dotnet workload install android
dotnet workload install ios

2. .csproj を SDK-style に移行する

各プラットフォーム固有のプロジェクトの .csproj を SDK-style のような形式に移行して回ります。
.NET アップグレード アシスタントで移行してしまうのが早いです。
https://docs.microsoft.com/ja-jp/dotnet/core/porting/upgrade-assistant-overview

cmd.exe
$ dotnet tool install -g upgrade-assistant
$ rd /s /q XamarinSandbox\XamarinSandbox.Android\bin XamarinSandbox\XamarinSandbox.Android\obj
$ upgrade-assistant upgrade XamarinSandbox\XamarinSandbox.Android\XamarinSandbox.Android.csproj
$ rd /s /q XamarinSandbox\XamarinSandbox.iOS\bin XamarinSandbox\XamarinSandbox.iOS\obj
$ upgrade-assistant upgrade XamarinSandbox\XamarinSandbox.iOS\XamarinSandbox.iOS.csproj
sh
$ dotnet tool install -g upgrade-assistant
$ rmdir -r XamarinSandbox/XamarinSandbox.Android/bin XamarinSandbox/XamarinSandbox.Android/obj
$ upgrade-assistant upgrade XamarinSandbox/XamarinSandbox.Android/XamarinSandbox.Android.csproj
$ rmdir -r XamarinSandbox/XamarinSandbox.iOS/bin XamarinSandbox/XamarinSandbox.iOS/obj
$ upgrade-assistant upgrade XamarinSandbox/XamarinSandbox.iOS/XamarinSandbox.iOS.csproj

3. .csproj を手作業で修正

下記のように変更します。

共有プロジェクト

  • TargetFrameworknet6.0 に変更
  • (Xamarin.Forms.Visual.Material を参照している場合のみ) XFDisableTargetFrameworkValidationTrue で追加
XamarinSandbox.csproj
   <PropertyGroup>
-    <TargetFramework>netstandard2.0</TargetFramework>
+    <TargetFramework>net6.0</TargetFramework>
+    <XFDisableTargetFrameworkValidation>True</XFDisableTargetFrameworkValidation>
   </PropertyGroup>

Android プロジェクト

  • TargetFrameworknet6.0-android に変更
  • PropertyGroup<GenerateAssemblyInfo>false</GenerateAssemblyInfo> を追加
  • RootNamespace をアップグレード前の値で追加
  • (Xamarin.Forms.Visual.Material を参照している場合のみ) XFDisableTargetFrameworkValidationTrue で追加
XamarinSandbox.Android.csproj
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net6.0-android</TargetFramework>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <RootNamespace>XamarinSandbox.Droid</RootNamespace>
+    <XFDisableTargetFrameworkValidation>True</XFDisableTargetFrameworkValidation>
   </PropertyGroup>

iOS プロジェクト

  • TargetFrameworknet6.0-ios に変更
  • PropertyGroup<GenerateAssemblyInfo>false</GenerateAssemblyInfo> を追加
  • (Xamarin.Forms.Visual.Material を参照している場合のみ) XFDisableTargetFrameworkValidationTrue で追加
XamarinSandbox.iOS.csproj
   <PropertyGroup>
-    <TargetFramework>net6.0</TargetFramework>
+    <TargetFramework>net6.0-ios</TargetFramework>
+    <GenerateAssemblyInfo>false</GenerateAssemblyInfo>
+    <XFDisableTargetFrameworkValidation>True</XFDisableTargetFrameworkValidation>
   </PropertyGroup>

4. Visual Studio でソリューションを開き、構成マネージャを開く

ここまで行った後、Visual Studio 2022 でソリューションを開くと、「現在のソリューションには、正しくない構成マッピングが含まれています。(後略)」という表示が出ます。
指示に従い構成マネージャを開き、そのまま何も変更せずに閉じ、すべて保存します。
それだけで XamarinSandbox.sln が修正されるので、これで完了です。

Ex. 本当に .NET 6 になったのか確かめてみる

せっかくなので本当に .NET 6 が Android や iOS 上で動作しているのか確かめてみましょう。

従来の Xamarin では .NET の Linux 向け実装である Mono が使用されていました。
その Mono に含まれる Mono.Runtime クラスの Runtime.GetDisplayName() クラスをリフレクションで取得し、実行することで確かめてみましょう。
下記のようなコードで取得することができます。[4]

string monoVersion = Type.GetType("Mono.Runtime")
    ?.GetMethods()
    .FirstOrDefault(x => x.Name == "GetDisplayName")
    ?.Invoke(null, null)
    ?.ToString();
string dotnetVersion = Environment.Version.ToString();

移行前後で実行してみると以下のような情報が取得できます。

移行前 移行後
monoVersion 6.12.0 (2020-02/c633fe92383) (null)
dotnetVersion 4.0.50524.0 6.0.0

というわけで本物の .NET 6 が動いているようです。

脚注
  1. https://dotnet.microsoft.com/en-us/platform/support/policy/xamarin ↩︎

  2. https://github.com/dotnet/maui/wiki/Xamarin.Forms-vs-.NET-MAUI ↩︎

  3. ソース: https://github.com/xamarin/Xamarin.Forms/blob/8af4f19be0cfb75f6a1df70c4db94eb6937ea1ee/.nuspec/Xamarin.Forms.Visual.Material.targets#L2 ↩︎

  4. 参考: https://social.msdn.microsoft.com/Forums/en-US/5dd995d7-9692-4647-9c7a-107129823ea0/how-to-check-my-mono-version ↩︎

GitHubで編集を提案

Discussion

tama_chitama_chi

有用な記事をありがとうございます!
こちらの記事拝見させていただき、早速ランタイムの変更(Mono → .net6.0)を試させて頂きました!

新規プロジェクトを作成し試してみたところ、Androidは移行できたのですが、
iOS向けがiOS固有の実装「Xamarin.Forms.Platform.iOS」が読み込めず、
ビルドエラーとなってしまいます...

具体的には、「global::Xamarin.Forms.Platform.iOS」がアセンブリ内に見つからず、
FormsApplicationDelegateを参照できない、というエラーとなります。

こちら、何かしら解決方法をお持ちだったりはしませんでしょうか?

https://github.com/xamarin/Xamarin.Forms/tree/5.0.0/Xamarin.Forms.Platform.iOS

ProudustProudust

返答が遅くなってしまいすみません。
再調査しましたところ、.NET 6 の破壊的変更によって Xamarin.iOS/Mac/tvOS ライブラリは .NET 6 プロジェクトから参照できなくなっていたことがわかりました。
Preview 版の .NET 6 では参照できていたこと、また Xamarin.Android のライブラリは問題なく参照できていたことから見落としていたようです。
検証不足な記事を載せてしまい申し訳ありませんでした。