MAUI: un futuro nelle HybridView ? Parte 1

Recentemente ho avuto l’onore di presentare una sessione al workshop Dot Net Conf 2023 – Le Novità Di .NET 8 organizzato da Dot Net Liguria, con oggetto MAUI e le novità della versione 8.

Come spesso accade il materiale preparato era molto di più di quanto ho poi presentato alla sessione, e pertanto ho pensato di scriverne qui per riproporre non solo quanto presentato nella sessione, ma anche completarlo con le altre tagliate.

L’oggetto di questo scritto è presentare la mia esperienza di sviluppo con MAUI, e quindi come le novità uscite nella versione 8 del framework hanno cambiato la mia percezione del framework.

Inoltre tenterò di fare una serie di valutazioni, prettamente personali, su come saranno gli sviluppi futuri di questa piattaforma.

Cos’è MAUI

Per iniziare e per inciso: è l’evoluzione di Xamarin.

Punto.

Ma non solo: in realtà è molto di più.

Se avete amato Xamarin non potrete che amare MAUI: viceversa se l’avete odiato….. bè insomma avete capito.

Detto in modo più professionale, e per quelli che NON sanno cosa sia Xamarin… .

MAUI sta per NET Multi-platform App UI, ed è un framework cross-platform che permette di scrivere applicazioni usando un unico code-base ed essere mandato in esecuzione nativamente su Android, iOS, macOS e Windows.

Per scrivere questo code-base si usa C#. In altri termini SOLO usando C# è possibile scrivere maschere grafiche, logiche funzionali e quant’altro che girano in TUTTE le piattaforme indicate sopra.

Per disegnare le maschere, in realtà, è anche possibile usare XAML, ma non è obbligatorio.

Oss.: Lo XAML usato in MAUI sostanzialmente ha la stessa sintassi usata da WPF, ma in realtà NON è la stessa cosa. Il diavolo, spesso, è nei dettagli…. la sintassi e alcune istruzioni sono differenti, anche se occorre dire che queste sono abbastanza minime e l’impostazione generale è assolutamente identica.

La grossa potenza di scrivere le cose in MAUI è tutto viene tradotto poi in codice/controlli grafici nativi.

Per meglio chiarire: una casella di testo in MAUI viene chiamata Entry, ma poi in fase di compilazione questa verrà tradotta in EditText in Android, in UITextField in iOS, e così via.

Questa caratteristica è molto, e dico molto, importante: questo non solo perché il prodotto finale ha un look and feel nativo (il che non è poco….), ma ha anche perfomance praticamente uguali a quelle offerte dalle applicazioni scritte con framework nativi.

Quindi MAUI offre il sogno proibito di ogni sviluppatore: write-once e run-anywhere.

MAUI sotto il cofano

Per inciso: per lavorare con MAUI ci si può dimenticare delle note che Vi scriverò in questo capitolo, ma ogni tanto è anche interessante dare uno sguardo sotto il cofano della macchina che si guida tutti i giorni per capire come funziona…. anche se è meglio on toccare troppo se non si hanno le competenze necessarie……

Quindi dicevamo: vediamo cosa c’è sotto il cofano di MAUI.

In realtà esistono 4 set differenti di framework:
.NET for Android, .NET for iOS, .NET for macOS, and Windows UI (WinUI).

Detto in modo brutale e impreciso: lo scopo di questi 4 framework è quello di poter scrivere codice nella piattaforma dedicata usando, però, C#, invece che il linguaggio nativo.

In altri termini grazie a questi framework dedicati è possibile accedere alla totalità delle API della piattaforma, utilizzando però C#.

Quindi per esempio scrivendo per .NET for Android si ha la stessa struttura del codice, e finanche le stesse chiamate alle API di Android che se si usasse un progetto nativo in Java o Kotlin, solo che si scrive tutto in C#.

A questi 4 framework si aggiunge .NET MAUI, che in pratica è un framework che permette di scrivere codice comune code-base: in fase di compilazione quanto scritto in questo livello viene poi “tradotto” nel framework dedicato alla piattaforma.

La sentenza sopra può essere espressa in modo più professionale: .NET Maui si pone come un wrapper che traduce le chiamate alle API native per la piattaforma.

Ovviamente tutto questo “sta in piedi” perchè M$ “traduce” le API native della piattaforma in chiamate possibili negli strati superiori.

Questo porta alla seguente osservazione: per esempio quando esce una nuova versione di Android che propone nuove API, queste saranno disponibili in MAUI (sia in .NET per Android, che eventualmente per .Net MAUI) solo dopo una nuova release di M$ che aggiorna MAUI.

