Von Mobile First zur iOS-App: Drei Wege für eine Vue-Anwendung

Eine kleine Einmaleins-App als Praxisprojekt: von der klassischen Webseite über die Progressive Web App bis zur nativen iOS-App mit Capacitor.

Eine App, drei Ausbaustufen

Am Anfang stand eine ganz normale Mobile-First-Webanwendung. Sie sollte Kindern helfen, das kleine Einmaleins zu üben. Die Idee ist bewusst einfach: Die App stellt eine Aufgabe wie „Wie viel ist: 9 × 7 = ?“, zeigt drei Antwortmöglichkeiten und zählt die richtigen Antworten. Nach jeweils sechs richtigen Antworten gibt es ein kleines Icon als Belohnung.

Keine Benutzerkonten, kein Backend, keine komplexe Geschäftslogik. Die Anwendung muss lediglich auf dem Smartphone funktionieren und Spaß machen.

Trotzdem wurde daraus ein interessantes technisches Experiment. Denn eine Webseite, die auf einem Smartphone funktioniert, ist nicht automatisch eine installierbare App. Und eine installierbare Web-App ist noch nicht dasselbe wie eine native iOS-Anwendung.

Ich habe deshalb drei Entwicklungsstufen betrachtet:

  1. Klassische Web-App: Die Anwendung liegt auf einem Webserver und wird in Safari geöffnet.
  2. Progressive Web App (PWA): Die Anwendung bleibt eine Webanwendung, bekommt aber unter anderem ein Manifest, kann zum Home-Bildschirm hinzugefügt und Offline verwendet werden.
  3. Native iOS-App mit Capacitor: Die Vue-Anwendung wird in eine native iOS-App eingebettet und über Xcode auf dem iPhone installiert.

Die eigentliche Einmaleins-Logik muss sich dabei kaum ändern. Der wesentliche Unterschied liegt darin, wie die Anwendung bereitgestellt, gestartet und in das Betriebssystem integriert wird.

Die GUI der App an sich.

1. Die drei Varianten im technischen Vergleich

MerkmalKlassische Web-AppProgressive Web AppCapacitor-App
TechnologieHTML, CSS, JavaScript, VueHTML, CSS, JavaScript, Vue plus PWA-FunktionenVue plus nativer iOS-Container
Ausführung auf iOSSafariWeb-App-Modus auf dem Home-BildschirmWKWebView innerhalb einer nativen App
BereitstellungWebserverWebserver über HTTPSiOS-App-Projekt, signiert und installiert
InstallationKeine; URL öffnenÜber „Zum Home-Bildschirm“Über Xcode, TestFlight oder App Store
ManifestNicht erforderlichFür eine sauber konfigurierte PWA sinnvollNicht erforderlich
Service WorkerNicht erforderlichFür Offline-Caching und weitere FunktionenNicht erforderlich
Offline-BetriebNur mit zusätzlicher ImplementierungMit geeignetem Service Worker und lokal verfügbaren DatenMit lokal gebündelten Webdateien möglich
Zugriff auf GerätefunktionenBrowser-APIs mit EinschränkungenBrowser-APIs mit iOS-spezifischen EinschränkungenÜber Capacitor-Plugins und native APIs
UpdatesDirekt auf dem WebserverDirekt auf dem Webserver, gegebenenfalls mit Cache-VerhaltenWeb-Build und Synchronisierung; App-Updates für native Änderungen
Apple Developer AccountNeinNeinFür bestimmte Installations- und Veröffentlichungswege erforderlich
Mac und XcodeNeinNeinFür lokale iOS-Entwicklung erforderlich
App StoreNeinNeinOptional, wenn die App darüber veröffentlicht werden soll
AufwandGeringGering bis mittelMittel bis höher
Geeignet fürWebseiten und einfache Web-ToolsInstallierbare WebanwendungenApps mit stärkerer iOS-Integration

Wichtig: Die Grenzen sind nicht absolut. Eine PWA kann beispielsweise offline funktionieren, wenn sie entsprechend entwickelt wurde. Eine Capacitor-App kann um native Funktionen erweitert werden, muss es aber nicht. Keine der drei Varianten macht eine Anwendung automatisch sicherer, schneller oder besser.

Was bedeutet das konkret?

