From 4dfb32ba2532725a691f1b1431fb7a1edfdee1a9 Mon Sep 17 00:00:00 2001 From: Till JS Date: Wed, 13 May 2026 13:29:04 +0200 Subject: [PATCH] chore: Rebrand auf ev.mana.cardecky MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apple-Developer-Portal-App-ID lautet ev.mana.cardecky (analog zur Domain cardecky.mana.how). Alle Bundle-IDs, App-Group, Keychain- Group, OSLog-Subsysteme, URL-Schemes, Widget-Kind, App-Intent-Phrases, Marketing-Texte und Doku nachgezogen. Bundle-IDs neu: - Main: ev.mana.cardecky - Widget: ev.mana.cardecky.widget - Share: ev.mana.cardecky.share - Tests: ev.mana.cardecky.tests / .uitests App-Group: group.ev.mana.cardecky Keychain-Access-Group: $(AppIdentifierPrefix)ev.mana.cardecky OSLog-Subsystem: ev.mana.cardecky AASA gleichzeitig in cards-Repo angepasst (Commit 21ec535) und auf mana-server redeployed — Probe liefert appID "QP3GLU8PH3.ev.mana.cardecky". Plus: ShareExtension/Resources/Info.plist + entitlements werden jetzt analog zu Widget-Resources gitignored (sind XcodeGen-generated). 35 Unit-Tests + 1 UI-Test grün, alle drei Targets bauen. Co-Authored-By: Claude Opus 4.7 (1M context) --- .gitignore | 2 + CLAUDE.md | 8 +- PLAN.md | 8 +- README.md | 2 +- .../CardsShareExtension.entitlements | 10 -- ShareExtension/Resources/Info.plist | 41 ----- ShareExtension/ShareViewController.swift | 2 +- Sources/Core/Auth/AppConfig.swift | 2 +- .../Notifications/NotificationManager.swift | 2 +- Sources/Core/Sync/PendingShareStore.swift | 2 +- Sources/Core/Sync/WidgetSnapshot.swift | 2 +- Sources/Core/Telemetry/Log.swift | 12 +- Tests/UnitTests/CardsNativeTests.swift | 4 +- Widgets/CardsWidget/CardsDueWidget.swift | 2 +- docs/MARKETING_COPY.md | 155 ++++++++++++++++++ docs/RELEASE_CHECKLIST.md | 23 +-- project.yml | 20 +-- 17 files changed, 203 insertions(+), 94 deletions(-) delete mode 100644 ShareExtension/Resources/CardsShareExtension.entitlements delete mode 100644 ShareExtension/Resources/Info.plist create mode 100644 docs/MARKETING_COPY.md diff --git a/.gitignore b/.gitignore index 4136871..5d22002 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ Sources/Resources/Info.plist Sources/Resources/CardsNative.entitlements Widgets/CardsWidget/Resources/Info.plist Widgets/CardsWidget/Resources/CardsWidgetExtension.entitlements +ShareExtension/Resources/Info.plist +ShareExtension/Resources/CardsShareExtension.entitlements diff --git a/CLAUDE.md b/CLAUDE.md index 4513ab1..cf17405 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -18,7 +18,7 @@ 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) + │ ev.mana.cardecky │ WidgetKit (β-6) └──────────────────┘ ``` @@ -53,7 +53,7 @@ Beschlossen. Nicht ohne explizite Diskussion antasten. 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. +5. **Bundle-ID `ev.mana.cardecky`.** 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 @@ -74,7 +74,7 @@ Beschlossen. Nicht ohne explizite Diskussion antasten. sind **nicht** im Git - **SwiftFormat** mit `.swiftformat` (4-space, 120-col, sorted imports) - **SwiftLint** mit `.swiftlint.yml` -- **Logging:** App-Subsystem `ev.mana.cards` via +- **Logging:** App-Subsystem `ev.mana.cardecky` via `Sources/Core/Telemetry/Log.swift`. ManaCore loggt parallel unter `ev.mana.core` - **Persistenz:** SwiftData für Deck/Card-Cache (ab β-1), JWT im @@ -117,7 +117,7 @@ cards-native/ │ │ ├── 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) +│ │ ├── Telemetry/ OSLog (Subsystem ev.mana.cardecky) │ │ └── Theme/ CardsTheme (forest-Werte) │ ├── Widgets/ (WidgetKit-Extension — ab β-6) │ ├── ShareExtension/ (Save-as-Card — ab β-6) diff --git a/PLAN.md b/PLAN.md index 61aa4a5..b1dbd35 100644 --- a/PLAN.md +++ b/PLAN.md @@ -17,12 +17,12 @@ mit Flugmodus zwischendurch) steht aus — Aufgabe für Till. ✅ **β-0 — Setup (2026-05-12, Tag `v0.1.0`)** - Repo-Skelett unter `git.mana.how/till/cards-native` -- `project.yml` mit Bundle-ID `ev.mana.cards`, ManaSwiftCore via +- `project.yml` mit Bundle-ID `ev.mana.cardecky`, ManaSwiftCore via `path: ../mana-swift-core` - `AppConfig` als `ManaAppConfig`-Provider: - Auth: `https://auth.mana.how` - API: `https://cardecky-api.mana.how` - - Keychain-Service: `ev.mana.cards` + - Keychain-Service: `ev.mana.cardecky` - `CardsTheme.swift` mit forest-Werten (lokal nachgebaut aus `mana/packages/themes/src/variants/forest.css`) - `LoginView` (Email/PW gegen mana-auth) @@ -63,7 +63,7 @@ mit Flugmodus zwischendurch) steht aus — Aufgabe für Till. - `WidgetSnapshot` Codable mit `topDecks` (Top-3 nach dueCount) und `totalDueCount` - `WidgetSnapshotStore` schreibt in App-Group-Container - `group.ev.mana.cards` + `group.ev.mana.cardecky` - `DeckListStore.refresh` ruft `updateWidgetSnapshot()` und `WidgetCenter.shared.reloadAllTimelines()` nach jedem Pull - `CardsWidgetExtension`-Target (eigenes app-extension-Bundle): @@ -73,7 +73,7 @@ mit Flugmodus zwischendurch) steht aus — Aufgabe für Till. - `DueProvider` als `TimelineProvider`: liest Snapshot, plant Refresh alle 30 min (plus instant-Refresh via Haupt-App) - `DueWidgetView` mit Family-Switch, alle 5 Family-Layouts -- `com.apple.security.application-groups: group.ev.mana.cards` +- `com.apple.security.application-groups: group.ev.mana.cardecky` im Haupt- und Widget-Entitlement - `WidgetSnapshot.swift` in beiden Targets via XcodeGen-source-array (single-source-of-truth) diff --git a/README.md b/README.md index b62470f..2967c18 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Reachability-Check. Vollständiger Phasen-Plan in ``` HTTPS/JWT ┌──────────────────┐ cards-api ◄───────────── │ cards-native │ SwiftUI - cardecky-api.mana.how │ ev.mana.cards │ WidgetKit (β-6) + cardecky-api.mana.how │ ev.mana.cardecky │ WidgetKit (β-6) └──────────────────┘ │ ┌─────────────────────────────────────────┐ diff --git a/ShareExtension/Resources/CardsShareExtension.entitlements b/ShareExtension/Resources/CardsShareExtension.entitlements deleted file mode 100644 index 19e0259..0000000 --- a/ShareExtension/Resources/CardsShareExtension.entitlements +++ /dev/null @@ -1,10 +0,0 @@ - - - - - com.apple.security.application-groups - - group.ev.mana.cards - - - diff --git a/ShareExtension/Resources/Info.plist b/ShareExtension/Resources/Info.plist deleted file mode 100644 index 228f71f..0000000 --- a/ShareExtension/Resources/Info.plist +++ /dev/null @@ -1,41 +0,0 @@ - - - - - CFBundleDevelopmentRegion - $(DEVELOPMENT_LANGUAGE) - CFBundleDisplayName - Als Karte speichern - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - XPC! - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - NSExtension - - NSExtensionAttributes - - NSExtensionActivationRule - - NSExtensionActivationSupportsText - - NSExtensionActivationSupportsWebURLWithMaxCount - 1 - - - NSExtensionPointIdentifier - com.apple.share-services - NSExtensionPrincipalClass - $(PRODUCT_MODULE_NAME).ShareViewController - - - diff --git a/ShareExtension/ShareViewController.swift b/ShareExtension/ShareViewController.swift index d7b5cea..ee87506 100644 --- a/ShareExtension/ShareViewController.swift +++ b/ShareExtension/ShareViewController.swift @@ -72,7 +72,7 @@ final class ShareViewController: UIViewController { private func cancel() { dismiss(animated: true) { [weak self] in - self?.extensionContext?.cancelRequest(withError: NSError(domain: "ev.mana.cards.share", code: 0)) + self?.extensionContext?.cancelRequest(withError: NSError(domain: "ev.mana.cardecky.share", code: 0)) } } } diff --git a/Sources/Core/Auth/AppConfig.swift b/Sources/Core/Auth/AppConfig.swift index 14e28da..d07e8ab 100644 --- a/Sources/Core/Auth/AppConfig.swift +++ b/Sources/Core/Auth/AppConfig.swift @@ -7,7 +7,7 @@ import ManaCore enum AppConfig { static let manaAppConfig: ManaAppConfig = DefaultManaAppConfig( authBaseURL: URL(string: "https://auth.mana.how")!, - keychainService: "ev.mana.cards", + keychainService: "ev.mana.cardecky", keychainAccessGroup: nil ) diff --git a/Sources/Core/Notifications/NotificationManager.swift b/Sources/Core/Notifications/NotificationManager.swift index 53e2186..a48aec9 100644 --- a/Sources/Core/Notifications/NotificationManager.swift +++ b/Sources/Core/Notifications/NotificationManager.swift @@ -15,7 +15,7 @@ final class NotificationManager { } private(set) var authorization: AuthorizationStatus = .unknown - private let identifier = "ev.mana.cards.dailyReminder" + private let identifier = "ev.mana.cardecky.dailyReminder" private let store = UserDefaults.standard /// Persistiert User-Pref. Format: ISO-Stunde:Minute (default 18:00). diff --git a/Sources/Core/Sync/PendingShareStore.swift b/Sources/Core/Sync/PendingShareStore.swift index 87eca02..cd14d83 100644 --- a/Sources/Core/Sync/PendingShareStore.swift +++ b/Sources/Core/Sync/PendingShareStore.swift @@ -18,7 +18,7 @@ struct PendingShare: Codable, Identifiable, Hashable, Sendable { } enum PendingShareStore { - static let appGroupID = "group.ev.mana.cards" + static let appGroupID = "group.ev.mana.cardecky" static let filename = "pending-shares.json" static var url: URL? { diff --git a/Sources/Core/Sync/WidgetSnapshot.swift b/Sources/Core/Sync/WidgetSnapshot.swift index c76aa41..559694c 100644 --- a/Sources/Core/Sync/WidgetSnapshot.swift +++ b/Sources/Core/Sync/WidgetSnapshot.swift @@ -22,7 +22,7 @@ struct WidgetSnapshot: Codable, Sendable { /// Liest und schreibt WidgetSnapshot in den shared App-Group-Container. enum WidgetSnapshotStore { /// App-Group-ID — muss exakt mit dem Entitlement-Eintrag matchen. - static let appGroupID = "group.ev.mana.cards" + static let appGroupID = "group.ev.mana.cardecky" static let snapshotFilename = "widget-snapshot.json" static var snapshotURL: URL? { diff --git a/Sources/Core/Telemetry/Log.swift b/Sources/Core/Telemetry/Log.swift index 0be94ed..7a01f3c 100644 --- a/Sources/Core/Telemetry/Log.swift +++ b/Sources/Core/Telemetry/Log.swift @@ -1,13 +1,13 @@ import Foundation import OSLog -/// App-eigene OSLog-Logger unter Subsystem `ev.mana.cards`. +/// App-eigene OSLog-Logger unter Subsystem `ev.mana.cardecky`. /// ManaCore loggt unter `ev.mana.core` parallel — siehe /// `mana-swift-core/Sources/ManaCore/Telemetry/CoreLog.swift`. enum Log { - static let app = Logger(subsystem: "ev.mana.cards", category: "app") - static let auth = Logger(subsystem: "ev.mana.cards", category: "auth") - static let api = Logger(subsystem: "ev.mana.cards", category: "api") - static let study = Logger(subsystem: "ev.mana.cards", category: "study") - static let sync = Logger(subsystem: "ev.mana.cards", category: "sync") + static let app = Logger(subsystem: "ev.mana.cardecky", category: "app") + static let auth = Logger(subsystem: "ev.mana.cardecky", category: "auth") + static let api = Logger(subsystem: "ev.mana.cardecky", category: "api") + static let study = Logger(subsystem: "ev.mana.cardecky", category: "study") + static let sync = Logger(subsystem: "ev.mana.cardecky", category: "sync") } diff --git a/Tests/UnitTests/CardsNativeTests.swift b/Tests/UnitTests/CardsNativeTests.swift index b16c9e6..b979c91 100644 --- a/Tests/UnitTests/CardsNativeTests.swift +++ b/Tests/UnitTests/CardsNativeTests.swift @@ -13,8 +13,8 @@ struct AppConfigTests { #expect(AppConfig.manaAppConfig.authBaseURL.absoluteString == "https://auth.mana.how") } - @Test("Keychain-Service ist ev.mana.cards") + @Test("Keychain-Service ist ev.mana.cardecky") func keychainServiceIsAppSpecific() { - #expect(AppConfig.manaAppConfig.keychainService == "ev.mana.cards") + #expect(AppConfig.manaAppConfig.keychainService == "ev.mana.cardecky") } } diff --git a/Widgets/CardsWidget/CardsDueWidget.swift b/Widgets/CardsWidget/CardsDueWidget.swift index 35967fc..99023f6 100644 --- a/Widgets/CardsWidget/CardsDueWidget.swift +++ b/Widgets/CardsWidget/CardsDueWidget.swift @@ -2,7 +2,7 @@ import SwiftUI import WidgetKit struct CardsDueWidget: Widget { - let kind: String = "ev.mana.cards.due" + let kind: String = "ev.mana.cardecky.due" var body: some WidgetConfiguration { StaticConfiguration(kind: kind, provider: DueProvider()) { entry in diff --git a/docs/MARKETING_COPY.md b/docs/MARKETING_COPY.md new file mode 100644 index 0000000..8b493ef --- /dev/null +++ b/docs/MARKETING_COPY.md @@ -0,0 +1,155 @@ +# MARKETING_COPY — cards-native + +Vorschläge für App-Store-Description (de + en). Zum Eintragen in +App-Store-Connect. **Nicht final** — vor Submission durch dich +gegenlesen, Tonalität an Vereins-Stil schärfen (siehe `mana/docs/BRAND.md`, +`mana/docs/MISSION.md`). + +## Name + Subtitle + +**App-Name:** `Cards` +**Subtitle (max 30 Zeichen):** `Karteikarten — mana e.V.` + +## Keywords (max 100 Zeichen, comma-separated) + +``` +Karteikarten,Spaced Repetition,Lernen,Vokabeln,Anki,Flashcards,FSRS,mana,Verein,Open Source +``` + +(95 Zeichen) + +## Description — DE (max 4000 Zeichen) + +``` +Cards ist die Karteikarten-App des Vereins mana e.V. — Spaced +Repetition wie es sein soll: ohne Werbung, ohne Tracking, ohne +Abo-Zwang. + +KARTEN, WIE DU SIE BRAUCHST +- Klassische Vorder-/Rückseite, beidseitig, Lückentext (Cloze), + Eintippen, Multiple Choice +- Bild-Verdeckung mit selbst gesetzten Masken — für Anatomie, Karten, + Diagramme +- Audio-Karten für Sprachen und Aussprache + +MODERNER LERN-ALGORITHMUS +Cards nutzt FSRS (Free Spaced Repetition Scheduler), den genauesten +offenen Algorithmus für Karteikarten. Karten kommen wieder, wenn du +sie wirklich brauchst — nicht nach willkürlichen Intervallen. + +OFFLINE LERNEN +Karten cachen lokal auf deinem Gerät. Im Flugzeug, in der U-Bahn, +ohne WLAN — Cards funktioniert. Deine Bewertungen werden gequeued +und beim nächsten Online-Moment automatisch hochgeladen. + +WIDGETS UND NOTIFICATIONS +Zeige dir die heute fälligen Karten direkt auf dem Home-Bildschirm +oder Lock-Screen. Erinnerung zur Lieblings-Lern-Uhrzeit — lokal, ohne +externe Push-Server. + +CARDECKY — MARKETPLACE FÜR LERN-DECKS +Stöbere durch öffentliche Decks aus der Cardecky-Community: Geografie, +Sprachen, Wissenschaft. Abonnieren = Karten landen direkt in deiner +Bibliothek, du kannst sie nach Belieben anpassen. + +VEREIN, NICHT FIRMA +Cards wird vom Verein mana e.V. (Schweiz, in Gründung) betrieben. +Kein Tracking, kein Werbe-Anbieter, kein Crash-Reporter. Wenn du dem +Verein etwas zurückgeben willst: mana-ev.ch/spende. + +NATIV +Geschrieben in SwiftUI, optimiert für iPhone, iPad und Mac. Apple- +Pencil, Magic-Keyboard und Universal-Links sind voll unterstützt. +Teile aus Safari oder Mail direkt eine neue Karte. + +DSGVO-EHRLICH +Deine Daten gehören dir. Export jederzeit als JSON, Lösch-Knopf +löscht alles. Wir hosten in Europa, sprechen Deutsch, und antworten +selbst. +``` + +## Description — EN (max 4000 Zeichen) + +``` +Cards is the flashcard app from mana e.V. — spaced repetition the +way it should be: no ads, no tracking, no subscription pressure. + +THE CARDS YOU NEED +- Classic front/back, two-sided, cloze, type-in, multiple-choice +- Image occlusion with hand-drawn masks — for anatomy, maps, diagrams +- Audio cards for languages and pronunciation + +MODERN SPACED-REPETITION +Cards uses FSRS (Free Spaced Repetition Scheduler), the most accurate +open algorithm available. Cards come back when you actually need them. + +OFFLINE-FIRST +Decks cache locally on your device. On a plane, in the subway, off +the grid — Cards keeps working. Your reviews queue up and sync on +the next online moment. + +WIDGETS AND REMINDERS +See today's due cards on your Home Screen or Lock Screen. Daily +reminder at your preferred time — local, no push servers. + +CARDECKY MARKETPLACE +Browse public decks from the Cardecky community: geography, languages, +science. Subscribe = decks land in your library, ready to be edited. + +ASSOCIATION, NOT CORPORATION +Cards is operated by mana e.V. (Switzerland, formation in progress). +No tracking, no ad networks, no crash reporters. Want to give back? +mana-ev.ch/donate. + +NATIVE +Built in SwiftUI for iPhone, iPad, and Mac. Apple Pencil, +Magic Keyboard, and Universal Links are first-class. Share from +Safari or Mail to create a card. + +GDPR-HONEST +Your data, your call. Full JSON export, single-button delete. +Hosted in Europe, German-speaking support, replies from real humans. +``` + +## Privacy-Declaration (App-Store-Connect) + +| Datentyp | Gesammelt | Zweck | Verlinkt mit User | Tracking | +|---|---|---|---|---| +| Email | Ja | App-Functionality (Login) | Ja | Nein | +| User Content (Cards, Decks) | Ja | App-Functionality | Ja | Nein | +| Usage Data (FSRS Reviews) | Ja | App-Functionality | Ja | Nein | + +Keine weiteren Kategorien. Insbesondere: keine `Identifiers`, +keine `Diagnostics`, keine `Location`, keine `Contacts`. + +## Privacy-Policy- + Support-URLs + +Vor Submission setzen — vermutlich: +- Privacy-Policy: `https://cardecky.mana.how/privacy` (existiert? prüfen) +- Support: `https://cardecky.mana.how/help` oder `kontakt@mana-ev.ch` +- Marketing: `https://cardecky.mana.how` + +Falls die URLs noch nicht live sind, vor Submission in cards-web +ergänzen (Routes `/privacy`, `/help`). + +## Screenshot-Skizzen + +Pro Locale (de, en) jeweils 3–5 Screenshots zeigen: + +1. **DeckListView mit fälligen Karten** — Hauptbildschirm, "X Karten fällig" sichtbar +2. **Study mit gefliptem Cloze** — Markierte Antwort, RatingBar unten +3. **Image-Occlusion** — Bild mit aktiver Maske + Reveal-Hover +4. **Marketplace Explore** — Featured-Carousel +5. **Widget auf Home-Screen** (optional, via Simulator-Screenshot komponiert) + +iPhone-Größen (mindestens): +- 6.7" (iPhone 16 Pro Max) +- 6.5" (iPhone XS Max / 11 Pro Max / 14 Plus) +- 5.5" (iPhone 8 Plus — wenn ältere Support-Tier) + +Plus iPad: +- 12.9" (iPad Pro) + +Apple skaliert die größere Auflösung auf kleinere Slots, wenn keine +spezifischen Screenshots vorhanden — Mindest-Set ist daher Größe 6.7" +für iPhone + 12.9" für iPad. Reicht zur Submission. diff --git a/docs/RELEASE_CHECKLIST.md b/docs/RELEASE_CHECKLIST.md index 84e7291..f4f7fe4 100644 --- a/docs/RELEASE_CHECKLIST.md +++ b/docs/RELEASE_CHECKLIST.md @@ -11,14 +11,14 @@ AASA) und über Xcode (für Build + Sign). - [x] **Team-ID gesetzt** (`QP3GLU8PH3`, mana e.V.) — `DEVELOPMENT_TEAM` in `project.yml > settings > base`. Greift bei Archive automatisch. -- [ ] **App-ID `ev.mana.cards`** im Developer-Portal anlegen, falls +- [ ] **App-ID `ev.mana.cardecky`** im Developer-Portal anlegen, falls noch nicht da. Mit Capabilities: App Groups, Keychain Sharing, Associated Domains. -- [ ] **App-ID `ev.mana.cards.share`** + **`ev.mana.cards.widget`** für +- [ ] **App-ID `ev.mana.cardecky.share`** + **`ev.mana.cardecky.widget`** für die Extensions analog anlegen, ebenfalls mit App Groups. -- [ ] **App-Group `group.ev.mana.cards`** im Portal anlegen und allen +- [ ] **App-Group `group.ev.mana.cardecky`** im Portal anlegen und allen drei App-IDs zuweisen. -- [ ] **Keychain-Access-Group**: heute `ev.mana.cards`. Wenn +- [ ] **Keychain-Access-Group**: heute `ev.mana.cardecky`. Wenn Shared-Keychain mit `memoro-native` gewünscht (siehe `mana/docs/MANA_SWIFT.md` Phase γ), auf `$(AppIdentifierPrefix)ev.mana.shared` umstellen und @@ -49,9 +49,11 @@ AASA) und über Xcode (für Build + Sign). `cards/infrastructure/docker-compose.production.yml` hinterlegt (Commit folgt). Wird zur Runtime von `$env/dynamic/public` aufgelöst und in den AASA-Response geschrieben. -- [ ] **Production-Deploy von cards-web** mit dem neuen Compose-Stand: - `cd ~/projects/cards/infrastructure && docker compose -f docker-compose.production.yml up -d cards-web` auf mana-server. - Erst danach liefert die AASA die echte Team-ID statt Platzhalter. +- [x] **Production-Deploy von cards-web** durchgeführt 2026-05-13. + Probe von außen: `curl https://cardecky.mana.how/.well-known/apple-app-site-association` + liefert `application/json` mit `"appID":"QP3GLU8PH3.ev.mana.cardecky"`. + Cloudflare-Tunnel reicht den Endpoint sauber durch (kein + HTML-Captive, kein Redirect). - [ ] **cardecky-api.mana.how** muss erreichbar bleiben — die App ist 100% Online-write. Health-Probe verifizieren. @@ -84,12 +86,13 @@ AASA) und über Xcode (für Build + Sign). ### App-Store-Connect - [ ] **App-Eintrag erstellen** unter https://appstoreconnect.apple.com - mit Bundle-ID `ev.mana.cards`. + mit Bundle-ID `ev.mana.cardecky`. - [ ] **App-Name** + **Subtitle** (max 30 Zeichen): - Name: "Cards" - Subtitle: "Karteikarten — Verein mana" -- [ ] **Description** (de + en, max 4000 Zeichen). Vorschlag siehe - `docs/MARKETING_COPY.md` (existiert noch nicht — TODO). +- [ ] **Description** (de + en, max 4000 Zeichen). Vorschlag in + [`docs/MARKETING_COPY.md`](MARKETING_COPY.md) — vor Submission + gegenlesen und Vereins-Tonalität schärfen. - [ ] **Keywords** (max 100 Zeichen, comma-separated): "Karteikarten,Spaced Repetition,Lernen,Vokabeln,Anki,Flashcards,FSRS,mana,Verein,Open Source" - [ ] **Screenshots** für iPhone 16 Pro Max + iPhone SE-3 + iPad Pro. diff --git a/project.yml b/project.yml index 7fdf6f6..a2879d6 100644 --- a/project.yml +++ b/project.yml @@ -60,7 +60,7 @@ targets: LSApplicationCategoryType: "public.app-category.education" UILaunchScreen: {} CFBundleURLTypes: - - CFBundleURLName: ev.mana.cards + - CFBundleURLName: ev.mana.cardecky CFBundleURLSchemes: - cards NSUserActivityTypes: @@ -74,14 +74,14 @@ targets: com.apple.security.network.client: true com.apple.security.files.user-selected.read-write: true keychain-access-groups: - - $(AppIdentifierPrefix)ev.mana.cards + - $(AppIdentifierPrefix)ev.mana.cardecky com.apple.developer.associated-domains: - applinks:cardecky.mana.how com.apple.security.application-groups: - - group.ev.mana.cards + - group.ev.mana.cardecky settings: base: - PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cards + PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cardecky CODE_SIGN_STYLE: Automatic ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor @@ -111,10 +111,10 @@ targets: path: ShareExtension/Resources/CardsShareExtension.entitlements properties: com.apple.security.application-groups: - - group.ev.mana.cards + - group.ev.mana.cardecky settings: base: - PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cards.share + PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cardecky.share CODE_SIGN_STYLE: Automatic SKIP_INSTALL: "YES" @@ -137,13 +137,13 @@ targets: path: Widgets/CardsWidget/Resources/CardsWidgetExtension.entitlements properties: com.apple.security.application-groups: - - group.ev.mana.cards + - group.ev.mana.cardecky dependencies: - sdk: WidgetKit.framework - sdk: SwiftUI.framework settings: base: - PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cards.widget + PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cardecky.widget CODE_SIGN_STYLE: Automatic SKIP_INSTALL: "YES" INFOPLIST_KEY_CFBundleDisplayName: Cards Widget @@ -157,7 +157,7 @@ targets: - target: CardsNative settings: base: - PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cards.tests + PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cardecky.tests GENERATE_INFOPLIST_FILE: "YES" CardsNativeUITests: @@ -169,7 +169,7 @@ targets: - target: CardsNative settings: base: - PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cards.uitests + PRODUCT_BUNDLE_IDENTIFIER: ev.mana.cardecky.uitests GENERATE_INFOPLIST_FILE: "YES" schemes: