مهاجرت Xamarin.Forms به .NET MAUI در ۲۰۲۶: راهنمای کامل با Upgrade Assistant

پشتیبانی Xamarin.Forms پایان یافته. این راهنما با Upgrade Assistant، تبدیل Renderer به Handler و رفع خطاهای رایج، مهاجرت به .NET MAUI ۹ را گام‌به‌گام نشان می‌دهد.

مهاجرت Xamarin به .NET MAUI: راهنمای ۲۰۲۶

به‌روزرسانی: ۲۱ خرداد ۱۴۰۵ (۱۱ ژوئن ۲۰۲۶)

مهاجرت از Xamarin.Forms به .NET MAUI یعنی هدف‌گذاری پروژه روی net9.0-android و net9.0-ios به جای MonoAndroid/Xamarin.iOS، تبدیل Custom Rendererها به Handler، جایگزینی Xamarin.Essentials با Microsoft.Maui.Essentials، و حذف فایل‌های AssemblyInfo.cs و packages.config به‌نفع پروژهٔ SDK-style. از آنجا که پشتیبانی رسمی Xamarin از مه ۲۰۲۴ پایان یافته، در ۲۰۲۶ این مهاجرت یک به‌روزرسانی اختیاری نیست؛ یک ضرورت امنیتی و عملیاتی برای هر تیمی است که هنوز اپ Xamarin در پروداکشن دارد.

  • پشتیبانی Xamarin.Forms از ۱ مه ۲۰۲۴ به پایان رسیده و SDKهای جدید iOS 18 و Android 15 رسماً روی Xamarin پشتیبانی نمی‌شوند.
  • ابزار رسمی .NET Upgrade Assistant در نسخهٔ ۸.۶.۰ به بعد، تبدیل خودکار csproj، نمونه‌سازی MauiProgram و تشخیص Renderer را انجام می‌دهد.
  • هر CustomRenderer باید به Handler با PropertyMapper و CommandMapper تبدیل شود (این بزرگ‌ترین تلاش دستی مهاجرت است).
  • پکیج‌های Xamarin Community Toolkit جداگانه (CommunityToolkit.Maui) نصب می‌شوند و فضای نام آن‌ها تغییر کرده است.
  • برای تیم‌های بزرگ، مهاجرت تدریجی با اپ‌های Multi-Targeted در یک Solution امکان‌پذیر است، اما توصیه می‌کنیم مدت آن را زیر ۳ ماه نگه دارید.
  • پس از مهاجرت، فعال‌سازی NativeAOT و حذف Reflection اضافه می‌تواند زمان Startup را تا ۴۵٪ کاهش دهد.

چرا باید از Xamarin.Forms به .NET MAUI مهاجرت کنیم؟

پاسخ کوتاه: چون دیگر انتخابی نیست. مایکروسافت در سند رسمی پایان پشتیبانی Xamarin اعلام کرده که از ۱ مه ۲۰۲۴ هیچ به‌روزرسانی امنیتی، رفع باگ، یا پشتیبانی پلتفرمی برای Xamarin.Forms 5.0، Xamarin.Essentials و Xamarin.iOS/Android منتشر نمی‌شود. این یعنی اپ شما در ۲۰۲۶ نه تنها از قابلیت‌های جدید iOS 18 و Android 15 محروم است، بلکه با هر آپدیت سیستم‌عامل احتمال شکستن رفتار آن بیشتر می‌شود.

صادقانه بگویم، من خودم آخرین پروژه‌ای که در Xamarin نگه داشتیم را در اوایل ۲۰۲۵ مهاجرت کردم و یک نکته که مرا غافلگیر کرد، نبود اخطار واضح در Visual Studio بود؛ کد Xamarin هنوز compile می‌شد، اما App Store Connect دیگر باینری‌های ساخته‌شده با Xamarin.iOS را قبول نمی‌کرد. در تجربهٔ ما، تیم‌هایی که مهاجرت را به تأخیر انداختند با چند مشکل مشترک مواجه شدند: عدم امکان انتشار به App Store با پشتیبانی Privacy Manifest که از بهار ۲۰۲۴ اجباری شد، ناتوانی در استفاده از Android 15 Edge-to-Edge UI، و وابستگی به نسخه‌های قدیمی Visual Studio که بعداً پشتیبانی خود را از Mono workload خارج کرد. علاوه بر این، .NET MAUI به‌طور پیش‌فرض از NativeAOT روی iOS و R2R روی Android بهره می‌برد که Startup سرد را به‌طور معناداری بهبود می‌دهد، اتفاقی که در Xamarin هرگز نخواهد افتاد.