Für meine Einmaleins-App könnte die klassische Webseite bereits ausreichen. Die Kinder öffnen einen Link, beantworten die Fragen und sehen ihre Belohnungen.

Mit einer PWA wird daraus eine Anwendung, die sich auf dem Home-Bildschirm ablegen lässt und sich im passenden Darstellungsmodus wie eine eigenständige App öffnen kann. Außerdem lässt sich auch Offline - ohne Internet - benutzen.

Mit Capacitor geht die Integration einen Schritt weiter: Ich bekomme ein echtes iOS-App-Projekt, kann native Plugins einsetzen und die App über die üblichen iOS-Entwicklungswerkzeuge testen und verteilen.

Der entscheidende Punkt ist dabei: Ich muss die Anwendung nicht dreimal entwickeln. Ich kann dieselbe Vue-Codebasis für alle drei Varianten verwenden.


2. Stufe 1: Die klassische Mobile-First-Web-App

Das Prinzip

Eine klassische Web-App ist zunächst eine Webseite. Sie wird auf einem Webserver bereitgestellt und über eine URL aufgerufen.

Für Vue 3 mit Vite sieht der grundlegende Entwicklungsablauf so aus:

npm run dev
npm run build

Der erste Befehl startet den Entwicklungsserver. Der zweite erzeugt die fertigen Webdateien, üblicherweise im Verzeichnis dist/.

Diese Dateien können auf einem gewöhnlichen Webserver, einem statischen Hosting-Anbieter oder einem CDN bereitgestellt werden. Ein Node.js-Server muss für eine rein statische Vue-Anwendung nicht dauerhaft laufen.

Was bedeutet Mobile First?

Mobile First bedeutet, dass ich das Layout zuerst für kleine Displays entwerfe. Große Schaltflächen, gut lesbare Aufgaben, ausreichend Abstand zwischen den Antwortmöglichkeiten und eine einfache Bedienung stehen im Vordergrund.

Ein mögliches Layout:

Für eine solche Anwendung ist Vue ideal: Der Zustand der aktuellen Frage, die Antwortauswahl und der Fortschritt lassen sich direkt in der Benutzeroberfläche abbilden.

Die Installation auf dem iPhone

Hier gibt es eigentlich nichts zu installieren.

  1. Die Anwendung auf einem Webserver bereitstellen.
  2. Die URL auf dem iPhone in Safari öffnen.
  3. Die Anwendung wie eine normale Webseite verwenden.
  4. Optional ein Lesezeichen anlegen.

Das ist der einfachste Weg. Änderungen am Webserver stehen nach dem nächsten Laden der Seite zur Verfügung, ohne dass eine App neu installiert oder über einen Store aktualisiert werden muss.

Die Anwendung im Browserfenster.

Vorteile und Grenzen

Der größte Vorteil ist die Einfachheit. Ich benötige weder Xcode noch einen Apple Developer Account. Auch die Bereitstellung ist unkompliziert.

Der Nachteil: Die Anwendung bleibt erkennbar eine Webseite. Browseroberfläche, Navigation und Startverhalten entsprechen dem Browser. Ein App-Icon auf dem Home-Bildschirm entsteht nicht automatisch, und Offline-Fähigkeit muss gesondert umgesetzt werden.

Für einen kleinen Rechentrainer ist das allerdings kein ernsthaftes Problem. Wenn es nur darum geht, eine Aufgabe zu stellen und eine Antwort zu prüfen, ist die klassische Web-App bereits eine vollständige Lösung.


3. Stufe 2: Aus der Webseite wird eine Progressive Web App

Was ändert sich?

Eine Progressive Web App ist keine eigene Programmiersprache und auch kein neues Frontend-Framework. Sie ist eine Webanwendung, die Webstandards nutzt, um sich stärker wie eine installierbare App zu verhalten.

Für mein Beispiel bleiben Vue, CSS und die Rechenlogik erhalten. Ich ergänze vor allem Metadaten für die Installation und – falls ich Offline-Unterstützung möchte – einen Service Worker.

