Guida

Compilare la tua app .NET MAUI per iOS senza possedere un Mac

Puoi scrivere un’app .NET MAUI in Visual Studio su Windows. La build iOS richiede comunque un Mac, perché richiede Xcode. La risposta di Microsoft è Pair to Mac, che comanda un Mac via SSH. Questa pagina mostra una strada più semplice che abbiamo testato: installare .NET su un Mac e compilare con dotnet nel suo terminale.

Perché MAUI ha bisogno di un Mac per iOS

La pagina di Microsoft su Pair to Mac è chiara. Per compilare app iOS servono gli strumenti di build di Apple, e girano solo su un Mac. Visual Studio su Windows invia la build a un Mac via SSH. Il Mac compila e firma l’app.

La vecchia scorciatoia non c’è più. Microsoft ha ritirato Visual Studio per Mac il 31 agosto 2024. Su un Mac ora usi la riga di comando dotnet, oppure un editor come VS Code con il C# Dev Kit.

La strada che abbiamo testato: dotnet sul Mac

Collegati al Mac via SSH e installa il .NET SDK. Poi aggiungi il workload MAUI e compila. Il nostro test ha usato lo script di installazione di Microsoft e una nuova app dal template MAUI. Questo è il job di build del workflow, esattamente come è passato sul runner macos-26 di GitHub il 19 ottobre 2026.

name: maui-ios
on: workflow_dispatch
jobs:
  build-ios-xcode-26-6:
    runs-on: macos-26
    steps:
      - name: Install .NET SDK 10.0.401 into the home folder
        run: |
          curl -sSL https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh
          bash dotnet-install.sh --version 10.0.401 --install-dir "$HOME/.dotnet"
          echo "DOTNET_ROOT=$HOME/.dotnet" >> "$GITHUB_ENV"
          echo "$HOME/.dotnet" >> "$GITHUB_PATH"
      - run: dotnet --version
      - name: Install the MAUI workload (workload set 10.0.401)
        run: time dotnet workload install maui --version 10.0.401
      - run: dotnet workload --info
      - run: sudo xcode-select -s /Applications/Xcode_26.6.app
      - run: xcodebuild -version && sw_vers
      - run: dotnet new maui -n MauiDemo
      - run: grep -n "TargetFrameworks" MauiDemo/MauiDemo.csproj
      - name: Build for the iOS Simulator (Debug)
        run: time dotnet build -f net10.0-ios -c Debug
        working-directory: MauiDemo
      - name: Build for a device, unsigned (Release, ios-arm64)
        run: time dotnet build -f net10.0-ios -c Release -r ios-arm64 -p:EnableCodeSigning=false
        working-directory: MauiDemo
      - name: Publish for a device, unsigned
        continue-on-error: true
        run: time dotnet publish -f net10.0-ios -c Release -r ios-arm64 -p:EnableCodeSigning=false
        working-directory: MauiDemo
      - run: find bin -maxdepth 5 \( -name "*.app" -o -name "*.ipa" \) -print -exec du -sh {} \;
        working-directory: MauiDemo

Sul tuo Mac, esegui gli stessi comandi via SSH. Lo script di installazione mette .NET nella tua cartella home, quindi non serve sudo. Imposta queste due variabili in ogni nuovo terminale, oppure aggiungile a ~/.zshrc:

export DOTNET_ROOT=$HOME/.dotnet
export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools

Installa il workload maui completo, non maui-ios. Il nostro primo test provava solo maui-ios. Il restore è fallito con NETSDK1147, perché il template punta anche ad Android e mancava il workload android.

EnableCodeSigning=false ti permette di provare una build per dispositivo senza certificato. Il risultato non può andare su un telefono né ad Apple. Dimostra solo che il tuo codice compila per ios-arm64.

Cosa ha stampato l’esecuzione

Queste righe sono copiate dal log del job. L’intero job è durato 3 minuti e 21 secondi.

Xcode 26.6
Build version 17F113
 Workload version: 10.0.401