از منظر فنی، .NET MAUI یک نسخهٔ نوسازی‌شده از Xamarin.Forms است: همان مدل XAML، همان MVVM، اما با Runtime مدرن .NET 9، یک معماری Handler به‌جای Renderer، و یک پروژهٔ تک‌فولدری به‌جای چهار پروژهٔ پراکنده. این یعنی هرچند مهاجرت کار دارد، اما کسب‌وکار شما با یک پلتفرم کاملاً بیگانه روبرو نیست.

چه چیزی در .NET MAUI نسبت به Xamarin.Forms تغییر کرده است؟

پیش از شروع مهاجرت، باید دقیقاً بدانیم چه چیزی تغییر کرده تا برآورد زمان واقعی داشته باشیم. ما در پروژه‌هایی که اخیراً مهاجرت کردیم، اختلاف‌های زیر را به‌عنوان «گلوگاه‌های زمانی» شناسایی کردیم:

مفهومXamarin.Forms.NET MAUI ۹
ساختار پروژه۴ پروژه جدا (Shared، Android، iOS، UWP)یک پروژه Multi-Targeted با TargetFrameworks
سفارشی‌سازی کنترل‌هاCustom RendererHandler با PropertyMapper
دسترسی به API نیتیوXamarin.EssentialsMicrosoft.Maui.Essentials (در‌جا)
تزریق وابستگیService Locator دستیGeneric Host با builder.Services
سیستم بوت‌استرپFormsApplicationActivityMauiProgram.CreateMauiApp()
پشتیبانی پلتفرمAndroid، iOS، UWP، TizenAndroid، iOS، macOS (Catalyst)، Windows (WinUI 3)، Tizen
RuntimeMono 2020.NET 9 با NativeAOT
XAML Hot Reloadمحدودکامل با XAML Live Preview

مهم‌ترین تغییر مفهومی، حذف Forms.Init() و جایگزینی آن با MauiProgram است. در گذشته هر پلتفرم یک Activity/AppDelegate جدا داشت که Forms را راه‌اندازی می‌کرد؛ حالا یک نقطه ورود مشترک با Generic Host وجود دارد که شبیه ASP.NET Core است. این یعنی اگر تیم شما با ASP.NET Core کار کرده، آشنایی با DI و Logging و Configuration در MAUI سریع‌تر اتفاق می‌افتد.

تغییر دومی که تیم‌ها را غافلگیر می‌کند، حذف UWP و افزوده شدن WinUI 3 است. اگر اپ شما مشتری UWP داشت، باید بدانید که MAUI Windows هدف net9.0-windows10.0.19041 است و یک معماری متفاوت دارد؛ برخی کنترل‌های UWP دیگر مستقیم در دسترس نیستند و نیاز به Wrapper جدید دارند.

استفاده از .NET Upgrade Assistant برای مهاجرت خودکار

ابزار رسمی مایکروسافت برای این کار .NET Upgrade Assistant است که از نسخهٔ ۸.۶.۰ به بعد قالب‌های اختصاصی Xamarin.Forms را پشتیبانی می‌کند. کد منبع و راهنمای کامل آن هم در مخزن GitHub پروژه در دسترس است. نصب و اجرای آن ساده است:

# نصب ابزار به صورت Global
dotnet tool install -g upgrade-assistant

# اجرای ابزار روی پروژه Xamarin.Forms shared
cd path/to/MyXamarinApp
upgrade-assistant upgrade MyXamarinApp.sln

# انتخاب گزینه Upgrade Xamarin.Forms project to .NET MAUI
# سپس انتخاب net9.0 به عنوان TargetFramework

ابزار به‌صورت تعاملی این مراحل را انجام می‌دهد: ادغام چهار پروژه به یک csproj واحد، تولید فایل MauiProgram.cs، تبدیل Application.xaml به‌نسخهٔ MAUI، انتقال resourceهای Android (Resources/drawable) به ساختار جدید Platforms/Android/Resources، و حذف خودکار فایل‌های AssemblyInfo.cs که در پروژه‌های SDK-style دیگر لازم نیستند.

