# CLAUDE.md — cards-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/CARDS_NATIVE_GREENFIELD.md` (übergeordneter > Greenfield-Plan). Dieses CLAUDE.md ist die Konventions- und > Cross-Repo-Referenz. ## Was dieses Repo ist **Cards Native** — native SwiftUI-Universal-App (iOS / iPadOS / macOS) für **Cardecky**, die Spaced-Repetition-Karten-App des Vereins **mana e.V.** Web-Parität zu `cardecky.mana.how`, plus native iOS- Affordances (Widgets, Notifications, Universal-Links, Pencil). ``` HTTPS/JWT ┌──────────────────┐ cards-api ◄───────────── │ cards-native │ SwiftUI cardecky-api.mana.how │ (this repo) │ SwiftData (Cache) │ ev.mana.cards │ WidgetKit (β-6) └──────────────────┘ ``` ## Status **Phase β-0 — Setup (2026-05-12).** Repo-Skelett, ManaCore + ManaTokens als Package-Dependency, Login + Cardecky-API-Reachability-Probe. Phasen β-1 bis β-7 in `../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md`. ## Leitprinzip: Web-Parität Die Web-App auf `cardecky.mana.how` ist Funktions-Referenz. Bei Konflikt zwischen Native und Web → **Web gewinnt**. Native ist Re-Implementation, kein neues Produkt. Datenmodell, FSRS-Verhalten, Marketplace-Slugs, Sharing-URLs: identisch zu Web. ## Architektonische Invarianten Beschlossen. Nicht ohne explizite Diskussion antasten. 1. **Server-authoritative FSRS.** Grading-Calls gehen *immer* an `POST /api/v1/reviews/:cardId/:subIndex/grade`. Kein lokaler ts-fsrs-Port. 2. **Offline-Read, Online-Write.** Decks + Due-Cards via SwiftData gecacht (offline sichtbar). Grades werden bei Offline in einer lokalen Queue persistiert und beim Reconnect der Reihe nach abgesendet. 3. **mana-auth via ManaCore.** `import ManaCore`, `AuthClient(config: AppConfig.manaAppConfig)`. Eigene Auth-Implementierung ist verboten. 4. **Pure SwiftUI.** Keine externen UI-Libraries. AppKit/UIKit nur als Bridge wenn zwingend (z.B. `PencilKit` für Image-Occlusion). 5. **Bundle-ID `ev.mana.cards`.** Reverse-Domain mana-ev.ch. Universal-Link-Domain: `cardecky.mana.how`. 6. **Cards-Domain-Logik bleibt am Server.** SubIndex-Berechnung für Cloze, Image-Occlusion-Mask-Validation, Content-Hash — alles Server. Native zeigt nur, was vom Server kommt. 7. **`forest`-Theme.** Heute lokal in `CardsTheme.swift` nachgebaut (Werte gespiegelt aus `mana/packages/themes/src/variants/forest.css`). Migration auf ManaTokens-Theme-Switch ist Phase ε. 8. **Web gewinnt bei Konflikt.** Eleganteres Native-Verhalten geht zuerst in die Web-App, dann nach hier. ## Konventionen - **Swift 6.0**, Strict Concurrency komplett - **iOS 18 / iPadOS 18 / macOS 15** Minimum - **SwiftUI** als einziges UI-Framework - **XcodeGen** als SOT: `project.yml` definiert Targets, Info.plist, Entitlements. `.xcodeproj`, generierte Info.plist und Entitlements sind **nicht** im Git - **SwiftFormat** mit `.swiftformat` (4-space, 120-col, sorted imports) - **SwiftLint** mit `.swiftlint.yml` - **Logging:** App-Subsystem `ev.mana.cards` via `Sources/Core/Telemetry/Log.swift`. ManaCore loggt parallel unter `ev.mana.core` - **Persistenz:** SwiftData für Deck/Card-Cache (ab β-1), JWT im Keychain (über ManaCore) - **Lokalisierung:** DE primary, EN fallback via `Localizable.xcstrings` ## Cardecky-API-Wire-Format Wire-Format gegen `https://cardecky-api.mana.how/api/v1/*`. Quelle der Wahrheit: `../cards/apps/api/src/routes/*.ts`. Bei neuem DTO verifizieren: 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` 4. Test-Fixture aus echtem Server-Response in `Tests/UnitTests/` ## Repo-Layout ``` cards-native/ ├── project.yml XcodeGen-Manifest (SOT) ├── PLAN.md Phase-Tracking (gekürzt aus Greenfield-Plan) ├── CLAUDE.md dieses File ├── README.md ├── .swiftformat, .swiftlint.yml ├── Sources/ │ ├── App/ CardsNativeApp (@main), RootView │ ├── Features/ │ │ ├── Account/ LoginView, AccountView (ab β-1) │ │ ├── Decks/ DashboardView (Placeholder), DeckList (β-1) │ │ ├── Study/ (β-2) │ │ ├── Editor/ (β-3) │ │ ├── Marketplace/ (β-5) │ │ ├── Stats/ (β-1) │ │ └── Imports/ (β-3) │ ├── Core/ │ │ ├── Auth/ AppConfig (ManaAppConfig-Provider) │ │ ├── API/ CardsAPI (AuthenticatedTransport-Wrapper) │ │ ├── Domain/ (Card-Type-Enums, Rating-Enum — ab β-2) │ │ ├── Storage/ (SwiftData-Models — ab β-1) │ │ ├── Sync/ (ReviewQueue, MediaCache — ab β-2/β-4) │ │ ├── Telemetry/ OSLog (Subsystem ev.mana.cards) │ │ └── Theme/ CardsTheme (forest-Werte) │ ├── Widgets/ (WidgetKit-Extension — ab β-6) │ ├── ShareExtension/ (Save-as-Card — ab β-6) │ └── Resources/ │ ├── Assets.xcassets │ ├── Localizable.xcstrings │ ├── Info.plist (generiert, gitignored) │ └── CardsNative.entitlements (generiert, gitignored) ├── Tests/ │ ├── UnitTests/ │ └── UITests/ └── docs/ ``` ## Wichtige Cross-Repo-Doks - `../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md` — vollständiger Phasen-Plan und Architektur-Entscheidungen - `../mana/docs/MANA_SWIFT.md` — native-Plattform-SOT - `../mana/docs/MANA_AUTH_FEDERATION.md` — Auth-Protokoll, das ManaCore implementiert - `../mana/docs/COMPLIANCE.md` — Telemetrie/Auth/Bezahl-Regeln, gilt auch nativ - `../cards/CLAUDE.md` — Cards-Repo, Web + API - `../cards/STATUS.md` — Web-Phasenstand (Funktions-Referenz) - `../mana-swift-core/CLAUDE.md` — geteilter Code, Konventionen ## Lokal entwickeln **Pre-Requisites:** - Xcode 16+ - `brew install xcodegen swiftformat swiftlint` - `../mana-swift-core/` muss als Schwester-Verzeichnis existieren (Package-Dependency via `path: ../mana-swift-core`) **Workflow:** ```bash xcodegen generate open CardsNative.xcodeproj ``` **Vor jedem Commit:** ```bash swiftformat Sources 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 + Login + API-Reachability-Probe (**JETZT**) - β-1: Decks-Liste mit SwiftData-Cache - β-2: Study-Loop + Offline-Grade-Queue + Endurance-Test auf realem Gerät - β-3: Card-/Deck-Editor (basic, cloze, typing, multiple-choice) - β-4: Media + image-occlusion + audio-front - β-5: Marketplace + Universal-Links - β-6: Native-Polish (Widgets, Notifications, Share-Extension) - β-7: App-Store-Submission Bei Phasen-Wechsel: PLAN.md aktualisieren + Greenfield-Plan abhaken.