Ein Web-App-Manifest beschreibt unter anderem den Namen, das Startverhalten, die Farben und die Icons der Anwendung. Ein Service Worker kann Ressourcen zwischenspeichern und damit einen Offline-Betrieb ermöglichen. Das sind zwei unterschiedliche Aufgaben: Ein Manifest ersetzt keinen Service Worker, und ein Service Worker ist nicht mit dem Manifest identisch. MDN: PWAs installierbar machen.

Schritt 1: Das Manifest anlegen

Ich lege im öffentlichen Verzeichnis des Vite-Projekts die Datei public/manifest.webmanifest an:

{
  "id": "/",
  "name": "Einmaleins-Trainer",
  "short_name": "Einmaleins",
  "description": "Das kleine Einmaleins spielerisch üben",
  "start_url": "/",
  "scope": "/",
  "display": "standalone",
  "background_color": "#ffffff",
  "theme_color": "#2563eb",
  "icons": [
    {
      "src": "/icons/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    },
    {
      "src": "/icons/icon-512.png",
      "sizes": "512x512",
      "type": "image/png"
    }
  ]
}

Die beiden Icon-Dateien müssen tatsächlich vorhanden sein. Für eine produktive PWA sollte ich außerdem den Pfad und die Konfiguration anpassen, falls die Anwendung nicht unter der Domain-Wurzel, sondern beispielsweise unter /einmaleins/ läuft.

Schritt 2: Das Manifest in HTML einbinden

In der index.html meines Vite-Projekts ergänze ich im head:

<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#2563eb">
<meta name="viewport"
      content="width=device-width, initial-scale=1">

Damit kennt der Browser das Manifest und die vorgesehene Theme-Farbe. Das allein sorgt allerdings noch nicht für einen Offline-Modus.

Schritt 3: Die Anwendung veröffentlichen

Ich erstelle wieder den normalen Produktions-Build:

npm run build

Das Verzeichnis dist/ enthält nun auch das Manifest und die öffentlichen Assets. Anschließend lade ich den Inhalt wie zuvor auf meinen Webserver.

Für eine öffentlich erreichbare PWA verwende ich HTTPS. Die Sonderfälle für lokale Entwicklungsadressen sind für die eigentliche Veröffentlichung nicht relevant.

Schritt 4: Die PWA auf dem iPhone installieren

Auf dem iPhone läuft die Installation nicht wie bei einer klassischen Desktop-Anwendung über eine frei verfügbare Installationsdatei.

Der übliche Weg ist:

  1. Die veröffentlichte URL in Safari öffnen.
  2. Das Teilen-Menü öffnen.
  3. „Zum Home-Bildschirm“ auswählen.
  4. Den Namen prüfen und die Aktion bestätigen.
  5. Das neue Icon auf dem Home-Bildschirm antippen.

Je nach iOS-Version und Browser können die Menübezeichnungen und verfügbaren Wege variieren. Das Manifest ist die Grundlage für das gewünschte Erscheinungsbild, während iOS die konkrete Installation steuert.

Über Teilen zum Home-Bildschirm hinzufügen.

Das Icon auf dem Home-Bildschirm.

Die GUI der App als Progressive Web App.

Schritt 5: Was ist mit Offline-Fähigkeit?

Hier wird es interessant. Eine PWA kann so erweitert werden, dass die Anwendung auch ohne Internetverbindung startet.

Dazu muss ein Service Worker die benötigten Dateien – etwa index.html, JavaScript, CSS und Icons – zwischenspeichern. Zusätzlich muss ich dafür sorgen, dass alle benötigten Daten lokal verfügbar sind.

Für meinen Rechentrainer ist das vergleichsweise einfach: Die Aufgaben können direkt im Browser erzeugt werden. Ein Backend ist nicht erforderlich. Wenn ich später Spielstände dauerhaft speichern möchte, kann ich beispielsweise localStorage oder IndexedDB verwenden.

Ein Service Worker sollte nicht einfach blind alle Antworten und Anfragen cachen. Für eine kleine Anwendung reicht ein gezielter Cache für die statischen Dateien. Bei Änderungen muss ich außerdem die Cache-Versionierung und das Verhalten bei neuen Releases berücksichtigen.

Mein Fazit zur PWA: Mit relativ wenig zusätzlichem Aufwand bekommt die vorhandene Web-App ein App-Icon, ein eigenständigeres Startverhalten und die Möglichkeit, offline zu funktionieren. Für viele kleine Anwendungen ist das bereits ausreichend.


4. Stufe 3: Eine native iOS-App mit Capacitor

Warum überhaupt Capacitor?

Bis hierhin habe ich die Anwendung nur im Browser ausgeführt. Die PWA kann sich auf dem Home-Bildschirm wie eine App verhalten, bleibt technisch aber eine Webanwendung.

Capacitor verfolgt einen anderen Ansatz: Es stellt eine native iOS-Hülle bereit, in der meine Webanwendung läuft. Auf iOS wird dafür WKWebView verwendet. Über Plugins kann JavaScript mit nativen Funktionen kommunizieren, beispielsweise mit Funktionen für lokale Benachrichtigungen oder Geräteinformationen.

Die Vue-Anwendung muss dafür nicht neu geschrieben werden. Capacitor wird in das vorhandene Projekt integriert. Offizielle Capacitor-Dokumentation.

Voraussetzungen

Für die lokale iOS-Entwicklung benötige ich:

Für die Veröffentlichung über den App Store kommen weitere Anforderungen hinzu, darunter die Apple-Entwicklerregistrierung, Signierung und die Prüfung der App-Store-Vorgaben.

Schritt 1: Capacitor installieren

Ich wechsle in das bestehende Vue-Projekt und installiere die benötigten Pakete:

npm install @capacitor/core
npm install -D @capacitor/cli

Danach initialisiere ich Capacitor:

npx cap init

Die CLI fragt nach dem App-Namen und einer eindeutigen App-ID, beispielsweise:

App name: Einmaleins-Trainer
App ID: de.example.einmaleins

Die App-ID sollte ich mit Bedacht wählen, da sie zur Identifikation der App in den nativen Projekten dient.

Schritt 2: Die iOS-Plattform hinzufügen

Jetzt installiere ich das iOS-Paket:

npm install @capacitor/ios
npx cap add ios

Capacitor erzeugt ein iOS-Projekt im Verzeichnis ios/. Dieses Projekt gehört zur Anwendung und sollte in der Versionsverwaltung berücksichtigt werden.

Schritt 3: Das Build-Verzeichnis kontrollieren

Meine Vue-Anwendung wird mit Vite gebaut. Das Ergebnis liegt normalerweise in dist/.

In der Capacitor-Konfiguration muss deshalb das richtige Verzeichnis angegeben sein. Eine typische capacitor.config.ts sieht so aus:

import type { CapacitorConfig } from '@capacitor/cli';

const config: CapacitorConfig = {
  appId: 'de.example.einmaleins',
  appName: 'Einmaleins-Trainer',
  webDir: 'dist'
};

export default config;

Entscheidend ist webDir: Capacitor muss wissen, wo die fertigen Webdateien liegen. Ich sollte die tatsächliche Konfiguration meines Projekts prüfen, statt mich auf automatisch gesetzte Standardwerte zu verlassen.

Schritt 4: Bauen und synchronisieren

Nun erstelle ich den Produktions-Build und synchronisiere die Dateien mit dem nativen Projekt:

npm run build
npx cap sync ios

cap sync kopiert die Webdateien in das native Projekt und synchronisiert die benötigten nativen Abhängigkeiten.

Der Unterschied zur PWA: Bei der Capacitor-App werden die Webdateien normalerweise in das iOS-App-Paket übernommen. Die App muss deshalb nicht bei jedem Start erst eine öffentlich gehostete Webseite laden.

Schritt 5: Das iOS-Projekt in Xcode öffnen

npx cap open ios

Xcode öffnet das generierte Projekt. Dort wähle ich einen Simulator oder mein verbundenes iPhone als Zielgerät und starte die Anwendung.

Beim ersten Lauf auf einem echten iPhone muss ich gegebenenfalls die Signierung konfigurieren und mein Entwicklerteam auswählen. Für die lokale Entwicklung kann ein persönliches Apple-Konto genügen; TestFlight und App-Store-Veröffentlichungen haben zusätzliche Voraussetzungen.

Das Projekt in XCode.

Die Icons beider Applikationen.

Schritt 6: Änderungen übernehmen

Die Entwicklung bleibt angenehm weborientiert. Ich ändere beispielsweise eine Vue-Komponente und baue die Anwendung erneut:

npm run build
npx cap sync ios

Anschließend starte ich die Anwendung wieder über Xcode.

Für die reguläre Entwicklung kann ich auch Live Reload nutzen. Das ist praktisch, aber nicht mit dem späteren Produktionsbetrieb gleichzusetzen: Bei Live Reload lädt die native Hülle Webinhalte von einem Entwicklungsserver.

Wenn ich nur HTML, CSS oder JavaScript ändere, kann ich die aktualisierten Webdateien in das native Projekt übernehmen. Ändere ich dagegen native Konfigurationen oder Plugins, muss ich die nativen Abhängigkeiten ebenfalls synchronisieren und gegebenenfalls neu bauen.

Was habe ich durch Capacitor gewonnen?

Der größte Gewinn ist nicht, dass meine Rechenaufgabe plötzlich besser funktioniert. Sie funktioniert genauso wie vorher.

Ich habe jetzt vielmehr einen Weg, native Funktionen gezielt zu ergänzen. Wenn der Einmaleins-Trainer später beispielsweise lokale Benachrichtigungen, zusätzliche Geräteintegration oder eine Verteilung über TestFlight benötigt, kann ich diese Möglichkeiten nutzen, ohne die bestehende Vue-Anwendung grundsätzlich neu zu schreiben.

Allerdings wächst damit auch der Aufwand. Ich muss das native Projekt pflegen, Signierung und Build-Prozess berücksichtigen und bei Änderungen die iOS-Plattform testen. Capacitor ist deshalb kein kostenloser Ersatz für alle nativen Entwicklungsaufgaben, sondern eine Brücke zwischen Webtechnologie und nativer Plattform.


5. Die gemeinsame Vue-Logik

Der entscheidende Teil meines Experiments ist, dass die Rechenlogik in allen drei Varianten dieselbe bleiben kann.

Wichtig für den Architekturvergleich ist: Weder das Manifest noch Capacitor müssen die Rechenlogik kennen. Sie betreffen die Bereitstellung und Integration der Anwendung, nicht die mathematische Aufgabe.


6. Welche Variante würde ich wählen?

Für genau diese App würde ich zunächst bei der klassischen Web-App bleiben. Wenn das Ziel ein eigener Startbildschirm und ein app-ähnliches Startverhalten ist, würde ich anschließend die PWA ergänzen.

Capacitor würde ich dann wählen, wenn ich die App tatsächlich als iOS-App verteilen oder native APIs nutzen möchte.

ZielMeine Wahl
Möglichst schnell eine funktionierende Lern-App veröffentlichenKlassische Web-App
Ein App-Icon und ein eigenständigeres StartverhaltenPWA
Offline-Nutzung mit gezieltem CachingPWA oder Capacitor, je nach Anforderungen
Native iOS-APIs und PluginsCapacitor
TestFlight oder App StoreCapacitor mit entsprechendem iOS-Build
Möglichst wenig WartungsaufwandKlassische Web-App oder PWA

7. Mein Fazit: Drei Schritte statt drei Neuentwicklungen

Die Entwicklung hat mir vor allem gezeigt, dass „auf dem Smartphone funktionieren“, „installierbar sein“ und „eine native App sein“ drei unterschiedliche Dinge sind.

Eine mobile Webseite ist nicht automatisch eine PWA. Eine PWA ist nicht automatisch eine native App. Und eine Capacitor-App muss nicht von Grund auf nativ entwickelt werden.

Für eine kleine Vue-Anwendung kann ich mit sehr wenig zusätzlichem Code mehrere Bereitstellungswege abdecken. Die eigentliche Herausforderung liegt weniger in der Rechenaufgabe als in den Details rund um Installation, Offline-Verhalten, Updates, Signierung und Verteilung.

Mein persönlicher Ansatz wäre deshalb: erst die Anwendung selbst bauen, dann die Anforderungen an die Bereitstellung klären und erst danach die nächste technische Stufe ergänzen. Nicht jede Webseite muss eine App werden. Aber wenn eine Web-App auf dem Smartphone mehr können soll, gibt es mit PWA und Capacitor zwei interessante Wege, ohne die Webtechnologie aufzugeben.

Zurück
2026-10-25 10:25