پس از اتمام، ابزار یک گزارش با لیست تغییرات ناتمام تولید می‌کند. مهم‌ترین موارد معمولاً اینها هستند: Custom Renderers که نیاز به بازنویسی دستی دارند، پکیج‌های NuGet که نسخهٔ MAUI ندارند، و ارجاع‌های Xamarin.Forms در کد C# که باید با Microsoft.Maui.Controls جایگزین شوند.

مراحل دستی پس از اجرای Upgrade Assistant

تجربه نشان داده ابزار خودکار حدود ۶۰ تا ۷۰ درصد کار را انجام می‌دهد. باقی‌مانده نیازمند مداخلهٔ دستی است. ترتیب کاری که ما در تیم‌ها دنبال می‌کنیم:

  1. به‌روزرسانی MauiProgram.cs: تمام Service Registrationهایی که قبلاً در App.xaml.cs داشتید، به builder.Services.AddSingleton<...>() منتقل می‌شوند. این یک فرصت عالی برای پاک‌سازی Service Locator pattern قدیمی است.
  2. تغییر namespaceهای XAML: همهٔ فایل‌های XAML باید فضای نام http://xamarin.com/schemas/2014/forms را به http://schemas.microsoft.com/dotnet/2021/maui تغییر دهند. این کار با Find & Replace کل پروژه سریع انجام می‌شود.
  3. به‌روزرسانی Bootstrap هر پلتفرم: فایل MainActivity.cs در اندروید و AppDelegate.cs در iOS باید از MauiAppCompatActivity و MauiUIApplicationDelegate ارث‌بری کنند.
  4. به‌روزرسانی Build Properties: در csproj جدید، SupportedOSPlatformVersion برای اندروید باید حداقل ۲۱ و برای iOS حداقل ۱۵ تنظیم شود.
// MauiProgram.cs - نقطه ورود جدید
public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("Vazirmatn-Regular.ttf", "Vazir");
            });

        // ثبت سرویس‌ها (جایگزین Service Locator قدیمی)
        builder.Services.AddSingleton<IApiClient, ApiClient>();
        builder.Services.AddTransient<MainViewModel>();
        builder.Services.AddTransient<MainPage>();

#if DEBUG
        builder.Logging.AddDebug();
#endif

        return builder.Build();
    }
}

اگر تیم شما تازه به معماری MVVM با CommunityToolkit مهاجرت می‌کند، این مرحله بهترین فرصت برای حذف کدهای ViewModel دست‌ساز است.

تبدیل Custom Renderer به Handler در .NET MAUI

این بخش معمولاً ۵۰ تا ۷۰ درصد زمان مهاجرت را می‌گیرد. فلسفهٔ Handler با Renderer متفاوت است: به‌جای ارث‌بری از یک کلاس نیتیو و override کردن متدها، شما یک PropertyMapper تعریف می‌کنید که می‌گوید «وقتی property X روی کنترل MAUI تغییر کرد، این عمل را روی کنترل نیتیو انجام بده.» اگر می‌خواهید عمیق‌تر بدانید، مستندات رسمی Handler در .NET MAUI یک نقطهٔ شروع خوب است.

یک مثال واقعی: فرض کنید در Xamarin یک EntryRenderer سفارشی داشتید که خط زیر TextField در iOS را حذف می‌کرد:

// قدیم: Xamarin.Forms iOS Custom Renderer
public class NoUnderlineEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);
        if (Control != null)
        {
            Control.BorderStyle = UITextBorderStyle.None;
        }
    }
}

معادل آن در .NET MAUI با Handler:

// جدید: .NET MAUI Handler با PropertyMapper سفارشی
// در MauiProgram.cs
builder.ConfigureMauiHandlers(handlers =>
{
#if IOS
    Microsoft.Maui.Handlers.EntryHandler.Mapper.AppendToMapping(
        "NoUnderline",
        (handler, view) =>
        {
            handler.PlatformView.BorderStyle = UIKit.UITextBorderStyle.None;
        });
#endif
});

برای Rendererهای پیچیده‌تر (مثل CarouselView سفارشی یا ListView با Cell‌های سفارشی)، می‌توانید از IElementHandler ارث‌بری کنید و یک Handler کامل بنویسید. اما در عمل، در ۸۰٪ موارد AppendToMapping کفایت می‌کند. توصیهٔ ما این است که ابتدا یک inventory از همهٔ Renderers بگیرید و آن‌ها را بر اساس پیچیدگی دسته‌بندی کنید، سپس از ساده‌ترین شروع کنید تا تیم تجربه کسب کند.

جایگزینی پکیج‌های NuGet ناسازگار

هر پکیج Xamarin باید به نسخهٔ MAUI آن جایگزین شود. این جدول رایج‌ترین موارد را پوشش می‌دهد:

پکیج Xamarinمعادل .NET MAUIنکته مهاجرت
Xamarin.EssentialsMicrosoft.Maui.Essentials (داخلی)API تقریباً یکسان، فضای نام جدید است
Xamarin.Forms.MapsMicrosoft.Maui.Controls.Mapsنیاز به ApiKey جدید Google Maps
Xamarin.CommunityToolkitCommunityToolkit.Mauiبرخی Behaviors تغییر نام داده‌اند
Xamarin.Forms.Visual.Material(حذف شده)استفاده از Material You بومی Android 12+
SkiaSharp.Views.FormsSkiaSharp.Views.Maui.Controlsنسخهٔ ۲.۸۸+ نیاز است
AkavacheAkavache 10+ (پشتیبانی MAUI)به BlobCache.ApplicationName رسیدگی کنید
Plugin.Permissions(داخل Essentials)API ساده‌تر شده است

برای پکیج‌هایی که هنوز نسخهٔ MAUI ندارند، چند گزینه دارید: یافتن یک alternative، Fork و port کردن خودتان، یا تماس با نگهدارنده. در پروژه‌ای که اخیراً مهاجرت کردیم، ۳ پکیج بدون پشتیبانی MAUI داشتیم. برای دو تای آن‌ها alternative پیدا کردیم و سومی را Fork کردیم؛ کل این کار ۲ روز طول کشید.

رفع خطاهای رایج پس از مهاجرت

این خطاها را تقریباً همهٔ تیم‌ها در اولین Build پس از مهاجرت می‌بینند. اگر از قبل برای آن‌ها آماده باشید، چند ساعت در وقت تیم صرفه‌جویی می‌شود.

خطای CS0246: نام Xamarin.Forms یافت نشد

این خطا یعنی Find & Replace کامل انجام نشده. باید همهٔ using Xamarin.Forms; را با using Microsoft.Maui.Controls; و using Xamarin.Essentials; را با using Microsoft.Maui.Devices; یا فضای نام مناسب جایگزین کنید. این Script در PowerShell کار را خودکار می‌کند:

Get-ChildItem -Recurse -Include *.cs |
  ForEach-Object {
    (Get-Content $_.FullName) `
      -replace 'using Xamarin\.Forms;', 'using Microsoft.Maui.Controls;' `
      -replace 'using Xamarin\.Essentials;', 'using Microsoft.Maui.Storage;' |
    Set-Content $_.FullName
  }

خطای XFC0000: نمی‌توان فضای نام xmlns را resolve کرد

فایل XAML شما هنوز فضای نام Xamarin دارد. باید xmlns="http://xamarin.com/schemas/2014/forms" را به xmlns="http://schemas.microsoft.com/dotnet/2021/maui" تغییر دهید.

خطای Java.Lang.RuntimeException در Activity

این تقریباً همیشه به این معنی است که MainActivity هنوز از FormsAppCompatActivity ارث‌بری می‌کند. باید آن را به MauiAppCompatActivity تغییر دهید و فراخوانی Forms.Init() را حذف کنید.

عملکرد ضعیف در iOS Release Build

اگر پس از مهاجرت، اپ در iOS کند شده است، احتمالاً UseInterpreter هنوز روشن است. در csproj مطمئن شوید که <UseInterpreter>false</UseInterpreter> در پروفایل Release تنظیم شده. برای جزئیات بیشتر می‌توانید راهنمای بهینه‌سازی عملکرد .NET MAUI را مطالعه کنید.

چک‌لیست مهاجرت برای تیم‌های مهندسی