Successfully installed workload(s) maui.
  MauiDemo -> .../bin/Debug/net10.0-ios/iossimulator-arm64/MauiDemo.dll
Build succeeded.
Time Elapsed 00:00:23.70
  MauiDemo -> .../bin/Release/net10.0-ios/ios-arm64/MauiDemo.dll
Build succeeded.
Time Elapsed 00:02:06.70
  Created the package: bin/Release/net10.0-ios/ios-arm64/publish//MauiDemo.ipa
 97M	bin/Debug/net10.0-ios/iossimulator-arm64/MauiDemo.app
 16M	bin/Release/net10.0-ios/ios-arm64/publish/MauiDemo.ipa

La build per il simulatore ha richiesto 24 secondi. La build per dispositivo poco più di 2 minuti, perché una build Release compila in anticipo per arm64.

Abbinare il workload a Xcode

Ogni release di .NET per iOS richiede una versione esatta di Xcode. Le note di rilascio su GitHub, in dotnet/macios, la indicano. Al 19 ottobre 2026, questi sono i workload set di .NET 10:

  • 10.0.402.1 (iOS 27.1.10018) richiede Xcode 27.1. È uscito il 19 ottobre 2026, con il .NET SDK 10.0.402.
  • 10.0.402 (iOS 27.0.10722) richiede Xcode 27.0.
  • 10.0.401 (iOS 26.5.10318) richiede Xcode 26.6. È la coppia che abbiamo eseguito.

Abbiamo anche eseguito di proposito la coppia sbagliata. Con il workload set 10.0.402.1 e Xcode 26.6 selezionato, la build si è fermata con questo errore:

error : This version of .NET for iOS (27.1.10018) requires Xcode 27.1.
The current version of Xcode is 26.6. Either install Xcode 27.1, or use
a different version of .NET for iOS.

Per Xcode 27.1, il default sui nostri Mac, le note di rilascio indicano questa coppia. Non abbiamo potuto eseguirla, perché l’immagine macos-26 di GitHub non ha Xcode 27.

bash dotnet-install.sh --version 10.0.402 --install-dir "$HOME/.dotnet"
dotnet workload install maui --version 10.0.402.1

L’altra soluzione di Microsoft è tenere più versioni di Xcode e passare dall’una all’altra. Controlla quale è attiva, elenca quelle installate e scegline una:

xcode-select -p
ls -d /Applications/Xcode*.app
sudo xcode-select -s /Applications/Xcode.app
xcodebuild -version

Su un Mac MacRun, /Applications/Xcode.app è Xcode 26.6 e /Applications/Xcode-27.1.app è Xcode 27.1, il default. Su un altro Mac, usa il nome esatto dell’app che stampa ls. Puoi anche impostare DEVELOPER_DIR per una sola sessione del terminale. Microsoft segnala anche un cambiamento incompatibile per Xcode 27. Le app iOS devono adottare il ciclo di vita UIScene, altrimenti vanno in crash all’avvio.

Eseguire, firmare e pubblicare sul Mac

Per avviare l’app nel simulatore, la guida CLI di Microsoft usa il target Run. Guardi il simulatore attraverso lo schermo del Mac, da Windows via VNC.

dotnet build -t:Run -f net10.0-ios

Per l’App Store, aggiungi la tua identità di firma e il profilo. Questo è l’esempio di Microsoft con net8.0 cambiato in net10.0. Non abbiamo potuto eseguirlo, perché il nostro test non aveva un certificato.

dotnet publish -f net10.0-ios -c Release -p:ArchiveOnBuild=true \
  -p:RuntimeIdentifier=ios-arm64 \
  -p:CodesignKey="Apple Distribution: John Smith (AY2GDE9QM7)" \
  -p:CodesignProvision="MyMauiApp"

Il .ipa finisce in bin/Release/net10.0-ios/ios-arm64/publish. Per l’upload Microsoft indica Transporter. La nostra guida su come pubblicare senza Mac copre chiavi API e TestFlight.

Pair to Mac da Visual Studio: cosa chiede la documentazione

