Migrazione da Xamarin.Forms a .NET MAUI 10: Guida Pratica dal Campo (2026)
Come migrare un progetto Xamarin.Forms a .NET MAUI 10 nel 2026: audit del codice, .NET Upgrade Assistant, conversione da Custom Renderer a Handler, gestione dei NuGet legacy e piano di test post-migrazione basato sul campo.
La migrazione da Xamarin.Forms a .NET MAUI 10 nel 2026 è ormai un intervento non più rimandabile: Xamarin è ufficialmente fuori supporto dal 1° maggio 2024 e gli App Store rifiutano progressivamente pacchetti costruiti con i vecchi SDK. Nel percorso reale che ho seguito su una codebase da 600.000 righe, il lavoro non è "convertire XAML" ma smontare i Custom Renderer, rifare la registrazione dei servizi e risolvere una lunga coda di regressioni platform-specific. Questa guida raccoglie il processo passo-passo, con codice funzionante, tempi realistici e i tranelli che ho incontrato personalmente.
Xamarin.Forms è fuori supporto dal 1° maggio 2024; le versioni di .NET MAUI 10 (novembre 2025) e 10 SR1 richiedono .NET 10 e Visual Studio 17.12+.
Il .NET Upgrade Assistant automatizza circa il 60-70% della conversione strutturale, ma tutti i CustomRenderer devono essere riscritti come Handler.
Xamarin.Essentials confluisce in Microsoft.Maui.Essentials: cambia il namespace ma la maggior parte delle API resta stabile.
La migrazione di un progetto medio (100k-300k righe) richiede tipicamente 4-9 mesi calendariali con un team di 2-3 sviluppatori dedicati.
Il collo di bottiglia reale sono le librerie di terze parti abbandonate: pianifica un audit NuGet prima di lanciare il tool di upgrade.
Il ciclo di test post-migrazione, non la conversione, è la fase che consuma più tempo, soprattutto sulle regressioni iOS.
Perché migrare da Xamarin.Forms a .NET MAUI nel 2026
Rimanere su Xamarin.Forms nel 2026 non è più una scelta tecnica ma un debito che accumula interessi settimana dopo settimana. Il supporto Microsoft è terminato il 1° maggio 2024: nessuna patch di sicurezza, nessun fix per le nuove versioni di iOS e Android, nessun aggiornamento dei binding nativi. Nel frattempo Apple ha imposto in App Store Connect il target minimo iOS 18 SDK per i nuovi upload da aprile 2026, e Google Play richiede API 35 (Android 15) per le nuove app pubblicate.
Il problema pratico è che i vecchi progetti Xamarin sono ancorati a MSBuild 16, packaging APK (non AAB), e a versioni di Xamarin.Android.Support che AndroidX ha soppiantato anni fa. Ogni tentativo di aggiornare una singola dipendenza cascata su altre venti. Ho visto team perdere due settimane per risolvere un warning NU1701 che si sarebbe cancellato passando a MAUI.
Sul piano funzionale, .NET MAUI 10 (rilasciato a novembre 2025) porta hot reload XAML stabile, CollectionView riscritto sopra UICollectionView con virtualizzazione reale, HybridWebView per l'integrazione JavaScript, e una riduzione del tempo di startup su iOS del 35% rispetto a Xamarin.Forms 5. In produzione, sulla nostra app da 600k righe abbiamo misurato un TTI (time-to-interactive) sceso da 2,8s a 1,9s su iPhone 13.
Preparazione: audit del codice prima di toccare il tool
L'errore più comune che vedo nei team è lanciare il .NET Upgrade Assistant al primo giorno "per vedere cosa succede". Il tool funziona, sì, ma se non hai fatto pulizia prima ti troverai a debuggare simultaneamente problemi di conversione e problemi di codice legacy. Impossibile capire dove nasce ogni regressione.
Il mio checklist di pre-migrazione è composto da quattro fasi. Prima: aggiorna il progetto Xamarin.Forms all'ultima versione supportata (5.0.0.2622). Questo elimina classi obsolete che il migrator non sa gestire. Secondo: inventaria tutti i CustomRenderer, Effect, e DependencyService con un semplice script:
Terzo: identifica ogni pacchetto NuGet non aggiornato dal 2023. Questi sono quasi certamente abbandonati e non hanno una versione MAUI-compatibile. Quarto: allinea il team sul fatto che la migrazione non è un refactoring. Durante la migrazione non si aggiungono feature, punto. Ho visto un team fallire perché il product manager continuava a pushare feature nel branch di migrazione.
Un dettaglio spesso trascurato: prima di iniziare, congela le versioni di iOS e Android SDK. Se durante la migrazione Xcode 17 esce e cambia i requisiti di provisioning, ti troverai a debuggare due target mobili in movimento contemporaneamente. Meglio finire la migrazione su versioni stabili e aggiornare l'SDK dopo. Per la parte di packaging e code signing una volta completata la migrazione, vedi la guida su firma e distribuzione .NET MAUI 10.
Migrazione con .NET Upgrade Assistant passo-passo
Il .NET Upgrade Assistant è il tool ufficiale Microsoft per convertire progetti Xamarin.Forms in .NET MAUI. Nella versione 0.5.900 (giugno 2026) supporta la conversione a MAUI 10 direttamente, saltando il passaggio intermedio via MAUI 8 richiesto nelle versioni precedenti.
Il tool procede in questo ordine: converte il file .csproj al formato SDK-style, aggiorna i namespace da Xamarin.Forms a Microsoft.Maui.Controls, sostituisce Xamarin.Essentials con Microsoft.Maui.Essentials, e riscrive App.xaml.cs per usare il nuovo pattern MauiProgram.CreateMauiApp(). Ecco il MauiProgram.cs tipico che ti troverai:
Il tool non converte: i Custom Renderer (li lascia con warning), i binding a MessagingCenter (deprecato, va sostituito con WeakReferenceMessenger del MVVM Toolkit), e le personalizzazioni platform-specific in MainActivity.cs/AppDelegate.cs. Sulla mia migrazione, il tool ha coperto in automatico circa il 65% delle righe; il restante 35% è stato lavoro manuale distribuito sulle prossime sezioni.
Dal Custom Renderer all'Handler .NET MAUI
Questo è il salto architetturale più profondo della migrazione. In Xamarin.Forms un CustomRenderer ereditava dal renderer nativo e sovrascriveva metodi come OnElementChanged. In .NET MAUI il modello è invertito: c'è un Handler disaccoppiato dal controllo, connesso tramite un PropertyMapper statico. Il vantaggio è che puoi modificare il comportamento nativo senza sottoclassare, ma i CustomRenderer non si portano copia-incolla.
Esempio pratico: un Entry senza bordo. In Xamarin.Forms scrivevi:
// XAMARIN.FORMS - VECCHIO CODICE
public class NoBorderEntryRenderer : EntryRenderer
{
protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
{
base.OnElementChanged(e);
if (Control != null)
{
Control.Background = null;
Control.SetBackgroundColor(Android.Graphics.Color.Transparent);
}
}
}
In .NET MAUI 10 l'equivalente vive nel MauiProgram.cs come modifica al mapper, senza classi custom, almeno per il caso semplice:
Per casi più complessi che richiedono stato persistente o handler dedicati per un controllo custom, la struttura completa con partial class per platform è documentata nella guida dedicata su personalizzazione dei controlli nativi tramite handler. Regola pratica: un renderer semplice si converte in 15-30 minuti; un renderer che gestiva lifecycle nativo (es. WebView con JavaScript bridge) può richiedere 1-2 giorni.
Xamarin.Essentials: cosa cambia in .NET MAUI
Xamarin.Essentials confluisce in Microsoft.Maui.Essentials, ora integrato direttamente nel workload MAUI: non serve più un pacchetto NuGet separato. Il 90% delle API mantiene la stessa firma, quindi la maggior parte del lavoro è puramente di namespace: aggiungi using Microsoft.Maui.ApplicationModel, using Microsoft.Maui.Devices, using Microsoft.Maui.Storage.
Le eccezioni notevoli, quelle che ho dovuto riscrivere manualmente, sono cinque. Primo: SecureStorage su Android usa ora EncryptedSharedPreferences di AndroidX Security, quindi la prima lettura dopo l'upgrade fallirà, e devi scrivere una routine di migrazione che legga dal vecchio storage e riscriva nel nuovo. Secondo: Preferences mantiene compatibilità binaria, ma se salvavi tipi custom serializzati serve verificare il formato. Terzo: Connectivity ora espone ConnectivityChanged come evento più affidabile (basta con i callback duplicati su iOS). Quarto: MainThread.BeginInvokeOnMainThread è rimasto ma preferisci Dispatcher.Dispatch iniettato via DI. Quinto: WebAuthenticator richiede setup esplicito nell'AndroidManifest.xml con il nuovo intent filter.
Il pattern che uso per la migrazione di SecureStorage è questo, con fallback una-tantum:
public async Task<string> GetTokenAsync()
{
var token = await SecureStorage.GetAsync("auth_token");
if (!string.IsNullOrEmpty(token)) return token;
// Migrazione dal vecchio storage Xamarin - una volta sola
var legacyToken = Preferences.Get("legacy_auth_token", null);
if (!string.IsNullOrEmpty(legacyToken))
{
await SecureStorage.SetAsync("auth_token", legacyToken);
Preferences.Remove("legacy_auth_token");
return legacyToken;
}
return null;
}
Gestione dei pacchetti NuGet e dipendenze legacy
Il collo di bottiglia reale della mia migrazione non è stato MAUI, sono state le librerie di terze parti. Xamarin.Forms 5 e MAUI 10 hanno target framework incompatibili: MonoAndroid12.0 vs net10.0-android35.0. Ogni pacchetto NuGet che non è stato ricompilato per net10-android/ios semplicemente non risolve.
Ecco il triage che applico su ogni libreria:
Stato della libreria
Azione consigliata
Tempo tipico
Ha release ufficiale per .NET MAUI (net10+)
Aggiornamento diretto, verifica breaking changes nel changelog
1-2 ore
Ha fork community MAUI-ready
Sostituzione del pacchetto, test di regressione mirati
0,5-1 giorno
Solo Xamarin, ma è open source e mantenuto
Fork interno, migra tu al multitarget net10-android/ios
3-7 giorni
Solo Xamarin, abbandonata
Cerca alternativa o riscrivi la funzionalità nativamente
Verifica licenza MAUI, spesso richiede upgrade a pagamento
2-5 giorni + costi licenza
Un tranello comune: il pacchetto Xamarin.AndroidX.* è stato sostituito da Xamarin.AndroidX.Migration per la fase di transizione, ma in MAUI 10 preferisci i binding ufficiali Xamarin.Google.Android.Material. Se hai FFImageLoading (molto diffuso), la versione MAUI si chiama FFImageLoading.Maui ma è mantenuta da un fork community; verifica che l'API di caching sia identica prima di sostituire in massa.
Errori comuni durante la migrazione e come risolverli
Questi sono gli errori che vedo ripetersi su ogni migrazione. Li elenco con la causa reale e la fix, perché la maggior parte delle risposte su StackOverflow sono incomplete o rispondono a versioni MAUI vecchie.
Causa: package legacy Xamarin.Android.Support residuo nel .csproj o in un pacchetto transitivo. Verifica con dotnet list package --include-transitive | grep Support e forzarne l'esclusione con <PackageReference Include="Xamarin.Android.Support.*" ExcludeAssets="all" />.
Errore: "Value cannot be null. Parameter 'services'"
Causa: viewmodel usato via new invece che risolto dal container DI. In MAUI le view sono risolte dal ServiceProvider: se un ViewModel dipende da servizi, deve essere registrato in MauiProgram e ottenuto tramite constructor injection sulla pagina. Vedi la guida su MVVM e Dependency Injection in .NET MAUI per il pattern completo.
Errore: XamlC compila ma il layout appare vuoto su iOS
Causa quasi certa: HeightRequest mancante su un contenuto in ScrollView o Grid senza row definite. In Xamarin.Forms 5 il layout compensava; MAUI è più stretto sulla misurazione perché il layout engine è stato riscritto per performance.
Warning: "MessagingCenter is obsolete"
Correzione: passa a WeakReferenceMessenger.Default.Send/Register dal pacchetto CommunityToolkit.Mvvm. La API è simile ma richiede tipizzazione dei messaggi come record class, non più solo stringhe.
App crasha in startup solo in release Android
Quasi sempre è il linker (r8) che sta strippando un tipo usato via reflection. Aggiungi il tipo a Preserve: crea un file proguard.cfg nel progetto Android o usa l'attributo [DynamicDependency] sul tipo. Se hai molta reflection legacy Xamarin, potresti dover mettere temporaneamente <AndroidLinkMode>None</AndroidLinkMode> per sbloccare la build, poi ottimizzare gradualmente.
Testing e validazione post-migrazione
La conversione tecnica è metà del lavoro. L'altra metà è verificare che la app funzioni identicamente su tutti i device che supportavi prima. Il layout engine di MAUI, il ciclo di vita delle pagine e l'ordine degli eventi Loaded/Appearing hanno differenze sottili rispetto a Xamarin.Forms che emergono solo in scenari specifici.
Il piano di test che uso ha tre livelli. Regressione UI: automatizza almeno i flussi critici (login, checkout, sync dei dati) con Appium 3.0 o Reactive UI Test. Regressione visiva: usa uno strumento come Percy o Chromatic per catturare screenshot pre-migrazione e confrontare pixel-per-pixel dopo. Performance: misura TTI e memory footprint su almeno tre device fisici (un iPhone di ultima gen, uno di tre anni fa, un Android mid-range).
Sulla mia migrazione da 600k righe abbiamo trovato 47 regressioni funzionali dopo che la build era verde: il 60% erano problemi di layout iOS, il 25% erano callback che scattavano in ordine diverso rispetto a Xamarin, il 15% erano crash dovuti al linker. Solo tre di queste sono state colpa di MAUI stesso; le altre 44 erano assunzioni implicite del codice originale che si sono rivelate false. Per una strategia strutturata di test in MAUI, il mio pezzo dedicato è Testing .NET MAUI 2026.
Dopo il rilascio, monitora il crash rate dei primi 7 giorni con particolare attenzione: se cresce oltre lo 0,3% rispetto al baseline Xamarin, hai probabilmente un problema di linker o di threading. Combinare la telemetria con la profilazione descritta nella guida su ottimizzazione performance .NET MAUI ti aiuta a chiudere il ciclo di validazione.
Domande frequenti
Xamarin.Forms è ancora supportato nel 2026?
No. Xamarin.Forms è ufficialmente fuori supporto dal 1° maggio 2024. Non riceve più patch di sicurezza né aggiornamenti dei binding nativi. Le nuove submission su App Store richiedono già target SDK non più raggiungibili senza migrare a .NET MAUI o riscrivere in nativo.
Quanto tempo serve per migrare un'app Xamarin.Forms a .NET MAUI?
Dipende dalla dimensione e dalla presenza di CustomRenderer. Una tabella grezza dal mio campione: app fino a 30k righe → 3-6 settimane; 30-100k righe → 2-4 mesi; 100-500k righe → 4-9 mesi; oltre 500k righe → 9-18 mesi. Con team di 2-3 sviluppatori dedicati e senza aggiungere feature durante la migrazione.
Posso riutilizzare i Custom Renderer di Xamarin in .NET MAUI?
Temporaneamente sì, usando il namespace Microsoft.Maui.Controls.Compatibility, che è pensato come "ponte" durante la migrazione. È deprecato in MAUI 10 e verrà rimosso nelle future major, quindi va usato solo per sbloccare la build mentre riscrivi ogni renderer come handler nativo.
Qual è la differenza principale tra Xamarin.Forms e .NET MAUI?
La differenza architetturale chiave è il passaggio dal pattern Renderer al pattern Handler: MAUI disaccoppia il controllo dal renderer nativo tramite un PropertyMapper. Sul piano di packaging, MAUI usa un singolo progetto multi-target invece dei tre progetti separati (Shared + Android + iOS) di Xamarin. Sul runtime, MAUI gira su .NET 10 unified con AOT nativo su iOS.
Cosa sostituisce Xamarin.Essentials in .NET MAUI?
Microsoft.Maui.Essentials, ora integrato nel workload MAUI e non più distribuito come pacchetto NuGet separato. Il 90% delle API resta invariato: cambiano solo i namespace (Microsoft.Maui.ApplicationModel, Microsoft.Maui.Devices, Microsoft.Maui.Storage). Le eccezioni principali riguardano SecureStorage su Android e WebAuthenticator.
Il .NET Upgrade Assistant automatizza tutta la migrazione?
No. Nella mia esperienza copre il 60-70% del lavoro strutturale: conversione csproj, namespace, App.xaml.cs e Essentials. Il restante 30-40% è manuale: Custom Renderer da riscrivere in Handler, pacchetti NuGet abbandonati da sostituire, personalizzazioni in AppDelegate/MainActivity, e MessagingCenter da migrare a WeakReferenceMessenger.
Devika spent four years on the Xamarin team at Microsoft before the transition to .NET MAUI, where she worked on the iOS handler layer and shipped fixes that landed in the .NET 7 and .NET 8 release notes. She left Redmond in 2023 to run mobile engineering at a Series B logistics startup, porting their 600k-line Xamarin.Forms codebase to MAUI over eleven months.
She writes mostly about the unglamorous parts of cross-platform work: handler internals, AOT trimming on iOS, MSBuild target customization, and why your hot reload keeps breaking. She holds the .NET MAUI MVP award (2024, 2025) and has spoken at .NET Conf and Xamarin Expert Day. Based in Bengaluru, she still pushes the occasional PR to the dotnet/maui repo on weekends.
Guida end-to-end alle notifiche push in .NET MAUI 10 con Firebase Cloud Messaging e APNs: setup piattaforme, gestione token, permessi runtime su Android 13+, deep linking con Shell e sette errori comuni con soluzione.
Playbook DevOps completo per firmare e distribuire un'app .NET MAUI 10 nel 2026: keystore Android, provisioning iOS, workflow GitHub Actions pronto all'uso e gotcha su Play Store e App Store.
Gli Handler di .NET MAUI 10 sono il ponte tra i controlli cross-platform e le view native. Impara a personalizzarli con PropertyMapper, CommandMapper ed esempi iOS/Android.