Voller Marketplace-Flow mit TabBar und Universal-Link-Handler. Drei Live-Decks (Geografie, English A2, Periodensystem) sind browse-, abonnier- und lernbar. - PublicDeckEntry/PublicDeck/PublicDeckVersion/PublicDeckOwner/ PublicDeckDetail Codable mit snake_case - ExploreResponse, BrowseResponse, SubscribeResponse - MarketplaceSort-Enum (recent/popular/trending) - CardsAPI.explore/browseMarketplace/publicDeck/subscribe/unsubscribe - MarketplaceStore @Observable mit Explore + Browse States - ExploreView: Featured + Trending Horizontal-Carousels, Browse-Link - BrowseView: Searchable + Sort-Picker + List - PublicDeckView: Header/Metadata/Subscribe — Subscribe löst Auto-Fork serverseitig aus, Response liefert private_deck_id, NavigationLink zum eigenen Deck - PublicDeckCard + BrowseRow mit forest-Theme - RootView: TabBar (Decks/Entdecken/Account) statt Single-View - Universal-Link-Handler: onOpenURL + onContinueUserActivity für https://cardecky.mana.how/d/<slug> und cards://d/<slug> - associated-domains: applinks:cardecky.mana.how im entitlement - 5 neue Marketplace-Decoding-Tests (35 Total grün) Universal-Links funktionieren erst nach AASA-Setup auf cardecky.mana.how/.well-known/apple-app-site-association (Web-Aufgabe, heute 404). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
9.4 KiB
Plan — cards-native (SwiftUI Universal)
Stand: 2026-05-13 — Phasen β-0 bis β-5 abgeschlossen. Alle 7 Card-Types + voller Marketplace (Explore/Browse/Subscribe)
- TabBar + Universal-Link-Handling für
cardecky.mana.how/d/<slug>. 35 Unit-Tests + 1 UI-Test grün.
Pflicht-Check für β-2: Endurance-Test auf realem Gerät (200+ Karten mit Flugmodus zwischendurch) steht aus — Aufgabe für Till.
SOT:
../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md. Dieses File ist die App-lokale Status-Spur, das Greenfield-Doc hat die ganze Architektur-Begründung.
Aktueller Stand
✅ β-0 — Setup (2026-05-12, Tag v0.1.0)
- Repo-Skelett unter
git.mana.how/till/cards-native project.ymlmit Bundle-IDev.mana.cards, ManaSwiftCore viapath: ../mana-swift-coreAppConfigalsManaAppConfig-Provider:- Auth:
https://auth.mana.how - API:
https://cardecky-api.mana.how - Keychain-Service:
ev.mana.cards
- Auth:
CardsTheme.swiftmit forest-Werten (lokal nachgebaut ausmana/packages/themes/src/variants/forest.css)LoginView(Email/PW gegen mana-auth)- 3 Unit-Tests (AppConfig)
✅ β-5 — Marketplace (2026-05-13, Tag v0.6.0)
PublicDeckEntry,PublicDeck,PublicDeckVersion,PublicDeckOwner,PublicDeckDetail,ExploreResponse,BrowseResponse,SubscribeResponseCodable-DTOs mit snake_caseMarketplaceSortEnum (recent/popular/trending) mit deutschen LabelsCardsAPI: explore(), browseMarketplace(query:sort:language:), publicDeck(slug:), subscribe(slug:), unsubscribe(slug:)MarketplaceStore@Observable mit Explore-State + Browse-StateExploreViewmit Featured + Trending Carousels, Browse-LinkBrowseViewmit Searchable + Sort-Picker + ListePublicDeckViewmit Header + Version + Owner + Subscribe-Button (Auto-Fork serverseitig, danach NavigationLink zum eigenen Deck)PublicDeckCard+BrowseRowKomponenten mit forest-ThemeRootView→ TabBar (Decks / Entdecken / Account) statt Single-View- Universal-Link-Handler in
RootView(onOpenURL + onContinueUserActivity):https://cardecky.mana.how/d/<slug>undcards://d/<slug>→ Explore-Tab öffnetPublicDeckView associated-domains: applinks:cardecky.mana.howim entitlement- 5 neue Marketplace-Decoding-Tests (35 Total grün)
Wichtig: Universal-Links funktionieren erst, wenn AASA-Endpoint
unter cardecky.mana.how/.well-known/apple-app-site-association
ausgeliefert wird — heute 404. Web-seitige Aufgabe.
✅ β-4 — Media + Advanced Card-Types (2026-05-13, Tag v0.5.0)
MediaUploadResponseDTO +MediaKind-EnumMaskRegionCodable mit 0..1-Coordinates,MaskRegions.parse/encode- Helpers (1:1-Port auscards-domain/image-occlusion.ts— Sortierung nach ID lexikographisch)CardFieldsBuilder.imageOcclusion,.audioFrontmit korrektermask_regions-Serialisierung als stringified JSON-ArrayCardsAPI.uploadMedia(data, filename, mimeType)mit Multipart (25 MiB max),.fetchMedia(id)für streamed bytesMediaCacheactor mit LRU 200 MB (sortiert nachcontentModificationDate)mediaCache-Environment-Key, im App-Entrypoint instantiiertRemoteImageView — authentifiziertes Image-Loading mit ProgressView- Failure-State
AudioPlayerButton— AVAudioPlayer-Wrapper mit Play/Pause-Toggle, AVAudioSession-Setup für iOSCardRenderer.imageOcclusionView: AsyncImage + opake Maske über aktiver Region (Frontside), Label-Reveal auf BacksideCardRenderer.audioFrontView: AudioPlayerButton + back-Text auf FlipMaskEditorView: Touch-Drag-to-Create-Rectangle, Liste mit Label-Edit- Delete, 0..1-Normalisierung beim Commit
CardEditorViewerweitert: PhotosPicker für Image, fileImporter für Audio, Magic-Byte-MIME-Detection (JPEG/PNG/GIF/WebP)- 6 neue Tests für MaskRegions-Parse/Encode + Field-Builder (30 Total)
✅ β-3 — Editor (2026-05-13, Tag v0.4.0)
DeckCreateBody,DeckUpdateBody,CardCreateBody,CardUpdateBodyEncodable-Structs (snake_case viaCodingKeys, nil-Felder werden weggelassen)CardFieldsBuildermit Type-spezifischen Pflicht-Feld-KonstruktorenCardsAPI: createDeck/updateDeck/deleteDeck + createCard/updateCard/deleteCardDeckEditorViewfür Create + Edit in einer View (mode-switch), Color-Picker mit 8-Preset-Palette aus forest-Theme, Category-Picker (11 Kategorien mit deutschen Labels), Visibility-Segmented-ControlCardEditorViewmit Type-Picker (basic, basic-reverse, cloze, typing, multiple-choice) und dynamischen Feldern je Typ. Cloze-View zeigt Live-Cluster-Count und Hint-Syntax-Hinweis. image-occlusion und audio-front zeigen β-4-PlaceholderDeckDetailViewmit 4 Action-Buttons (Lernen, Karte hinzufügen, Bearbeiten, Löschen), Confirmation-Dialog für Delete- DeckListView: "+"-Button im Toolbar (Leading), Sheet für Create
- 7 zusätzliche Encoding-Tests (24 Unit-Tests total)
✅ β-2 — Study-Loop (2026-05-13, Tag v0.3.0)
Card,Review,DueReviewCodable-DTOs,CardType-Enum (alle 7 Typen)Rating-Enum:again | hard | good | easymit deutschen LabelsCloze-Helpers (extractClusterIds, subIndexCount, clusterId, renderPrompt, renderAnswer, hint) — 1:1-Port auscards/packages/cards-domain/src/cloze.tsCardsAPI.dueReviews(deckId:),CardsAPI.gradeReview(...)mit ISO8601-EncoderPendingGradeSwiftData-Model +GradeQueuefür Offline-Submit (FIFO-Drain, originaler reviewedAt-Timestamp bleibt erhalten)StudySessionals @Observable State-Machine (loading/studying/finished/failed)CardRenderer: basic, basic-reverse (sub-index-abhängig), cloze client-rendered. image-occlusion/audio-front/typing/multiple-choice zeigen Placeholder (β-3/β-4)RatingBarmit Haptic-Feedback (medium für again/hard/good, heavy für easy, soft beim Flip)StudySessionViewvollbild aus DeckListView per NavigationLink- 9 zusätzliche Tests (Cloze 8x, Review/DueReview-Decoding 3x)
✅ β-1 — Decks lesen (2026-05-13, Tag v0.2.0)
Deck-Codable-DTO mit snake_case-CodingKeys, plusDeckCategory,DeckVisibility,FsrsSettings- ISO8601-Date-Decoder mit Fractional-Seconds-Toleranz
CardsAPI.listDecks(),cardCount(deckId:),dueCount(deckId:)CachedDeckals SwiftData-Model mitlastFetchedAt(Offline-Read)DeckListStoreorchestriert API + Cache, paralleles Counts-Fetching via TaskGroupDeckListViewmit Pull-to-Refresh, Card/Due-Counts, deck.color-Streifen, Inbox-Banner für Marketplace-ForksAccountViewmit Sign-out-Button- iOS-Simulator-Build + Tests grün (6 Unit-Tests, 1 UI-Test)
Phasen (Detail in Greenfield-Plan)
| Phase | Status | Inhalt |
|---|---|---|
| β-0 | ✅ 2026-05-12 | Setup, Login, API-Probe |
| β-1 | ✅ 2026-05-13 | Decks lesen, SwiftData-Cache, Pull-to-Refresh |
| β-2 | ✅ 2026-05-13 | Study-Loop, Offline-Grade-Queue (Endurance-Test offen) |
| β-3 | ✅ 2026-05-13 | Editor: Deck-CRUD + Card-Create (5 Types); Anki-Import auf β-3-ext verschoben |
| β-4 | ✅ 2026-05-13 | Media-Upload, image-occlusion (Touch-Mask-Editor), audio-front (AVAudioPlayer) |
| β-5 | ✅ 2026-05-13 | Marketplace (Explore/Browse/Subscribe) + TabBar + Universal-Link-Handler (AASA server-side pending) |
| β-6 | — | Native-Polish (Widgets, Notifications, Share-Extension) |
| β-7 | — | App-Store-Submission |
Nächste Schritte für β-6 (Native-Polish)
Aus Greenfield-Plan-Sektion "Phase β-6":
- WidgetKit-Extension (Small, Medium, Lock-Screen) mit Due-Count
- UNUserNotificationCenter — tägliche Reminder zur konfigurierten Zeit
- Siri-Shortcuts ("Karten lernen" → Default-Deck)
- Share-Extension "Save as Card" für Safari/Mail
- Keyboard-Shortcuts iPad/macOS (Space=flip, 1-4=Rating, J/K=next/prev)
- App-Group
group.ev.mana.cardsfür Widget-Daten-Sharing
Notizen aus β-4
- PencilKit für Mask-Editor explizit nicht genutzt. Web macht
Image-Occlusion-Masks per Touch-Drag-Rechteck (kein Freihand). Server-
Schema (
MaskRegion = {id, x, y, w, h, label}) erlaubt nur Rechtecke, PencilKit-Strokes wären dafür übersteigert. Wenn später Polygon-Masks oder Freihand-Skizzen dazu kommen, kann PencilKit nachgereicht werden. - Apple-Pencil-Support trotzdem grundsätzlich da: SwiftUI's
DragGesturereagiert auf Pencil-Eingaben genauso wie auf Finger.
Verschoben auf β-3-Extension oder später
- Anki-Import (
.apkg-Parser): Web parsed client-side und ruftPOST /cardspro Karte. Native bräuchte eigenen Swift-Parser für Anki-Pakete (Plist/sqlite/.apkg) — eigener Brocken, nicht blockierend für andere Phasen. - Card-Edit (PATCH /cards/:id): Card-Create reicht für Web-Parität in v1, Edit kann später nachgereicht werden.
- Distractor-Vorschau für Multiple-Choice-Editor: Server liefert
Distractors zur Lernzeit (
/decks/:deckId/distractors), Editor zeigt sie nicht — Web macht das auch nicht.
Pflicht-Tests für β-2 (vor β-3-Start)
- Endurance-Test auf realem Gerät: 200+ Karten lernen, Flugmodus zwischendurch — alle Grades landen am Server nach Reconnect.
- Cross-Check mit Web: Karte gegrade in Native → Web zeigt identischen Review-State nach Reload.
Cross-Refs
../mana/docs/playbooks/CARDS_NATIVE_GREENFIELD.md— Greenfield-Plan SOT../mana/docs/MANA_SWIFT.md— Plattform-SOT../cards/CLAUDE.md— Cards-Repo../cards/STATUS.md— Web-Phasenstand (Referenz)../mana-swift-core/CLAUDE.md— ManaCore-KonventionenCLAUDE.md— Repo-Konventionen