zitare-native/CLAUDE.md
Till 0bd59ed148 ζ-0 Setup: Repo-Skelett, iOS-Build grün, Healthz live
- 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>
2026-05-14 12:15:22 +02:00

233 lines
9.2 KiB
Markdown

# CLAUDE.md — zitare-native repo
Guidance für Claude Code in diesem Repository.
> **Wenn du gerade neu bist:** lies zuerst [`PLAN.md`](PLAN.md) und
> [`../mana/docs/playbooks/ZITARE_NATIVE_GREENFIELD.md`](../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`](../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:
1. **Home-Screen-Widget** „Zitat des Tages"
2. **ShareExtension** als Ziel für markierten Text
3. **Spotlight-Index** für system-weite Suche
4. **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.
1. **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.
2. **Read-only via Web, Submit via SwiftUI.** Submit ist der einzige
schreibende Pfad in v1. Edit, Moderation, History bleiben Web.
3. **Snapshot lokal gespiegelt für Widget + Spotlight.** Beim Launch
`https://zitare.com/index-min.json` pullen, in SwiftData
persistieren, App-Group `group.ev.mana.zitare` reicht es an Widget
+ ShareExtension durch. **Nicht** für den WebView-Pfad — der lädt
live.
4. **mana-auth via ManaCore + ManaAuthUI.** Submit-Pfad nutzt
`AuthClient` und die fertigen Views aus ManaAuthUI. Keine eigene
Auth. WebView gegen `zitare.mana.how` bekommt JWT per Cookie-
Injection (`mana.access` auf `.mana.how`).
5. **Universal-Link-Domain: `zitare.com`.** AASA auf
`https://zitare.com/.well-known/apple-app-site-association`.
`zitare.mana.how` ist *kein* applinks-Ziel.
6. **Theme: `paper`-Variant default.** Werte aus `@mana/themes/paper`,
lokal als `ZitareTheme.swift` nachgebaut.
7. **Bundle-ID `ev.mana.zitare`.** Reverse-Domain mana-ev.ch.
App-Display-Name: „Zitare". Category: `public.app-category.reference`.
8. **Pure SwiftUI für Native-Surfaces.** WKWebView ist die einzige
UIKit-Bridge.
9. **Web gewinnt bei Konflikt.** Funktion wandert zuerst in
`zitare/apps/zitare/` oder `zitare/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, `WKWebView` via
`UIViewRepresentable`/`NSViewRepresentable` die einzige Bridge
- **XcodeGen** als SOT: `project.yml` definiert 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.zitare` via
`Sources/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:
1. Path + Method gegen den Hono-Handler prüfen
2. Response-Schema (`zod`) gegen `Codable`-Struct mappen
3. snake_case via `CodingKeys`, optionale Felder explizit `Optional<T>`
4. 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.
```json
{
"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 via `path:`)
**Workflow:**
```bash
xcodegen generate
open ZitareNative.xcodeproj
```
**Vor jedem Commit:**
```bash
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.