macOS App Bundle startet nicht

Ich arbeite derzeit an einer Desktop Anwendung, mit der Buchungssätze automatisch aus Rechnungen erzeugt werden (wen es interessiert, siehe https://mrcarson.de). Da die Buchungen über eine API in die Buchhaltungssoftware Monkey Office importiert werden, die nur als Desktop Anwendung zur Verfügung steht, muss auch meine Anwendung in Teilen als Desktop App implementiert werden. Und da ich auf dem Mac arbeite, muss es eine macOS Anwendung sein.

Mit .NET Core und Avalonia ist es problemlos möglich, eine Anwendung zu erstellen, die sowohl unter Windows als auch macOS ausgeführt werden kann. Soweit so gut. In der IDE, in meinem Fall JetBrains Rider, läuft alles. Doch sobald ich versuche, die Anwendung als macOS App Bundle zu starten, crasht sie. Das macOS App Bundle startet nicht.

macOS App Bundle

Anwendungen auf dem Mac werden als sogenannte App Bundles deployed. Im Grunde sind das Verzeichnisse, die eine vorgegebene Struktur haben müssen. Die Extension .app mag auf eine Datei hindeuten, doch es sind tatsächlich Verzeichnisse. Im Mac Finder kann man nicht ohne weiteres in solche Verzeichnisse hineinwechseln. Macht man einen Doppelklick auf ein Verzeichnis, das auf die Extension .app endet, versucht macOS das Verzeichnis als Anwendung zu starten. Um in das Verzeichnis zu wechseln, muss man im Finder mit der rechten Maustaste oder Ctrl-Klick das Kontextmenü des App Bundles öffnen. Dort gibt es den Menüpunkt „Paketinhalt zeigen“ mit dem in das .app Verzeichnis gewechselt wird.

Ein App Bundle Verzeichnis hat folgenden Aufbau:

  • Es muss das Verzeichnis Contents enthalten, in dem die App abgelegt ist.
  • Die Datei plist enthält alle wichtigen Angaben zur App, insbesondere, welche Datei beim Start ausgeführt werden soll.
  • Im Verzeichnis Contents/MacOS liegt das ausführbare Programm.
  • Im Verzeichnis Contents/Resources liegt eine Datei mit den Icons in diversen Größen, bspw. icns.

Es gibt noch weitere Verzeichnisse und Dateien, ich beschränke mich hier auf das Minimum.

App Bundle erstellen

Um ein App Bundle zu erstellen, muss die .NET Core Anwendung zunächst mit dem Befehl dotnet publishübersetzt werden. Dabei muss die Prozessorarchitektur beachtet werden. Diese kann entweder Intel (osx-x64) oder Arm (osx-arm64) sein. Ein beispielhafter Aufruf sieht wie folgt aus:

				
					dotnet publish ../ -c Release -r osx-arm64 -p:PublishReadyToRun=false -p:TieredCompilation=false -p:PublishTrimmed=false -p:PublishSingleFile=false --self-contained -o "../publish-macos-arm64" -p:UseAppHost=true -nowarn:CCD0001
				
			

Anschließend liegt im angegebenen Ausgabe Verzeichnis, hier publish-macos-arm64, die fertig übersetzte Anwendung. Die Anwendung kann in diesem Verzeichnis mit einem Doppelklick auf die ausführbare Datei gestartet werden.

Innerhalb der Anwendung ist das aktuelle Verzeichnis (Directory.GetCurrentDirectory()) dann das Verzeichnis, in dem sich die ausführbare Datei befindet, in diesem Fall publish-macos-arm64. Für das aktuelle Verzeichnis gilt bei Ausführung innerhalb der IDE ähnliches. Üblicherweise wird die Anwendung beim Übersetzen im Verzeichnis bin/Debug/net9.0 bzw. bin/Release/net9.0 erzeugt und von dort gestartet. Aktuelles Verzeichnis ist dann eben dieses Verzeichnis. Später wird dieses Detail relevant. Doch schauen wir uns zunächst an, wie ein App Bundle erstellt werden kann.

Dazu ist es ausreichend, die Datei Info.plist zu erstellen und das Programm in die App Bundle Verzeichnisstruktur zu kopieren. Ich mache dies über ein Bash Skript:

				
					#!/bin/bash
APP_NAME="../mrcarson-macos-arm64.app"
PUBLISH_OUTPUT_DIRECTORY="../publish-macos-arm64/."
INFO_PLIST="Info.plist"
ICON_FILE="icon.icns"

create_app() {
  echo "[INFO] Creating macOS app bundle: $1 from $2"
  if [ -d "$1" ]
  then
      rm -rf "$1"
  fi

  mkdir "$1"

  mkdir "$1/Contents"
  mkdir "$1/Contents/MacOS"
  mkdir "$1/Contents/Resources"

  cp "$INFO_PLIST" "$1/Contents/Info.plist"
  cp "$ICON_FILE" "$1/Contents/Resources/icon.icns"
  cp -a "$2" "$1/Contents/MacOS"
}

create_app "$APP_NAME" "$PUBLISH_OUTPUT_DIRECTORY"

				
			

Nachdem das Skript ausgeführt wurde, existiert im Beispiel das Verzeichnis mrcarson-macos-arm64.app. Per Doppelklick lässt sich die Anwendung starten. Fast..

Zum einen muss das App Bundle noch signiert werden. Dazu ist ein Apple Developer Account erforderlich. Auch dieses Detail lasse ich hier weg. Zum anderen gibt es noch ein klitzekleines Detail, das den Start der App verhindert.

macOS App Bundle startet nicht

Häufig verwenden Anwendungen das Dateisystem, um dort bspw. die Einstellungen des Programms abzulegen oder ein Logfile zu schreiben. In meinem Fall habe ich die Logfiles im Verzeichnis logs abgelegt. Läuft die Anwendung in der IDE, wird unterhalb von bin/Debug/net9.0 das Verzeichnis logs angelegt und die Logfiles werden geschrieben. Auch das Erstellen weiterer Dateien gelingt problemlos, wenn lediglich der Dateiname angegeben wird. Die Dateien werden dann im aktuellen Verzeichnis bin/Debug/net9.0 angelegt.

Es hat mich fast zwei Tage gekostet herauszufinden, warum meine Anwendung nicht startet, wenn ich versuche, das App Bundle zu starten:

Ein App Bundle setzt das aktuelle Verzeichnis auf das Wurzelverzeichnis „/“.

Alle Versuche, dort eine Datei oder ein Verzeichnis anzulegen scheitern natürlich, weil die Berechtigungen dazu nicht ausreichen.

Lösung

Die Lösung liegt darin, beim Start der Anwendung als erstes das aktuelle Verzeichnis zu setzen. In meinem Beispiel mache ich das wie folgt:

 

				
					var userDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "MrCarson");