Usualmente questa traduzione avviene in periodi di tempi molto limitati (giorni o settimane), ma è una cosa da tenere ben presente: usando MAUI non si potranno mai usare le ultimissime API presenti nelle piattaforme.

Ma nella maggior parte dei casi questo non rappresenta un problema…..

Nota importante: spero che i più esperti mi scuseranno per l’inevitabile imprecisione di quanto scritto sinora (e anche delle esemplificazioni che farò nel seguito). Le cose esposte non sono perfettamente aderenti alla realtà poichè la situazione è più complessa, ma il modello che Vi ho proposto con queste righe funziona secondo me, e per un neofita dà nozioni agevoli da maneggiare.

Quanto scritto sinora può sembrare assolutamente pedante e di poca utilità pratica, ma non è così in realtà: MAUI dà la possibilità di scrivere codice anche nel framework nativo dedicato, con la pacifica coesistenza con il code-base.

Questa possibilità è utile per gestire situazioni molto particolari, e di cui urge un piccolo esempio.

Per iniziare diamo uno sguardo a come si presenta un progetto MAUI.

Quanto sotto la cartella Platform è da intendersi come scritto per la versione di .Net per la piattaforma.

E indovinate un pò: quanto sotto Platform/Android è codice per .Net Android, sotto Platform/iOS è codice per .Net per iOS, e così via.

Tutto quanto sta sopra fuori dalla cartella Platform è codice base-code, cioè scritto per .Net MAUI.

Lo ripeto perchè deve essere strachiaro a tutti: è raro dover mettere mano a quanto contenuto nella cartella Platform, al 99% si lavora sempre nel base-code.

Detto questo sviluppiamo un piccolo esempio per mostrare come poter interagire da base-code con codice scritto per la versione nativa del framework.

In questo esempio diciamo che è necessario rilevare le caratteristiche hardware del dispositivo su cui è in esecuzione la ns mobile app.

Facciamo finta che non esista alla scopo alcun pacchetto nuget dedicato e che occorra scrivere del codice allo scopo da zero (direi che nella pratica è cosa rara: al 99 % per ogni esigenza si ha un pacchetto nuget che esemplifica tutto, ma permettetemi di proseguire con questo esempio strampalato).

Per prima cosa creiamo un’interfaccia che ci permetterà, da base-code, di accedere all’informazione.

Ora è possibile scrivere l’implementazione dell’interfaccia, una per ogni piattaforma, che è in grado di interrogare con API native le caratteristiche del dispositivo.

/Platform/Android/DeviceInfoService.cs

In codice sopra è scritto usando .Net Android, e come si può vedere si stanno consultando API native, quindi disponibili solo se si pone il file all’interno della cartella /Platform/Android.

Se la medesima classe fosse stata scritta usando code-base .NET MAUI (cioè con il file collocato al di fuori della cartella dedicata non funzionerebbe nulla..

Proseguiamo l’esempio e scriviamo l’implementazione dell’interfaccia per iOS.

/Platform/iOS/DeviceInfoService.cs

Anche in questo caso il codice sta usando usa chiamate native per .Net iOS: mi fermo qui ma avrei potuto scrivere anloghe classi anche per le altre piattaforme.

Oss.: Vi faccio notare che le due classi DeviceInfoService hanno identico nome e stesso namespace. Questo non sarebbe normalmente possibile, ma lo è in questo caso in cui i file stanno in sottocartelle differenti di Platform.

Come consumare ora queste due classi da code-base ?

/MauiProgram.cs

Oss.: MauiProgram.cs è l’entry point di ogni applicativo MAUI, ed è il luogo delegato ad alimentare il DI di cui è equipaggiato MAUI. In defintiiva si è fornita l’implementazione dell’interfaccia.

Come detto la classe implementativa ha identico nome e namespace, e pertanto il DI andrà a iniettare nel DI solo l’istanza per cui il programma MAUI è stato compliato.

Detto in altri termini più crudi se si compila per Android tutto quanto è sotto Platform/iOS, etc etc è come non esistesse: verrebbe preso in cosiderazione solo quanto sotto /Platform/Android.

Quindi per finire è possibile usare la classe nel code-base seguente modo.

In definitiva l’istanza fornita dal DI dipenderà per quale piattaforma è compilato l’applicativo MAUI.

Oss.: Per precisione Vi segnalo che esiste un’altro metodo che permette di ottenere il medesimo risultato esposto sopra, e che è basato sulle classi partial.

Direi che per iniziare è più che sufficiente: il resto nei prossimi post.

Se avete avuto la pazienza di seguirmi sin qui grazie !

Linkografia

DotNetLiguria Workshop: Dot Net Conf 2023 – Le Novità Di .NET 8

di an