Forse preferisci restare in Visual Studio su Windows. In quel caso Pair to Mac richiede quanto segue, secondo la documentazione di Microsoft:

  • Login remoto attivo nelle impostazioni di Condivisione del Mac, con accesso completo al disco per gli utenti remoti.
  • Xcode installato e aperto una volta. Pair to Mac non può installare Xcode.
  • Mono installato, e Rosetta su un Mac con Apple silicon.
  • Un Mac raggiungibile da Windows. Se non compare nella lista, usa Add Mac e scrivi il suo indirizzo.

Visual Studio poi installa .NET sul Mac via SSH. Su un Mac MacRun, l’indirizzo sarebbe il suo indirizzo Tailscale. Non abbiamo testato Pair to Mac via Tailscale, quindi consideralo non testato. La strada con dotnet qui sopra è quella che abbiamo eseguito.

Chi non dovrebbe noleggiare un Mac per questo

  • Solo build. Il workflow qui sopra gira su GitHub così com’è. I repository pubblici non pagano nulla per i runner standard. Quelli privati pagano $0.062 al minuto per macOS, secondo la pagina di fatturazione di GitHub. Il nostro job da 3 minuti e 21 secondi viene fatturato come 4 minuti, circa 25 centesimi.
  • Debug sul tuo iPhone. Hot Restart in Visual Studio 2022 installa build Debug su un iPhone collegato via USB senza Mac. Non è supportato in Visual Studio 2026.
  • Lavoro iOS quotidiano. Se ti serve il Mac ogni giorno, comprarne uno può costare meno nell’arco di un anno.

Quando conviene un Mac a noleggio

Un job di CI non può mostrarti l’app. Un Mac a noleggio ti dà il simulatore, il debugger e una configurazione di firma che puoi sistemare a mano. Un pass giornaliero MacRun è un Mac mini M6 dedicato a $15, e una settimana costa $59. Xcode 27.1 è il default ed è installato anche Xcode 26.6. .NET non è preinstallato. Lo installi con i comandi qui sopra. L’installazione del workload ha richiesto 34 secondi nel nostro test.

Il Mac viene cancellato quando il pass finisce. Prima fai push del codice e copia altrove il tuo .ipa. Vedi Xcode da Windows per configurare lo schermo remoto.

Domande frequenti

Posso compilare un’app .NET MAUI per iOS su Windows senza Mac?

+

Non una build completa. La documentazione di Microsoft dice che gli strumenti di build per iOS girano solo su un Mac. Visual Studio invia la build a un Mac tramite Pair to Mac, oppure compili direttamente su un Mac con dotnet.

Quale Xcode serve a .NET 10 MAUI?

+

Dipende dal workload set. Al 19 ottobre 2026, il workload set 10.0.402.1 richiede Xcode 27.1, e 10.0.401 richiede Xcode 26.6. Le note di rilascio di dotnet/macios indicano lo Xcode di ogni release.

Perché la mia build MAUI dice che serve un altro Xcode?

+

Il tuo workload e lo Xcode selezionato non corrispondono. Cambia Xcode con sudo xcode-select -s, oppure installa il workload set adatto al tuo Xcode con dotnet workload install maui --version.

Visual Studio per Mac è ancora disponibile?

+

No. Microsoft ha ritirato Visual Studio per Mac il 31 agosto 2024. Su un Mac, usa la riga di comando dotnet o VS Code con il C# Dev Kit.

Pair to Mac funziona con un Mac in cloud?

+

Microsoft chiede un Mac raggiungibile da Windows, con il login remoto attivo. Non abbiamo testato Pair to Mac via Tailscale. Compilare con dotnet nel terminale del Mac è la strada che abbiamo testato.

Posso compilare un’app MAUI per iOS senza firma?

+

Sì. Nel nostro test, dotnet build con -r ios-arm64 e -p:EnableCodeSigning=false ha compilato un’app per dispositivo senza certificato. Non si può installare né caricare finché non la firmi.

Guide correlate