در تیم‌هایی که در ۲۰۲۵-۲۰۲۶ به ما کمک کردیم، این چک‌لیست عملیاتی را به‌عنوان «Definition of Done» مهاجرت تعریف کردیم. اگر می‌خواهید مهاجرت را به یک تیم بسپارید و هر هفته وضعیت آن را پیگیری کنید، این لیست بسیار کمک می‌کند:

  • [ ] Inventory کامل از همهٔ Custom Renderers، Effects، Behaviors و Triggers تهیه شده است.
  • [ ] لیست تمام پکیج‌های NuGet با وضعیت پشتیبانی MAUI آماده است.
  • [ ] CI Build روی شاخهٔ migration در GitHub Actions با موفقیت اجرا می‌شود.
  • [ ] حداقل ۸۰٪ از Unit Testهای موجود بدون تغییر روی کد مهاجرت‌شده pass می‌شوند.
  • [ ] Smoke test روی هر دو پلتفرم Android و iOS با Debug و Release Build انجام شده است.
  • [ ] زمان Startup و سایز APK/IPA با نسخهٔ Xamarin مقایسه و مستند شده است.
  • [ ] Privacy Manifest برای iOS تهیه و در پروژه قرار گرفته است.
  • [ ] مستندات Onboarding تیم به‌روزرسانی شده تا توسعه‌دهندگان جدید بدانند پروژه MAUI است.
  • [ ] Rollback plan در صورت بروز مشکل بحرانی در پروداکشن آماده است.
  • [ ] انتشار اولیه به یک گروه Beta (مثلاً ۵٪ کاربران) به‌صورت Staged Rollout انجام شده است.

اگر اپ شما RTL است یا تقویم شمسی دارد، پس از اتمام مهاجرت پیشنهاد می‌کنیم راهنمای محلی‌سازی و RTL در .NET MAUI را مرور کنید چون برخی رفتارها در MAUI متفاوت با Xamarin پیاده‌سازی شده‌اند.

پرسش‌های پرتکرار

آیا مهاجرت از Xamarin.Forms به .NET MAUI واقعاً اجباری است؟

بله. پشتیبانی Xamarin از ۱ مه ۲۰۲۴ پایان یافته و هیچ به‌روزرسانی امنیتی یا سازگاری با iOS 18 و Android 15 منتشر نمی‌شود. اپ‌های Xamarin همچنان اجرا می‌شوند، اما در میان‌مدت با مشکلات انتشار و امنیت روبرو خواهند شد.

مهاجرت یک پروژهٔ متوسط چقدر زمان می‌برد؟

برای یک پروژهٔ ۲۰ تا ۵۰ هزار خط کد با ۵ تا ۱۰ Custom Renderer، معمولاً ۳ تا ۶ هفته با یک توسعه‌دهنده تمام‌وقت زمان می‌برد. پروژه‌های بزرگ‌تر (بیش از ۱۰۰ هزار خط) می‌توانند تا ۱۲ هفته نیاز داشته باشند.

آیا می‌توان Xamarin و .NET MAUI را در یک پروژه به‌صورت موازی اجرا کرد؟

خیر، در یک csproj واحد ممکن نیست چون TargetFramework متفاوت است. اما می‌توانید در یک Solution دو پروژهٔ جدا داشته باشید و کد مشترک را در یک کلاس‌لایبرری netstandard2.0 نگه دارید تا تدریجاً مهاجرت کنید.

آیا Upgrade Assistant همهٔ کارها را انجام می‌دهد؟

نه. Upgrade Assistant حدود ۶۰ تا ۷۰ درصد کار را خودکار می‌کند: تبدیل csproj، تولید MauiProgram و تغییر فضای نام پایه. تبدیل Custom Renderer به Handler، بازنویسی پکیج‌های ناسازگار و رفع خطاهای پلتفرمی همچنان به مداخلهٔ دستی نیاز دارد.

آیا UWP در .NET MAUI پشتیبانی می‌شود؟

خیر. مایکروسافت در MAUI به‌جای UWP از WinUI 3 (Windows App SDK) استفاده می‌کند. اگر اپ Xamarin شما کاربران UWP داشت، باید یا آن‌ها را به WinUI 3 منتقل کنید یا UWP build را به‌عنوان یک سکوی جداگانه حفظ کنید.

آیا پس از مهاجرت می‌توانم از NativeAOT استفاده کنم؟

بله. NativeAOT روی iOS پیش‌فرض است و روی Android از .NET 9 به بعد به‌صورت Preview در دسترس است. فعال‌سازی آن می‌تواند زمان Startup را تا ۴۵٪ کاهش دهد، اما به حذف Reflection و بازنویسی کد Dynamic نیاز دارد.

Priya Sharma
درباره نویسنده Priya Sharma

Cross-platform engineering lead who's shipped apps to millions on both Play Store and App Store. Believes shared codebases shouldn't mean shared mediocrity.