{"id":6828,"date":"2022-09-06T17:03:37","date_gmt":"2022-09-06T17:03:37","guid":{"rendered":"https:\/\/cloudsurfers.it\/?p=6828"},"modified":"2022-09-06T17:03:38","modified_gmt":"2022-09-06T17:03:38","slug":"sviluppare-mqtt-broker-ios-net-maui","status":"publish","type":"post","link":"https:\/\/cloudsurfers.it\/index.php\/sviluppare-mqtt-broker-ios-net-maui\/","title":{"rendered":"Sviluppare un MQTT Broker su iOS con .NET MAUI"},"content":{"rendered":"<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"800\" height=\"400\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image.png\" alt=\"\" class=\"wp-image-6829\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image.png 800w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-300x150.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-768x384.png 768w\" sizes=\"auto, (max-width: 800px) 100vw, 800px\" \/><\/figure>\n<\/div>\n\n\n<p>In questa guida vedremo come realizzare un&#8217;applicazione iOS che istanzia un server MQTT raggiungibile sulla rete del dispositivo su cui questa viene eseguita.<\/p>\n\n\n\n<p>Per realizzarla utilizzeremo il framework <strong><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/what-is-maui\" target=\"_blank\" rel=\"noreferrer noopener\">.NET MAUI<\/a><\/strong> di Microsoft e la libreria <em>.NET<\/em> open source <strong><a href=\"https:\/\/github.com\/dotnet\/MQTTnet\" target=\"_blank\" rel=\"noreferrer noopener\">MQTTnet<\/a><\/strong> per la gestione del <em>broker<\/em> MQTT.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">MQTT?<\/h3>\n\n\n\n<p>Iniziamo con il dire che <strong>MQTT<\/strong> \u00e8 un protocollo di messaggistica <em>publish-subscribe<\/em> standard ISO. Questo pattern <em>publish-subscribe<\/em> richiede un <em>broker<\/em> che si occupa di distribuire i messaggi ai destinatari.<\/p>\n\n\n\n<p>Per fare un esempio, <a href=\"https:\/\/it.wikipedia.org\/wiki\/MQTT\" target=\"_blank\" rel=\"noreferrer noopener\">Facebook Messanger fa uso di MQTT<\/a> per la messaggistica tra gli utenti.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">.NET MAUI?<\/h3>\n\n\n\n<p><strong><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/\" target=\"_blank\" rel=\"noreferrer noopener\">.NET MAUI<\/a><\/strong>, invece, \u00e8 un framework multipiattaforma per la creazione di applicazioni native mobili e desktop.<\/p>\n\n\n\n<p><em>MAUI<\/em> \u00e8 l&#8217;evoluzione di <em>Xamarin.Forms<\/em> ed \u00e8 eseguibile su <em>Android<\/em>, <em>iOS<\/em>, <em>macOS<\/em> e <em>Windows<\/em> utilizzando un&#8217;unica <em>codebase<\/em>.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Preciso che in questo articolo svilupperemo su una macchina macOS con chip M1 eseguendo l&#8217;applicazione sul simulatore <em>iOS Simulator<\/em>. Il progetto sar\u00e0 comunque multipiattaforma e compatibile con i sistemi operativi Android, Windows e macOS.<\/p><\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Requisiti<\/h3>\n\n\n\n<p>Prima di iniziare con lo sviluppo vero e proprio abbiamo bisogno di soddisfare i seguenti requisiti:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/apps.apple.com\/it\/app\/xcode\/id497799835?mt=12\" target=\"_blank\" rel=\"noreferrer noopener\">Xcode<\/a>;<\/li><li><a href=\"https:\/\/developer.apple.com\/downloads\/\" target=\"_blank\" rel=\"noreferrer noopener\">Xcode Command Line Tools<\/a>;<\/li><li>Account <em>ID Apple<\/em> aggiunto nelle impostazioni di Xcode (non \u00e8 necessario essere iscritti al programma <em>Apple Developer Program<\/em>);<\/li><li><a href=\"https:\/\/visualstudio.microsoft.com\/it\/vs\/mac\/preview\/\" target=\"_blank\" rel=\"noreferrer noopener\">Visual Studio 2022 per Mac 17.4 Preview<\/a> (necessario selezionare i pacchetti: <em>.NET<\/em>, <em>.NET MAUI<\/em>, <em>Android<\/em> ed <em>iOS<\/em> <a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/get-started\/first-app?pivots=devices-ios&amp;tabs=vsmac\" target=\"_blank\" rel=\"noreferrer noopener\">in fase di installazione<\/a>);<\/li><li>Client <em>MQTT<\/em>, in questo esempio utilizzeremo il software <a href=\"https:\/\/mqttx.app\/\" target=\"_blank\" rel=\"noreferrer noopener\">MQTTX<\/a>.<\/li><\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Iniziamo<\/h3>\n\n\n\n<p>Avviamo <em>Visual Studio 2022 per Mac<\/em> e creiamo un nuovo progetto multipiattaforma <em>App .NET MAUI<\/em> selezionando il linguaggio <em>C#<\/em>:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-2.png\" alt=\"\" class=\"wp-image-6831\" width=\"690\" height=\"485\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-2.png 896w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-2-300x211.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-2-768x541.png 768w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n<\/div>\n\n\n<p>Successivamente selezioniamo: il framework <em>.NET 6<\/em>, il percorso ed il nome del progetto. In questo caso lo abbiamo chiamato <strong>MQTTBrokerDemo<\/strong>:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-3.png\" alt=\"\" class=\"wp-image-6832\" width=\"690\" height=\"292\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-3.png 896w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-3-300x127.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-3-768x326.png 768w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n<\/div>\n\n\n<p>Creando il progetto ci troveremo davanti al <em>template<\/em> base:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-4-1024x668.png\" alt=\"\" class=\"wp-image-6833\" width=\"690\" height=\"449\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-4-1024x668.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-4-300x196.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-4-768x501.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-4.png 1153w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n<\/div>\n\n\n<p>Selezioniamo un simulatore iOS dal menu a tendina in alto a sinistra, <em>iPhone 13 iOS 15.5<\/em> per esempio, ed eseguiamo.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-5-1024x694.png\" alt=\"\" class=\"wp-image-6834\" width=\"691\" height=\"468\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-5-1024x694.png 1024w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-5-300x203.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-5-768x520.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-5.png 1073w\" sizes=\"auto, (max-width: 691px) 100vw, 691px\" \/><\/figure>\n<\/div>\n\n\n<p>Vediamo subito una delle <em>chicche<\/em> di <em>.NET MAUI<\/em>, il <strong>Ricaricamento rapido XAML<\/strong>.<\/p>\n\n\n\n<p>Lasciando in esecuzione l&#8217;applicazione sul simulatore iOS, modifichiamo all&#8217;interno del file <em>MainPage.xaml<\/em> la stringa di testo &#8220;<em>Hello, World!<\/em>&#8221; con &#8220;<em>Ciao Mondo!<\/em>&#8220;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;Label\n    Text=&quot;Ciao Mondo!&quot;\n    SemanticProperties.HeadingLevel=&quot;Level1&quot;\n    FontSize=&quot;32&quot;\n    HorizontalOptions=&quot;Center&quot; \/&gt;\n<\/pre><\/div>\n\n\n<p>Senza nemmeno bisogno di salvare il file modificato sul simulatore la stringa si aggiorner\u00e0 autonomamente:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"333\" height=\"322\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-6.png\" alt=\"\" class=\"wp-image-6836\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-6.png 333w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-6-300x290.png 300w\" sizes=\"auto, (max-width: 333px) 100vw, 333px\" \/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">La struttura del progetto<\/h3>\n\n\n\n<p>Vediamo come si presenta la struttura di cartelle e file di un progetto <em>.NET MAUI<\/em>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"206\" height=\"313\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-7.png\" alt=\"\" class=\"wp-image-6837\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-7.png 206w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-7-197x300.png 197w\" sizes=\"auto, (max-width: 206px) 100vw, 206px\" \/><\/figure>\n<\/div>\n\n\n<p><strong>MauiProgram.cs<\/strong> si occupa di istanziare un oggetto <em>MauiApp<\/em>, la nostra applicazione appunto, permettendone di configurare diversi aspetti: il punto di ingresso <em>Application<\/em> (nel nostro caso <em>App<\/em>, <em>App.xaml.cs<\/em>), i font utilizzati e\/o utilizzabili, la configurazione di librerie di terze parti, ecc\u2026<\/p>\n\n\n\n<p>Visual Studio raggruppa i file <em>.xaml<\/em> con il relativo file <em>.cs<\/em> di logica in un&#8217;unico oggetto espandibile.<\/p>\n\n\n\n<p><strong>AppShell.xaml<\/strong> e <strong>MainPage.xaml<\/strong> sono pagine, rispettivamente: il contenitore e la pagina vera e propria, quella che contiene la stringa di testo &#8220;Ciao Mondo!&#8221; che abbiamo modificato in precedenza.<\/p>\n\n\n\n<p>La cartella <strong>Platform<\/strong> contiene le risorse riferite ad una specifica piattaforma. All&#8217;interno di <em>Platform\/iOS<\/em>, ad esempio, troviamo il file <em>Info.plist<\/em> utilizzato per configurare vari aspetti di un progetto Xcode come l&#8217;identificativo del pacchetto (<em>com.organization.demoapp<\/em>).<\/p>\n\n\n\n<p>Infine la cartella <strong>Resources<\/strong> contiene tutte le risorse dell&#8217;applicazione come: icone, font, stili, immagini e file generici vari.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Iniziamo lo sviluppo<\/h3>\n\n\n\n<p>L&#8217;idea \u00e8 quella di realizzare tre pagine all&#8217;interno del nostro contenitore <em>AppShell.xaml<\/em> a cui si potr\u00e0 accedere tramite una <em>TabBar<\/em> posizionata in basso alla schermata:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li>Pagina <strong>Configurazione<\/strong> per impostare il parametro della porta utilizzata dal nostro <em>broker<\/em> MQTT;<\/li><li>Pagina <strong>Client<\/strong> per mostrare l&#8217;elenco dei client connessi al <em>broker<\/em>;<\/li><li>Pagina <strong>Messaggi<\/strong> per mostrare l&#8217;elenco dei messaggi ricevuti in ingresso dai vari client connessi.<\/li><\/ul>\n\n\n\n<p>All&#8217;interno del progetto aggiungiamo quindi un nuovo file che chiamiamo <strong>ClientPage<\/strong> e selezioniamo come tipo &#8220;<em>.NET MAUI ContentPage (XAML)<\/em>&#8220;<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-8.png\" alt=\"\" class=\"wp-image-6838\" width=\"690\" height=\"528\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-8.png 769w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-8-300x229.png 300w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n<\/div>\n\n\n<p>Rifacciamo lo stesso procedimento e creiamo la pagina <strong>MessaggiPage<\/strong>.<\/p>\n\n\n\n<p>La pagina <strong>MainPage<\/strong> gi\u00e0 presente sar\u00e0 la nostra pagina di configurazione.<\/p>\n\n\n\n<p>Scarichiamo ora le tre icone da assegnare alle <em>Tab<\/em> (ho utilizzato il set di icone open source <em><a href=\"https:\/\/ionic.io\/ionicons\" target=\"_blank\" rel=\"noreferrer noopener\">Ionicons<\/a><\/em>):<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/unpkg.com\/ionicons@5.5.2\/dist\/svg\/settings-filled.svg\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/unpkg.com\/ionicons@5.5.2\/dist\/svg\/settings-filled.svg<\/a>;<\/li><li><a href=\"https:\/\/unpkg.com\/ionicons@5.5.2\/dist\/svg\/laptop-filled.svg\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/unpkg.com\/ionicons@5.5.2\/dist\/svg\/laptop-filled.svg<\/a>;<\/li><li><a href=\"https:\/\/unpkg.com\/ionicons@5.5.2\/dist\/svg\/mail-filled.svg\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/unpkg.com\/ionicons@5.5.2\/dist\/svg\/mail-filled.svg<\/a>.<\/li><\/ul>\n\n\n\n<p>Aggiungiamole come elemento esistente all&#8217;interno della cartella <em>Resources\/Images<\/em>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"309\" height=\"212\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-9.png\" alt=\"\" class=\"wp-image-6839\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-9.png 309w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-9-300x206.png 300w\" sizes=\"auto, (max-width: 309px) 100vw, 309px\" \/><\/figure>\n<\/div>\n\n\n<h4 class=\"wp-block-heading\">Piccoli problemi con iOS<\/h4>\n\n\n\n<p>Su iOS le icone in formato <em>.svg<\/em> non vengono ridimensionate a dovere quanto utilizzate come icone all&#8217;interno della <em>TabBar<\/em>, per ovviare a questo problema, bisogna aprire ognuno dei file <em>.svg<\/em> ed aggiungere la dimensione manualmente all&#8217;interno del file vettoriale come sotto:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"906\" height=\"31\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-10.png\" alt=\"\" class=\"wp-image-6840\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-10.png 906w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-10-300x10.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-10-768x26.png 768w\" sizes=\"auto, (max-width: 906px) 100vw, 906px\" \/><\/figure>\n<\/div>\n\n\n<p>In questo modo le icone verranno dimensionate correttamente all&#8217;interno della <em>TabBar<\/em>.<\/p>\n\n\n\n<p>Su GitHub si possono trovare diverse <em>issue<\/em> aperte su questa problematica e si possono usare diversi workaround per risolvere:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/github.com\/dotnet\/maui\/issues\/8786\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/dotnet\/maui\/issues\/8786<\/a><\/li><li><a href=\"https:\/\/github.com\/dotnet\/maui\/issues\/8609\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/github.com\/dotnet\/maui\/issues\/8609<\/a><\/li><\/ul>\n\n\n\n<p>Continiamo con lo sviluppo\u2026<\/p>\n\n\n\n<p>All&#8217;interno del file <em>AppShell.xaml<\/em> sostituiamo l&#8217;oggetto <em>ShellContent<\/em> presente come sotto:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;TabBar&gt;\n    &lt;ShellContent\n        Title=&quot;Configurazione&quot;\n        ContentTemplate=&quot;{DataTemplate local:MainPage}&quot;\n        Icon=&quot;settings.png&quot; \/&gt;\n\n    &lt;ShellContent\n        Title=&quot;Client&quot;\n        ContentTemplate=&quot;{DataTemplate local:ClientPage}&quot;\n        Icon=&quot;laptop.png&quot;\/&gt;\n\n    &lt;ShellContent\n        Title=&quot;Messaggi&quot;\n        ContentTemplate=&quot;{DataTemplate local:MessaggiPage}&quot;\n        Icon=&quot;mail.png&quot;\/&gt;\n&lt;\/TabBar&gt;\n<\/pre><\/div>\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\"><p>Nota: .NET MAUI converte in autonomia tutte le immagini .svg in .png. E&#8217; necessario quindi aggiungere l&#8217;estensione .png quando si fa riferimento ad un&#8217;immagine all&#8217;interno del codice.<\/p><\/blockquote>\n\n\n\n<p>Avviando l&#8217;applicazione vedremo la <em>TabBar<\/em> cliccabile con le due nuove pagine vuote in aggiunta alla pagina principale <em>MainPage<\/em> gi\u00e0 presente nel template di base.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-11.png\" alt=\"\" class=\"wp-image-6841\" width=\"250\" height=\"518\"\/><\/figure>\n<\/div>\n\n\n<h3 class=\"wp-block-heading\">Libreria MQTTNet<\/h3>\n\n\n\n<p>Aggiungiamo ora la libreria <strong><a href=\"https:\/\/github.com\/dotnet\/MQTTnet\" target=\"_blank\" rel=\"noreferrer noopener\">MQTTNet<\/a><\/strong> al progetto.<\/p>\n\n\n\n<p>Click destro sulla voce <em>Dipendenze<\/em>, <em>Gestisci pacchetti NuGet\u2026<\/em>, cerchiamo <strong>MQTTNet<\/strong> ed aggiungiamo il primo pacchetto trovato:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-12.png\" alt=\"\" class=\"wp-image-6842\" width=\"690\" height=\"432\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-12.png 895w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-12-300x188.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-12-768x482.png 768w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-12-480x300.png 480w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-12-640x400.png 640w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n<\/div>\n\n\n<p>Creiamo all&#8217;interno del progetto una nuova classe con un <em>pattern<\/em> <em>Singleton<\/em> e chiamiamola <strong>Broker<\/strong>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-13.png\" alt=\"\" class=\"wp-image-6843\" width=\"689\" height=\"529\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-13.png 770w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-13-300x230.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-13-768x589.png 768w\" sizes=\"auto, (max-width: 689px) 100vw, 689px\" \/><\/figure>\n<\/div>\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing System;\nusing MQTTnet;\nusing MQTTnet.Server;\n\nnamespace MQTTBrokerDemo\n{\n    public class Broker\n    {\n        private static Broker instance = null;\n        private static readonly object padlock = new object();\n\n        public MqttServer mqttServer;\n\n        public Broker()\n        {\n\n        }\n\n        public static Broker Instance\n        {\n            get\n            {\n                lock (padlock)\n                {\n                    if (instance == null)\n                    {\n                        instance = new Broker();\n                    }\n                    return instance;\n                }\n            }\n        }\n\n    }\n}\n<\/pre><\/div>\n\n\n<p>Utilizzando questo <em>pattern<\/em> l&#8217;oggetto <strong>mqttServer<\/strong> pu\u00f2 essere richiamato all&#8217;interno di tutta l&#8217;applicazione facendo riferimento sempre alla stessa istanza.<\/p>\n\n\n\n<p>Proseguiamo con la progettazione delle pagine.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pagina Configurazione<\/h3>\n\n\n\n<p>Apriamo il file <em>MainPage.xaml<\/em> e sostituiamo il contenuto dell&#8217;oggetto <em>VerticalStackLayout<\/em> con il seguente:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;VerticalStackLayout\n    Spacing=&quot;16&quot;\n    Padding=&quot;30,30&quot;&gt;\n\n    &lt;Label Text=&quot;Porta&quot;&gt;&lt;\/Label&gt;\n\n    &lt;Entry x:Name=&quot;entryPorta&quot;\n           Placeholder=&quot;Inserisci porta MQTT broker&quot;\n           ClearButtonVisibility=&quot;WhileEditing&quot;\n           Keyboard=&quot;Numeric&quot;\n           ReturnType=&quot;Done&quot;\n           Text=&quot;1883&quot;&gt;&lt;\/Entry&gt;\n\n    &lt;Label Text=&quot;Stato Broker&quot;&gt;&lt;\/Label&gt;\n\n    &lt;Entry x:Name=&quot;entryStatoBroker&quot;\n           IsEnabled=&quot;False&quot;\n           Text=&quot;NON AVVIATO&quot;&gt;&lt;\/Entry&gt;\n\n    &lt;Button\n        x:Name=&quot;buttonAvviaTerminaBroker&quot;\n        Text=&quot;Avvia Broker&quot;\n        Clicked=&quot;OnAvviaTerminaBrokerClicked&quot; \/&gt;\n\n&lt;\/VerticalStackLayout&gt;\n<\/pre><\/div>\n\n\n<p>Vendendo il codice nel dettaglio:<\/p>\n\n\n\n<p>Abbiamo aggiunto due oggetti di tipo <em>Entry<\/em>, casella di testo, una numerica per impostare la porta <em>TCP<\/em> utilizzata dal <em>broker MQTT<\/em>, ed una testuale disabilitata per evidenziare all&#8217;utente lo stato del server.<\/p>\n\n\n\n<p>Abbiamo inoltre aggiunto un oggetto <em>Button<\/em> che funger\u00e0 sia da pulsante di avvio che da pulsante <em>Termina Broker<\/em> a seconda dello stato di attivazione o meno del server.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-14.png\" alt=\"\" class=\"wp-image-6844\" width=\"250\" height=\"540\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-14.png 358w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-14-139x300.png 139w\" sizes=\"auto, (max-width: 250px) 100vw, 250px\" \/><\/figure>\n<\/div>\n\n\n<p>Passiamo ora al file di logica della schermata <em>MainPage.xaml.cs<\/em> e popoliamolo come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing MQTTnet;\nusing MQTTnet.Server;\n\nnamespace MQTTBrokerDemo;\n\npublic partial class MainPage : ContentPage\n{\n    bool started = false;\n\n    public MainPage()\n    {\n        InitializeComponent();      \n    }\n\n    private async void OnAvviaTerminaBrokerClicked(object sender, EventArgs e)\n    {\n        if (!started)\n        {\n            var options = new MqttServerOptionsBuilder()\n                .WithDefaultEndpoint()\n                .WithDefaultEndpointPort(Convert.ToInt32(entryPorta.Text))\n                .Build();\n\n            Broker.Instance.mqttServer = new MqttFactory().CreateMqttServer(options);\n\n            await Broker.Instance.mqttServer.StartAsync();\n\n            started = true;\n\n            entryStatoBroker.Text = &quot;AVVIATO&quot;;\n            buttonAvviaTerminaBroker.Text = &quot;Termina Broker&quot;;\n        }\n        else\n        {\n            if (Broker.Instance.mqttServer != null &amp;&amp; Broker.Instance.mqttServer.IsStarted)\n            {\n                await Broker.Instance.mqttServer.StopAsync();\n\n            }\n\n            started = false;\n\n            entryStatoBroker.Text = &quot;NON AVVIATO&quot;;\n            buttonAvviaTerminaBroker.Text = &quot;Avvia Broker&quot;;\n        }\n    }\n}\n<\/pre><\/div>\n\n\n<p>All&#8217;interno dell&#8217;evento <strong>OnAvviaTerminaBrokerClicked()<\/strong> in base allo stato del <em>broker<\/em>, avviato o meno, instanziamo un nuovo server <em>MQTT<\/em> utilizzando la porta impostata all&#8217;interno della casella di testo dedicata.<\/p>\n\n\n\n<p>Anche gli oggetti <strong>entryStatoBroker<\/strong> e <strong>buttonAvviaTerminaBroker<\/strong> vengono modificati di conseguenza per adattarsi in base allo stato del server <em>MQTT<\/em>.<\/p>\n\n\n\n<p>Avviamo l&#8217;applicazione e successivamente avviamo il <em>broker<\/em>:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-15.png\" alt=\"\" class=\"wp-image-6846\" width=\"250\" height=\"547\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-15.png 353w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-15-137x300.png 137w\" sizes=\"auto, (max-width: 250px) 100vw, 250px\" \/><\/figure>\n<\/div>\n\n\n<p>Proviamo a collegarci tramite un client <em>MQTT<\/em>, in questo caso utilizziamo <strong><a href=\"https:\/\/mqttx.app\/\" target=\"_blank\" rel=\"noreferrer noopener\">MQTTX<\/a><\/strong>:<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-16.png\" alt=\"\" class=\"wp-image-6847\" width=\"690\" height=\"503\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-16.png 1025w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-16-300x219.png 300w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-16-768x561.png 768w\" sizes=\"auto, (max-width: 690px) 100vw, 690px\" \/><\/figure>\n<\/div>\n\n\n<p>Impostiamo come <em>host<\/em> <strong>127.0.0.1<\/strong>, porta <strong>1883<\/strong> (o quella scelta in fase di avvio del <em>broker<\/em>) e connettiamoci.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"519\" height=\"123\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-17.png\" alt=\"\" class=\"wp-image-6848\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-17.png 519w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-17-300x71.png 300w\" sizes=\"auto, (max-width: 519px) 100vw, 519px\" \/><\/figure>\n<\/div>\n\n\n<p>Se tutto ha funzionato come deve il client evidenzier\u00e0 l&#8217;avvenuta connessione.<\/p>\n\n\n\n<p>Lasciamo ora aperto il client <em>MQTT<\/em> che ci servir\u00e0 pi\u00f9 avanti e passiamo a sviluppare la pagina elenco dei client connessi al <em>broker<\/em>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pagina Client<\/h3>\n\n\n\n<p>Apriamo il file <em>ClientPage.xaml<\/em> e sostituiamo il contenuto dell&#8217;oggetto <em>VerticalStackLayout<\/em> con il seguente:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;ListView Margin=&quot;30,30&quot; ItemsSource=&quot;{Binding ClientList}&quot;&gt;\n    &lt;ListView.ItemTemplate&gt;\n        &lt;DataTemplate&gt;\n            &lt;TextCell Text=&quot;{Binding Nome}&quot;&gt;&lt;\/TextCell&gt;\n\n        &lt;\/DataTemplate&gt;\n\n    &lt;\/ListView.ItemTemplate&gt;\n\n&lt;\/ListView&gt;\n<\/pre><\/div>\n\n\n<p>Abbiamo cos\u00ec creato un oggetto di tipo <strong>ListView<\/strong> che abbiamo collegato alla <em>collection<\/em> <strong>ClientList<\/strong> tramite <em>Binding<\/em> (vedremo poi di seguito la gestione lato C#).<\/p>\n\n\n\n<p>Ogni elemento della lista verr\u00e0 disegnato a schermo seguendo le indicazioni contenute nell&#8217;oggetto <strong>ItemTemplate<\/strong>. In questo caso utilizziamo un oggetto <strong>TextCell<\/strong> per mostrare solo una riga di testo.<\/p>\n\n\n\n<p>Per ogni record presente nella <em>collection<\/em> <strong>ClientList<\/strong>, verr\u00e0 mostrata la sua propriet\u00e0 <strong>Nome<\/strong> (che corrisponder\u00e0 all&#8217;identificativo del client <em>MQTT<\/em> connesso).<\/p>\n\n\n\n<p>Passiamo ora la logica della pagina compilando il file <em>ClientPage.xaml.cs<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing System.Collections.ObjectModel;\n\nnamespace MQTTBrokerDemo;\n\npublic class Client\n{\n    public string Nome { get; set; }\n}\n\npublic partial class ClientPage : ContentPage\n{\n    private ObservableCollection&lt;Client&gt; _clientList { get; set; }\n    public ObservableCollection&lt;Client&gt; ClientList {\n        get { return _clientList; }\n        set\n        {\n            _clientList = value;\n            OnPropertyChanged(nameof(ClientList));\n        }\n    }\n\n    protected override void OnAppearing()\n    {\n        base.OnAppearing();\n\n        if (Broker.Instance.mqttServer != null)\n        {\n            Broker.Instance.mqttServer.ClientConnectedAsync += MqttServer_ClientConnectedAsync;\n            Broker.Instance.mqttServer.ClientDisconnectedAsync += MqttServer_ClientDisconnectedAsync;\n\n            UpdateList();\n        }\n    }\n\n    protected override void OnDisappearing()\n    {\n        base.OnDisappearing();\n\n        if (Broker.Instance.mqttServer != null)\n        {\n            Broker.Instance.mqttServer.ClientConnectedAsync -= MqttServer_ClientConnectedAsync;\n            Broker.Instance.mqttServer.ClientDisconnectedAsync -= MqttServer_ClientDisconnectedAsync;\n        }\n    }\n\n    private void UpdateList()\n    {\n        MainThread.BeginInvokeOnMainThread(() =&gt;\n        {\n            ClientList.Clear();\n\n            var clients = Broker.Instance.mqttServer.GetClientsAsync().GetAwaiter().GetResult();\n            foreach (var client in clients)\n            {\n                ClientList.Add(new Client()\n                {\n                    Nome = client.Id\n                });\n            }\n        });\n    }\n\n    private Task MqttServer_ClientDisconnectedAsync(MQTTnet.Server.ClientDisconnectedEventArgs arg)\n    {\n        UpdateList();\n\n        return Task.CompletedTask;\n    }\n\n    private Task MqttServer_ClientConnectedAsync(MQTTnet.Server.ClientConnectedEventArgs arg)\n    {\n        UpdateList();\n\n        return Task.CompletedTask;\n    }\n\n    public ClientPage()\n    {\n        InitializeComponent();\n\n        ClientList = new ObservableCollection&lt;Client&gt;();\n\n        BindingContext = this;\n    }\n}\n<\/pre><\/div>\n\n\n<p>Vediamolo passo passo\u2026<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic class Client\n{\n    public string Nome { get; set; }\n}\n<\/pre><\/div>\n\n\n<p>Qui dichiariamo l&#8217;oggetto <strong>Client<\/strong> con una sola propriet\u00e0 di tipo stringa chiamata <strong>Nome<\/strong>.<br>Ogni client <em>MQTT<\/em> che si collega al <em>broker<\/em> si presenta con un identificativo che sar\u00e0 il nome mostrato in lista.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate ObservableCollection&lt;Client&gt; _clientList { get; set; }\npublic ObservableCollection&lt;Client&gt; ClientList {\n    get { return _clientList; }\n    set\n    {\n        _clientList = value;\n        OnPropertyChanged(nameof(ClientList));\n    }\n}\n<\/pre><\/div>\n\n\n<p>Successivamente creiamo una <em><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/api\/system.collections.objectmodel.observablecollection-1?view=net-6.0\" target=\"_blank\" rel=\"noreferrer noopener\">ObservableCollection<\/a><\/em> di oggetti di tipo <strong>Client<\/strong> e, molto importante, quando impostiamo la <em>collection<\/em>, <strong>set<\/strong>, \u00e8 necessario notificare alla <em>GUI<\/em> <em>MAUI<\/em> la propriet\u00e0 che subisce una modifica, in questo caso l&#8217;intera lista.<\/p>\n\n\n\n<p>L&#8217;utilizzo di collezioni di tipo <em><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/api\/system.collections.objectmodel.observablecollection-1?view=net-6.0\" target=\"_blank\" rel=\"noreferrer noopener\">ObservableCollection<\/a><\/em> \u00e8 necessario per permettere alla lista di aggiornarsi in tempo reale all&#8217;interno dell&#8217;interfaccia grafica.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprotected override void OnAppearing()\n{\n    base.OnAppearing();\n\n    if (Broker.Instance.mqttServer != null)\n    {\n        Broker.Instance.mqttServer.ClientConnectedAsync += MqttServer_ClientConnectedAsync;\n        Broker.Instance.mqttServer.ClientDisconnectedAsync += MqttServer_ClientDisconnectedAsync;\n\n        UpdateList();\n    }\n}\n\nprotected override void OnDisappearing()\n{\n    base.OnDisappearing();\n\n    if (Broker.Instance.mqttServer != null)\n    {\n        Broker.Instance.mqttServer.ClientConnectedAsync -= MqttServer_ClientConnectedAsync;\n        Broker.Instance.mqttServer.ClientDisconnectedAsync -= MqttServer_ClientDisconnectedAsync;\n    }\n}\n\nprivate Task MqttServer_ClientDisconnectedAsync(MQTTnet.Server.ClientDisconnectedEventArgs arg)\n{\n    UpdateList();\n\n    return Task.CompletedTask;\n}\n\nprivate Task MqttServer_ClientConnectedAsync(MQTTnet.Server.ClientConnectedEventArgs arg)\n{\n    UpdateList();\n\n    return Task.CompletedTask;\n}\n<\/pre><\/div>\n\n\n<p>Successivamente, all&#8217;apparizione della pagina a schermo, intercettiamo gli eventi <strong>ClientConnected<\/strong> e <strong>ClientDisconnected<\/strong> e, quando invocati, aggiorniamo la lista dei client connessi tramite il metodo <strong>UpdateList()<\/strong> che vedremo di seguito.<\/p>\n\n\n\n<p>I due eventi ci permetteranno di aggiornare la pagina in tempo reale nel caso un client <em>MQTT<\/em> si connetta o disconnetta dal <em>broker<\/em>.<\/p>\n\n\n\n<p>Quando la pagina scompare dallo schermo, <strong>OnDisappearing<\/strong>, i due eventi vengono eliminati.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nprivate void UpdateList()\n{\n    MainThread.BeginInvokeOnMainThread(() =&gt;\n    {\n        ClientList.Clear();\n\n        var clients = Broker.Instance.mqttServer.GetClientsAsync().GetAwaiter().GetResult();\n        foreach (var client in clients)\n        {\n            ClientList.Add(new Client()\n            {\n                Nome = client.Id\n            });\n        }\n    });\n}\n<\/pre><\/div>\n\n\n<p><strong>UpdateList()<\/strong> ci permette di ottenere dall&#8217;istanza del server <em>MQTT<\/em> i client connessi ed aggiungerli cos\u00ec alla <em>collection<\/em>.<\/p>\n\n\n\n<p>Quando vengono effettuate operazioni sulla collezione <strong>ClientList<\/strong>, bisogna accertarsi che vengano effettuate sul <em>Thread<\/em> principale. Ecco perch\u00e8 l&#8217;utilizzo del metodo <strong>BeginInvokeOnMainThread<\/strong>.<\/p>\n\n\n\n<p>Questo passaggio in questo caso \u00e8 necessario in quanto il metodo <strong>UpdateList()<\/strong> viene richiamato all&#8217;interno di <em><a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/api\/system.threading.tasks.task?view=net-6.0\" target=\"_blank\" rel=\"noreferrer noopener\">Task<\/a><\/em> evento dell&#8217;<em>MQTT<\/em> server, <em>Task<\/em> che vengono eseguiti su processi paralleli a quello principale.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic ClientPage()\n{\n    InitializeComponent();\n\n    ClientList = new ObservableCollection&lt;Client&gt;();\n\n    BindingContext = this;\n}\n<\/pre><\/div>\n\n\n<p>All&#8217;interno del costruttore viene inizializzata la <em>collection<\/em> ed impostato l&#8217;oggetto <strong>BindingContext<\/strong> come istanza corrente <em>ClientPage<\/em>, cos\u00ec da poter utilizzare <strong>ClientList<\/strong> per il <em>binding<\/em> <em>ItemsSource<\/em> della <em>ListView<\/em> vista sopra (<em>ClientPage.xaml<\/em>).<\/p>\n\n\n\n<p>Non ci resta che eseguire l&#8217;applicativo, avviare il broker <em>MQTT<\/em> tramite l&#8217;apposito pulsante, posizionarci sulla pagina <em>Client<\/em> e collegare ad esso un client <em>MQTT<\/em>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-18.png\" alt=\"\" class=\"wp-image-6849\" width=\"250\" height=\"547\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-18.png 354w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-18-137x300.png 137w\" sizes=\"auto, (max-width: 250px) 100vw, 250px\" \/><\/figure>\n<\/div>\n\n\n<p>Il client collegato appare correttamente nella lista.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Pagina Messaggi<\/h3>\n\n\n\n<p>Prendendo spunto dalla pagina <em>Client<\/em> appena creata, progettiamo la pagina <strong>Messaggi<\/strong>, del tutto simile a quella appena vista, con la differenza che al posto dei client connessi verranno mostrati i messaggi ricevuti da questi.<\/p>\n\n\n\n<p>Apriamo il file <em>MessaggiPage.xaml<\/em> e sostituiamo l&#8217;oggetto <em>VerticalStackLayout<\/em> come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: xml; title: ; notranslate\" title=\"\">\n&lt;ListView Margin=&quot;30,30&quot; ItemsSource=&quot;{Binding Messages}&quot;&gt;\n    &lt;ListView.ItemTemplate&gt;\n        &lt;DataTemplate&gt;\n            &lt;TextCell Text=&quot;{Binding Payload}&quot; Detail=&quot;{Binding ClientInfo}&quot;&gt;&lt;\/TextCell&gt;\n\n        &lt;\/DataTemplate&gt;\n\n    &lt;\/ListView.ItemTemplate&gt;\n\n&lt;\/ListView&gt;\n<\/pre><\/div>\n\n\n<p>Il codice \u00e8 quasi interamente uguale al precedente con l&#8217;aggiunta della propriet\u00e0 <em>Detail<\/em> che ci permette di mostrare una seconda riga alla cella di testo (<em>TextCell<\/em>).<\/p>\n\n\n\n<p>Compiliamo poi il file di logica <em>MessaggiPage.xaml.cs<\/em> come segue:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing System.Collections.ObjectModel;\n\nnamespace MQTTBrokerDemo;\n\npublic class Message\n{\n    public string Payload { get; set; }\n    public string ClientInfo { get; set; }\n}\n\npublic partial class MessaggiPage : ContentPage\n{\n    private ObservableCollection&lt;Message&gt; _messages { get; set; }\n    public ObservableCollection&lt;Message&gt; Messages\n    {\n        get { return _messages; }\n        set\n        {\n            _messages = value;\n            OnPropertyChanged(nameof(Messages));\n        }\n    }\n\n    protected override void OnAppearing()\n    {\n        base.OnAppearing();\n\n        if (Broker.Instance.mqttServer != null)\n        {\n            Broker.Instance.mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;\n        }\n    }\n\n    protected override void OnDisappearing()\n    {\n        base.OnDisappearing();\n\n        if (Broker.Instance.mqttServer != null)\n        {\n            Broker.Instance.mqttServer.InterceptingPublishAsync -= MqttServer_InterceptingPublishAsync;\n        }\n    }\n\n    private Task MqttServer_InterceptingPublishAsync(MQTTnet.Server.InterceptingPublishEventArgs arg)\n    {\n        MainThread.BeginInvokeOnMainThread(() =&gt;\n        {\n            Messages.Insert(0, new Message()\n            {\n                Payload = System.Text.Encoding.Default.GetString(arg.ApplicationMessage.Payload),\n                ClientInfo = $&quot;{arg.ClientId} - Topic: {arg.ApplicationMessage.Topic}&quot;\n            });\n        });\n\n        return Task.CompletedTask;\n    }\n\n    public MessaggiPage()\n    {\n        InitializeComponent();\n\n        Messages = new ObservableCollection&lt;Message&gt;();\n\n        BindingContext = this;\n    }\n}\n<\/pre><\/div>\n\n\n<p>Vediamo pi\u00f9 nel dettaglio le differenze che ci sono con la pagina <em>Client<\/em>:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nBroker.Instance.mqttServer.InterceptingPublishAsync += MqttServer_InterceptingPublishAsync;\n<\/pre><\/div>\n\n\n<p>Qui l&#8217;evento che andiamo ad intercettare \u00e8 <strong>InterceptingPublishAsync<\/strong>, invocato ogniqualvolta il broker <em>MQTT<\/em> riceve un messaggio da un client ad esso collegato.<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nMqttServer_InterceptingPublishAsync(MQTTnet.Server.InterceptingPublishEventArgs arg)\n{\n    MainThread.BeginInvokeOnMainThread(() =&gt;\n    {\n        Messages.Insert(0, new Message()\n        {\n            Payload = System.Text.Encoding.Default.GetString(arg.ApplicationMessage.Payload),\n            ClientInfo = $&quot;{arg.ClientId} - Topic: {arg.ApplicationMessage.Topic}&quot;\n        });\n    });\n\n    return Task.CompletedTask;\n}\n<\/pre><\/div>\n\n\n<p>A differenza della pagina precedente, qui non viene pulita la <em>collection<\/em> ma viene sempre inserito in cima l&#8217;ultimo messaggio ricevuto.<\/p>\n\n\n\n<p>Eseguiamo il tutto, colleghiamo il client <em>MQTT<\/em> ed inviamo qualche messaggio su un topic di esempio, che noi abbiamo chiamato <em>demo<\/em> (lato client viene eseguita un&#8217;operazione <em>publish<\/em>).<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-19.png\" alt=\"\" class=\"wp-image-6850\" width=\"250\" height=\"538\" srcset=\"https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-19.png 357w, https:\/\/cloudsurfers.it\/wp-content\/uploads\/2022\/09\/image-19-139x300.png 139w\" sizes=\"auto, (max-width: 250px) 100vw, 250px\" \/><\/figure>\n<\/div>\n\n\n<p>I messaggi ricevuti vengono mostrati in tempo reale sul dispositivo.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Conclusioni<\/h3>\n\n\n\n<p><em><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/what-is-maui\" target=\"_blank\" rel=\"noreferrer noopener\">.NET MAUI<\/a><\/em> \u00e8 un rivale di pesi massimi del calibro di <em><a href=\"https:\/\/ionicframework.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Ionic Framework<\/a><\/em> e <em><a href=\"https:\/\/flutter.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">Flutter<\/a><\/em>.<\/p>\n\n\n\n<p>E&#8217; certamente un gran bel passo avanti rispetto al precedente prodotto Microsoft <em>Xamarin.Forms<\/em>, ma, dal nostro punto di vista, presenta ancora alcune carenze.<\/p>\n\n\n\n<p>La documentazione, per esempio, risulta completa e lacunosa allo stesso tempo, molto dettagliata come Microsoft ci ha abituato ma in certi casi un livello di dettaglio troppo elevato che rende confusionario cercare cose semplici come ad esempio i controlli GUI da poter utilizzare all&#8217;interno di una pagina, <em>Label<\/em>, <em>Entry<\/em>, ecc\u2026. <em>Ionic Framework<\/em> da questo punto di vista, lo troviamo molto pi\u00f9 intuitivo.<\/p>\n\n\n\n<p>L&#8217;export multipiattaforma tramite un&#8217;unica <em>codebase<\/em> funziona molto bene, la grafica si adegua al sistema correttamente, cos\u00ec come sui framework concorrenti.<\/p>\n\n\n\n<p>Qualche incertezza per\u00f2 c&#8217;\u00e8 ancora, basti vedere il paragrafo di creazione della <em>TabBar<\/em> dove siamo stati obbligati ad impostare manualmente la dimensione delle icone perch\u00e9 sul sistema operativo iOS il ridimensionamento automatico non funziona (su Android, invece, funziona senza alcun <em>workaround<\/em>).<\/p>\n\n\n\n<p>Il parco librerie di terze parti \u00e8 abbastanza grande, liste come <em><a href=\"https:\/\/github.com\/jsuarezruiz\/awesome-dotnet-maui\" target=\"_blank\" rel=\"noreferrer noopener\">Awesome .NET MAUI<\/a><\/em> aiutano a trovare il componente di cui abbiamo bisogno ed anche a scoprire nuove potenzialit\u00e0 del framework.<\/p>\n\n\n\n<p>Per concludere, <em>.NET MAUI<\/em> non \u00e8 perfetto e non \u00e8 ancora completo rispetto alla concorrenza. La possibilit\u00e0 per\u00f2 di utilizzare librerie <em>.NET<\/em> su mobile, librerie come <em><a href=\"https:\/\/github.com\/dotnet\/MQTTnet\" target=\"_blank\" rel=\"noreferrer noopener\">MQTTNet<\/a><\/em>, lo rendono per certi versi unico.<\/p>\n\n\n\n<p>Realizzare un semplice applicativo <em>MQTT<\/em> server come quello visto sopra, con altri framework non sarebbe stato altrettanto semplice, se non impossibile.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">GItHub<\/h3>\n\n\n\n<p>Il progetto <em>.NET MAUI<\/em> realizzato in questo articolo \u00e8 disponibile su <a href=\"https:\/\/github.com\/Cloudsurfers-Dev\/MQTTBrokerDemo\">GitHub<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fonti<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/what-is-maui\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/what-is-maui<\/a><\/li><li><a href=\"https:\/\/it.wikipedia.org\/wiki\/MQTT\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/it.wikipedia.org\/wiki\/MQTT<\/a><\/li><li><a href=\"https:\/\/ionic.io\/ionicons\" target=\"_blank\" rel=\"noreferrer noopener\">https:\/\/ionic.io\/ionicons<\/a><\/li><\/ul>\n","protected":false},"excerpt":{"rendered":"<p><!-- wp:paragraph --><\/p>\n<p>In questa guida vedremo come realizzare un&#8217;applicazione iOS che istanzia un server MQTT raggiungibile sulla rete del dispositivo su cui questa viene eseguita.<\/p>\n<p><!-- \/wp:paragraph --><\/p>\n<p><!-- wp:paragraph --><\/p>\n<p>Per realizzarla utilizzeremo il framework <strong><a href=\"https:\/\/docs.microsoft.com\/it-it\/dotnet\/maui\/what-is-maui\" target=\"_blank\" rel=\"noreferrer noopener\">.NET MAUI<\/a><\/strong> di Microsoft e la libreria <em>.NET<\/em> open source <strong><a href=\"https:\/\/github.com\/dotnet\/MQTTnet\" target=\"_blank\" rel=\"noreferrer noopener\">MQTTnet<\/a><\/strong> per la gestione del <em>broker<\/em> MQTT.<\/p>\n<p><!-- \/wp:paragraph --><\/p>\n","protected":false},"author":3,"featured_media":6829,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"wds_primary_category":0,"footnotes":""},"categories":[90,188,36],"tags":[94,189,96,111,157,183,190,191,110],"class_list":["post-6828","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-net-core","category-net-maui","category-guide","tag-net-core","tag-net-maui","tag-app","tag-c","tag-ios","tag-mobile","tag-mqtt","tag-mqtt-broker","tag-tutorial"],"_links":{"self":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/6828","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/comments?post=6828"}],"version-history":[{"count":0,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/posts\/6828\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media\/6829"}],"wp:attachment":[{"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/media?parent=6828"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/categories?post=6828"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudsurfers.it\/index.php\/wp-json\/wp\/v2\/tags?post=6828"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}