if (!Directory.Exists(userDataPath)) {
    Directory.CreateDirectory(userDataPath);
}
Directory.SetCurrentDirectory(userDataPath);

				
			

Das spezielle Verzeichnis Environment.SpecialFolder.ApplicationData ist bei macOS das Verzeichnis /Users/<username>/Library/Application Support/. In diesem Verzeichnis lege ich, sofern es noch nicht existiert, ein Verzeichnis für meine Anwendung an, hier „MrCarson“. Anschließend wird das aktuelle Verzeichnis für den Prozess auf dieses Verzeichnis gesetzt. Damit läuft die Anwendung dann auch als App Bundle.

Fazit

Eine macOS Anwendung, die über ein App Bundle gestartet wird, hat ihr aktuelles Verzeichnis im Wurzelverzeichnis „/“. Wer sich diesen unsinnigen Default ausgedacht hat, sei dahingestellt. Fakt ist, dass sich eine Anwendung bewusst entscheiden muss, wo sie Dateien ablegt, statt davon auszugehen, dass das aktuelle Verzeichnis dem entspricht, aus dem die Anwendung gestartet wurde.

Unsere Seminare

course
Clean Code Developer Basics

Prinzipien und Tests – Das Seminar wendet sich an Softwareentwickler, die gerade beginnen, sich mit dem Thema Softwarequalität auseinanderzusetzen. Es werden die wichtigsten Prinzipien und Praktiken der Clean Code Developer Initiative vermittelt.

zum Seminar »
course
Clean Code Developer Advanced

Mit Flow Design von den Anforderungen zum Clean Code – Lernen Sie mit Flow Design einen Softwareentwicklungsprozess kennen, der Sie flüssig von den Anforderungen zum Clean Code führt.

zum Seminar »
course
Clean Code Developer Trainer

Seminare als Trainer durchführen – Dieses Seminar wendet sich an Softwareentwickler, die ihr Wissen über die Clean Code Developer Prinzipien und Praktiken bzw. über Flow Design als Trainer an andere weitergeben möchten.

zum Seminar »

Kommentar verfassen

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

de_DEGerman