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:
- Klassische Web-App: Die Anwendung liegt auf einem Webserver und wird in Safari geöffnet.
- 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.
- 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.

1. Die drei Varianten im technischen Vergleich
| Merkmal | Klassische Web-App | Progressive Web App | Capacitor-App |
|---|
| Technologie | HTML, CSS, JavaScript, Vue | HTML, CSS, JavaScript, Vue plus PWA-Funktionen | Vue plus nativer iOS-Container |
| Ausführung auf iOS | Safari | Web-App-Modus auf dem Home-Bildschirm | WKWebView innerhalb einer nativen App |
| Bereitstellung | Webserver | Webserver über HTTPS | iOS-App-Projekt, signiert und installiert |
| Installation | Keine; URL öffnen | Über „Zum Home-Bildschirm“ | Über Xcode, TestFlight oder App Store |
| Manifest | Nicht erforderlich | Für eine sauber konfigurierte PWA sinnvoll | Nicht erforderlich |
| Service Worker | Nicht erforderlich | Für Offline-Caching und weitere Funktionen | Nicht erforderlich |
| Offline-Betrieb | Nur mit zusätzlicher Implementierung | Mit geeignetem Service Worker und lokal verfügbaren Daten | Mit lokal gebündelten Webdateien möglich |
| Zugriff auf Gerätefunktionen | Browser-APIs mit Einschränkungen | Browser-APIs mit iOS-spezifischen Einschränkungen | Über Capacitor-Plugins und native APIs |
| Updates | Direkt auf dem Webserver | Direkt auf dem Webserver, gegebenenfalls mit Cache-Verhalten | Web-Build und Synchronisierung; App-Updates für native Änderungen |
| Apple Developer Account | Nein | Nein | Für bestimmte Installations- und Veröffentlichungswege erforderlich |
| Mac und Xcode | Nein | Nein | Für lokale iOS-Entwicklung erforderlich |
| App Store | Nein | Nein | Optional, wenn die App darüber veröffentlicht werden soll |
| Aufwand | Gering | Gering bis mittel | Mittel bis höher |
| Geeignet für | Webseiten und einfache Web-Tools | Installierbare Webanwendungen | Apps 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:
- Oben die aktuelle Aufgabe.
- Darunter drei große Antwortfelder.
- Anschließend der Fortschritt bis zur nächsten Belohnung.
- Nach sechs richtigen Antworten erscheint das Icon.
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.
- Die Anwendung auf einem Webserver bereitstellen.
- Die URL auf dem iPhone in Safari öffnen.
- Die Anwendung wie eine normale Webseite verwenden.
- 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.

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:
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:
- Die veröffentlichte URL in Safari öffnen.
- Das Teilen-Menü öffnen.
- „Zum Home-Bildschirm“ auswählen.
- Den Namen prüfen und die Aktion bestätigen.
- 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.
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:
- einen Mac,
- Node.js und npm,
- ein vorhandenes Vue-Projekt mit Produktions-Build,
- Xcode und die passenden iOS-Entwicklungswerkzeuge,
- ein iPhone oder einen iOS-Simulator zum Testen.
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:
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.
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
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.


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.
| Ziel | Meine Wahl |
|---|
| Möglichst schnell eine funktionierende Lern-App veröffentlichen | Klassische Web-App |
| Ein App-Icon und ein eigenständigeres Startverhalten | PWA |
| Offline-Nutzung mit gezieltem Caching | PWA oder Capacitor, je nach Anforderungen |
| Native iOS-APIs und Plugins | Capacitor |
| TestFlight oder App Store | Capacitor mit entsprechendem iOS-Build |
| Möglichst wenig Wartungsaufwand | Klassische 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ück2026-10-25 10:25