- project.yml mit Bundle ev.mana.zitare + Widget + ShareExt-Targets - ManaSwiftCore (ManaCore + ManaTokens) + ManaSwiftUI (ManaAuthUI) als Package-Dependencies via path: - Pure SwiftUI für Native-Surfaces, WKWebView nur für Lese-Tabs (Hybrid-Sonderfall vs cards/memoro/manaspur, dokumentiert im Playbook ZITARE_NATIVE_GREENFIELD.md) - Theme: paper-Variant aus @mana/themes - ZitareAPI.healthCheck via direct URLSession (öffentlicher Endpoint, kein AuthenticatedTransport-Gate) - 6/6 AppConfigTests + 1/1 UI-Smoke grün auf iPhone 16e Simulator - Live: zitare-api.mana.how/healthz → HTTP/2 200 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.2 KiB
CLAUDE.md — zitare-native repo
Guidance für Claude Code in diesem Repository.
Wenn du gerade neu bist: lies zuerst
PLAN.mdund../mana/docs/playbooks/ZITARE_NATIVE_GREENFIELD.md. Dieses CLAUDE.md ist die Konventions- und Cross-Repo-Referenz.
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 │ WKWebView (Lesen)
(statisch, public) │ (this repo) │ SwiftUI (Submit)
│ ev.mana.zitare │ WidgetKit
zitare-api ◄──────────── │ │ SwiftData (Snapshot-Cache)
zitare-api.mana.how │ │ CoreSpotlight
└──────────────────┘
Status
Phase ζ-0 — Setup. Repo-Skelett, project.yml, leerer Build im
Simulator. Phasen ζ-1 bis ζ-7 in
../mana/docs/playbooks/ZITARE_NATIVE_GREENFIELD.md.
Leitprinzip: Verteilungs-USP, nicht Funktions-USP
Anders als die anderen drei nativen Apps (cards/memoro/manaspur) gibt es bei Zitare keinen Hardware-Vorteil gegenüber dem Browser. Die Web-App ist mobile-responsive, statisch prerendered, hat Pagefind- client-Suche. Native bringt:
- Home-Screen-Widget „Zitat des Tages"
- ShareExtension als Ziel für markierten Text
- Spotlight-Index für system-weite Suche
- Native Submit-View mit ManaAuthUI
Alles andere (Lesen, Filtern, Search, Edit, Moderation) bleibt im
WKWebView gegen zitare.com / zitare.mana.how.
Architektonische Invarianten
Beschlossen. Nicht ohne explizite Diskussion antasten.
- Hybrid ausnahmsweise. Lese-Surfaces via
WKWebView, Native- Surfaces (Widget, ShareExt, Submit, Spotlight) pure SwiftUI. Diese Trennung ist fest — keine schleichende Native-Re-Implementation von Read-Routes. - Read-only via Web, Submit via SwiftUI. Submit ist der einzige schreibende Pfad in v1. Edit, Moderation, History bleiben Web.
- Snapshot lokal gespiegelt für Widget + Spotlight. Beim Launch
https://zitare.com/index-min.jsonpullen, in SwiftData persistieren, App-Groupgroup.ev.mana.zitarereicht es an Widget- ShareExtension durch. Nicht für den WebView-Pfad — der lädt live.
- mana-auth via ManaCore + ManaAuthUI. Submit-Pfad nutzt
AuthClientund die fertigen Views aus ManaAuthUI. Keine eigene Auth. WebView gegenzitare.mana.howbekommt JWT per Cookie- Injection (mana.accessauf.mana.how). - Universal-Link-Domain:
zitare.com. AASA aufhttps://zitare.com/.well-known/apple-app-site-association.zitare.mana.howist kein applinks-Ziel. - 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. - Pure SwiftUI für Native-Surfaces. WKWebView ist die einzige UIKit-Bridge.
- Web gewinnt bei Konflikt. Funktion wandert zuerst in
zitare/apps/zitare/oderzitare/apps/api/, dann ins WebView- bzw. Native-Surface hier.
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 Greenfield-Plan hat ein verifizierbares Erfolgskriterium. Nicht in die nächste Phase reinarbeiten, bevor die vorherige abgeschlossen ist:
- ζ-0: leerer Build + ManaCore-Login + Healthz-Probe (JETZT)
- ζ-1: WebShellView + Universal-Links + Cookie-SSO-Bridge
- ζ-2: Snapshot-Sync + DailyQuoteWidget auf realem Gerät
- ζ-3: Submit-View nativ mit ManaAuthGate
- ζ-4: Spotlight + ShareExtension + App Intents
- ζ-5: Polish, Theme-Sync, iPad-Split-Layout, Accessibility
- ζ-6: App-Store-Submission
Bei Phasen-Wechsel: PLAN.md aktualisieren + Memory-Eintrag
project_zitare_native.md nachziehen (sobald angelegt).
Don't do
- Keine Native-Re-Implementation der Read-Routes. Wenn dir die
Web-Quote-Ansicht im WebView nicht gefällt, fixe sie in
../zitare/apps/zitare/src/routes/(read)/. Nicht hier eine zweite bauen. - Kein eigener FSRS-Port, kein eigener Pagefind-Klon. Existiert beides nicht für Zitare und soll nicht entstehen.
- Keine Push-Notification-Pipeline. Widget reicht.
- Keine externen UI-Libs / kein Sentry / kein Crash-Reporting- SaaS. OSLog only (Compliance).
- Keine offline-Volltext-Funktion im WebView. Wenn Offline-
Lesen Use-Case wird → PWA-Pfad (Option A in
zitare/CLAUDE.md), nicht hier.