Lift von Hybrid (WKWebView für Lesen/Erkunden) auf fully-native ist beschlossen. Diese Phase entfernt die WebShell-Infrastruktur; das volle native Read-Surface folgt in η-2..η-5 nach docs/NATIVE_LIFT_PLAN.md. - ManaWebShell-Dep raus aus project.yml - Sources/Core/WebShell/CookieBridge.swift gelöscht - RootView auf vier native Tabs (Lesen + Erkunden = Platzhalter, Submit + Konto unverändert nativ) - DocComments in DeepLinkRouter / AppConfig / Account / Settings von WebView-Verweisen befreit - CLAUDE.md Invarianten von Hybrid auf η umgestellt (13 Invarianten, pure SwiftUI + Offline-first + SafariView-Ausnahme für Legal) - PLAN.md auf η-0 + Phasenübersicht η-0..η-10 - AppConfigTests.test_keychainService_matchesSharedGroup auf ManaSharedKeychainGroup aktualisiert (war drift seit Cross-App-SSO) Verifikation: - xcodebuild iOS-Simulator iPhone 16e: BUILD SUCCEEDED - nm ZitareNative | grep WKWebView: 0 Referenzen - otool -L: kein WebKit-Framework-Link - 20/20 Tests grün Cross-Repo-Follow-up (η-1 Blocker): - zitare/apps/zitare/ muss index-full.json + 7 Stammdaten-JSONs liefern - zitare/apps/api/ Volltext-Search-Endpoint bestätigen/ergänzen Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11 KiB
CLAUDE.md — zitare-native repo
Guidance für Claude Code in diesem Repository.
Wenn du gerade neu bist: lies zuerst
PLAN.mdunddocs/NATIVE_LIFT_PLAN.md. Das ältere Hybrid-Playbook in../mana/docs/playbooks/ZITARE_NATIVE_GREENFIELD.mdist seit 2026-05-22 überholt — nur als historische Referenz lesen.
Was dieses Repo ist
Zitare Native — native SwiftUI-Universal-App (iOS / iPadOS / macOS) für Zitare, den öffentlichen Zitat-Korpus des Vereins mana e.V.
HTTPS ┌──────────────────┐
zitare.com ◄──────────── │ zitare-native │ pure SwiftUI
(Snapshot + AASA) │ (this repo) │ SwiftData (voller Korpus)
│ ev.mana.zitare │ WidgetKit + ShareExt + Spotlight
api.zitare.com ◄──────────── │ │ SafariView (nur Legal)
(Read + Submit) │ │
└──────────────────┘
Status
Phase η-0 — De-Hybrid (2026-05-22, in Arbeit). Hybrid-Strategie
(WKWebView für Read-Surfaces) wurde aufgegeben. Plan +
Phasen-Übersicht in docs/NATIVE_LIFT_PLAN.md.
Status-Spur in PLAN.md.
Leitprinzip: pure-native + Offline-first
Das Verteilungs-USP-Argument trägt allein nicht; die App soll auch Funktion + Lese-Komfort eigenständig liefern, ohne den Browser-Schwester- Pfad. Native bringt:
- Voller Korpus offline in SwiftData (Snapshot-Sync
index-full.json). - Lokale FTS + Server-Fallback bei seltenen Queries.
- Home-Screen-Widget „Zitat des Tages".
- ShareExtension als Ziel für markierten Text.
- Spotlight-Index für system-weite Volltext-Suche.
- Native Submit mit ManaAuthUI.
- SafariView nur für statische Verein-Recht-Seiten (Impressum,
Datenschutz, Lizenz) — kein
WKWebViewim Binary.
Architektonische Invarianten (η)
Beschlossen 2026-05-22. Nicht ohne explizite Diskussion antasten.
- Pure SwiftUI für alle Surfaces. Kein
WKWebViewim App-Binary.ManaWebShell-Dep ist raus ausproject.yml. - Offline-first Lesen. SwiftData ist Primary-Store, API ist Fallback bei Cache-Miss + Sync-Quelle. Flugmodus-Test ist Akzeptanz- Kriterium ab η-2.
- Quote-Korpus ist Snapshot. Beim Launch + Background-Refresh
wird
index-full.jsongepullt, ETag-aware, in SwiftData persistiert, per App-Group an Widget + ShareExt + Spotlight durchgereicht. - Server gewinnt bei Schema-Konflikt. SwiftData-Modelle sind eine
abgeleitete Spiegelung des Drizzle-Schemas in
zitare/apps/api/. - Search lokal-first. Lokale FTS aus SwiftData; bei < 3 Treffern
oder leerem Result Server-Fallback (
GET /api/v1/quotes?q=). - Legal-Seiten via SafariView. Impressum / Datenschutz / Lizenz
öffnen
SFSafariViewController. Das ist die einzige zugelassene Browser-Brücke und nur für statisches Verein-Recht-Material. - Schreiben bleibt Submit-only in v1. Edit / Moderation / Flags- Verwaltung kommen NICHT in die App — bleiben Web.
- Universal-Links auf
zitare.comlösen ins native Surface. AASA bleibt,onContinueUserActivityroutet auf SwiftUI-Views, nicht in einen WebView-Tab. - mana-auth via ManaCore + ManaAuthUI. Submit + Konto nutzen
AuthClientund die fertigen Views aus ManaAuthUI. Keine eigene Auth, keine Cookie-Bridge mehr (entfällt mitWKWebView). - Universal-Link-Domain:
zitare.com(+app.zitare.comals AASA-Redundanz). AASA aufhttps://zitare.com/.well-known/apple-app-site-association. - Theme:
paper-Variant default. Werte aus@mana/themes/paper, lokal alsZitareTheme.swiftnachgebaut. - Bundle-ID
ev.mana.zitare. Reverse-Domain mana-ev.ch. App-Display-Name: „Zitare". Category:public.app-category.reference. - Web gewinnt bei Konflikt im Datenmodell. Funktion wandert
zuerst in
zitare/apps/zitare/oderzitare/apps/api/, dann ins native Surface hier — kein einseitiger Native-Vorlauf, der das Web abhängt.
Konventionen
- Swift 6.0, Strict Concurrency komplett
- iOS 18 / iPadOS 18 / macOS 15 Minimum
- SwiftUI als einziges UI-Framework,
WKWebViewviaUIViewRepresentable/NSViewRepresentabledie einzige Bridge - XcodeGen als SOT:
project.ymldefiniert Targets, Info.plist, Entitlements..xcodeproj, generierte Info.plist und Entitlements sind nicht im Git - SwiftFormat mit
.swiftformat - SwiftLint mit
.swiftlint.yml - Logging: App-Subsystem
ev.mana.zitareviaSources/Core/Telemetry/Log.swift - Persistenz: SwiftData für Snapshot-Cache + PendingSubmission- Queue, JWT im Keychain (über ManaCore)
- Lokalisierung: DE primary, EN fallback via
Localizable.xcstrings
Zitare-API-Wire-Format
Wire-Format gegen https://zitare-api.mana.how/api/v1/*. Quelle der
Wahrheit: ../zitare/apps/api/src/routes/*.ts. Bei neuem DTO:
- Path + Method gegen den Hono-Handler prüfen
- Response-Schema (
zod) gegenCodable-Struct mappen - snake_case via
CodingKeys, optionale Felder explizitOptional<T> - Test-Fixture aus echtem Server-Response in
Tests/UnitTests/
Snapshot-Vertrag
https://zitare.com/index-min.json ist der lokale Korpus-Spiegel.
Heute Build-Output (zitare/apps/zitare/src/content/index-min.json),
ausgeliefert als statische Datei.
{
"generatedAt": "2026-05-08T20:48:48.795Z",
"count": 11,
"quotes": [
{
"slug": "...",
"authorSlug": "...",
"language": "de",
"themeSlugs": [...],
"regionSlugs": [...],
...
}
]
}
Native-Konsumenten: SnapshotSync (App), DailyQuoteWidget
(WidgetExtension via App-Group), SpotlightIndexer (App), Submit-
View (Auto-Complete für Author/Theme).
Repo-Layout
zitare-native/
├── project.yml XcodeGen-Manifest (SOT)
├── PLAN.md Phasen-Tracking
├── CLAUDE.md dieses File
├── README.md
├── .swiftformat, .swiftlint.yml, .gitignore
├── Sources/
│ ├── App/ ZitareNativeApp (@main), RootView
│ ├── Features/
│ │ ├── WebShell/ WebShellView (WKWebView-Wrapper, ζ-1)
│ │ ├── Submit/ SubmitQuoteView (ζ-3)
│ │ ├── Account/ AccountView (ζ-0 stub)
│ │ └── Settings/ SettingsView (ζ-5)
│ ├── Core/
│ │ ├── Auth/ AppConfig (ManaAppConfig-Provider)
│ │ ├── API/ ZitareAPI (Quote-DTOs, Submit, Share)
│ │ ├── Snapshot/ SnapshotSync, SnapshotStore (ζ-2)
│ │ ├── Spotlight/ SpotlightIndexer (ζ-4)
│ │ ├── Telemetry/ Log (OSLog, ev.mana.zitare)
│ │ └── Theme/ ZitareTheme (paper-Werte)
│ ├── Widgets/ WidgetKit-Extension (ζ-2)
│ ├── ShareExtension/ „An Zitare schicken" (ζ-4)
│ └── Resources/
│ ├── Assets.xcassets AppIcon, AccentColor
│ ├── Localizable.xcstrings
│ ├── Info.plist (XcodeGen-generiert, gitignored)
│ └── ZitareNative.entitlements (generiert, gitignored)
├── Tests/
│ ├── UnitTests/
│ └── UITests/
└── docs/
Wichtige Cross-Repo-Doks
../mana/docs/playbooks/ZITARE_NATIVE_GREENFIELD.md— vollständiger Phasen-Plan und Architektur-Begründungen../mana/docs/MANA_SWIFT.md— Plattform-SOT für alle nativen Apps../mana/docs/MANA_AUTH_FEDERATION.md— Auth-Protokoll, Cookie-SSO../mana/docs/COMPLIANCE.md— Telemetrie/Auth/Bezahl-Regeln plus Plattform-Lock-In-Diskurs (gilt auch native)../zitare/CLAUDE.md— Web-App-Konventionen../zitare/STATUS.md— Web-Phasenstand (Funktions-Referenz)../zitare/app-manifest.json— Föderations-Vertrag (Shares / Accepts / Link-Patterns)../mana-swift-core/CLAUDE.md— ManaCore + ManaTokens../mana-swift-ui/CLAUDE.md— ManaAuthUI + ManaAuthGate
Lokal entwickeln
Pre-Requisites:
- Xcode 16+
brew install xcodegen swiftformat swiftlint../mana-swift-core/und../mana-swift-ui/als Schwester- Verzeichnisse (Package-Dependency viapath:)
Workflow:
xcodegen generate
open ZitareNative.xcodeproj
Vor jedem Commit:
swiftformat Sources Widgets ShareExtension Tests
swiftlint --strict
Phasen-Disziplin
Jede Phase aus dem Lift-Plan hat ein verifizierbares Erfolgskriterium.
Nicht in die nächste Phase reinarbeiten, bevor die vorherige
abgeschlossen ist. Vollständige Phasen-Tabelle +
Erfolgs-Kriterien in docs/NATIVE_LIFT_PLAN.md.
Übersicht:
- ζ-0..ζ-2: erledigt unter Hybrid-Annahme (Setup, WebShell, Snapshot- Stub + Widget-Code). WebShell entfällt jetzt; Widget + Snapshot- Code bleiben, werden in η-1 erweitert.
- η-0: De-Hybrid (ManaWebShell raus, RootView vier native Tabs) (JETZT)
- η-1: Snapshot v2 + volles SwiftData-Schema (Cross-Repo-Blocker:
zitare/apps/zitare/mussindex-full.json+ 7 Stammdaten-JSONs liefern) - η-2: Read-Core nativ (Heute, Quote-Detail, Author-Detail)
- η-3: Read-Browse (Source / Theme / Place / Role / Epoch / Language)
- η-4: Explore + Filter
- η-5: Search (lokal-first + Server-Fallback)
- η-6: Account voll nativ
- η-7: Legal-Sheets via SafariView
- η-8: Submit / ShareExt / Spotlight an Snapshot v2 anpassen
- η-9: Polish (iPad-Split, Accessibility, macOS-Layout)
- η-10: TestFlight
Bei Phasen-Wechsel: PLAN.md aktualisieren + Memory-Eintrag
project_zitare_native_dehybrid.md nachziehen.
Don't do
- Kein
WKWebViewim App-Binary. SafariView (SFSafariViewController) ist die einzige zugelassene Browser-Brücke, und nur für statisches Verein-Recht (Impressum, Datenschutz, Lizenz). Wenn du WKWebView brauchen würdest, baust du es als native View nach. - Kein eigener FSRS-Port, kein eigener Pagefind-Klon. Existiert beides nicht für Zitare. Lokale FTS in η-5 ist SwiftData-Predicate.
- Keine Push-Notification-Pipeline. Widget reicht.
- Keine externen UI-Libs / kein Sentry / kein Crash-Reporting- SaaS. OSLog only (Compliance).
- Kein Edit / Moderation / Admin in der App. Web-Surface
(
app.zitare.com) bleibt SOT für schreibende Pfade jenseits von Submit. Wer das braucht, öffnet Safari.