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

9.2 KiB

CLAUDE.md — zitare-native repo

Guidance für Claude Code in diesem Repository.

Wenn du gerade neu bist: lies zuerst PLAN.md und ../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:

  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.

{
  "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:

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.