Repository navigation
feat(v1.9.8): "Horizon" — iOS store-readiness - #203
Conversation
The iOS store-readiness pass (the analogue of Android v1.8.8 "Atlas"): accessibility, EN/ES i18n, ReplayKit capture, Game Center, a 4.7 + privacy-manifest pass, and the dormant StoreKit / foss-vs-App-Store seam. No Rust/core/bridge changes; only CARGO_PKG_VERSION moves 1.9.7 -> 1.9.8, so the shipped/native/no_std/wasm core stays byte-identical and AccuracyCoin holds 100% (139/139). Interim TestFlight. - Accessibility: VoiceOver labels/hints/values across the pill menu / top bar / library cells / palette + save-state rows / Settings; Dynamic Type (pill icons -> scalable .title3, decorative symbols hidden); built-in High-contrast + Okabe-Ito colourblind palettes (AccessibilityPalettes.swift, generated deterministically, feeding the same loadPalette path), selectable globally + per-game. - EN/ES i18n: a String Catalog (Localizable.xcstrings, sourceLanguage en, 84 ES entries) via SwiftUI's automatic LocalizedStringKey lookup; English byte-identical (keys are the English strings). CFBundleLocalizations en,es. Full coverage incremental. - ReplayKit: a Record toggle (pill menu + Settings) -> RPScreenRecorder start/stop -> RPPreviewViewController; graceful not-available/denied; PiP a documented follow-up. - Game Center (GameCenterModel.swift, opt-in, default off): GKLocalPlayer auth + GKAccessPoint; no app-defined achievements (RA stays the achievement system); GKSavedGame flagged as a future CloudKit alternative; no-op when unavailable. - Privacy manifest: discloses RetroAchievements (UserID, linked/functionality) + Game Center (OtherDataTypes); CloudKit intentionally not listed (user's own private DB); FileTimestamp C617.1 + UserDefaults CA92.1 reasons added; NSPrivacyTracking false. - foss/StoreKit seam: an APPSTORE_BUILD compile flag (SWIFT_ACTIVE_COMPILATION_ CONDITIONS, default foss; BuildChannel.swift) + a dormant StoreManager (real StoreKit 2 calls behind a default-off guard) -- both channels behave identically (fully unlocked, no ads) until the v2.1.0 split (ADR 0027). ReplayKit/GameKit/ StoreKit frameworks linked in project.yml. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
There was a problem hiding this comment.
Code Review
This pull request prepares the iOS SwiftUI shell of RustyNES for store-readiness (v1.9.8 "Horizon") by adding accessibility features (VoiceOver support and custom high-contrast/colorblind palettes), EN/ES internationalization via a String Catalog, gameplay video capture using ReplayKit, and opt-in Game Center integration. It also updates the privacy manifest and introduces a dormant StoreKit 2 scaffold along with a compile-time FOSS vs. App Store build channel seam. The review feedback highlights several critical improvements: in GameCenterModel, the authentication handler should guard against disabled states, clear pending auth requests to prevent stuck UI, and clean up state when disabled; in Entitlements, the entitlement reconciliation logic needs to properly reset the purchase status to handle refunded or revoked transactions.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
Pull request overview
iOS v1.9.8 “Horizon” store-readiness pass focused on SwiftUI-shell improvements (accessibility, i18n, ReplayKit, Game Center, privacy manifest) plus distribution seams and version bumps, while keeping the Rust core byte-identical.
Changes:
- Adds opt-in Game Center authentication/presence and ReplayKit screen recording flows (with SwiftUI hosts for system controllers).
- Introduces EN/ES localization via a String Catalog and broad VoiceOver/Dynamic Type improvements across UI surfaces.
- Adds privacy manifest disclosures, dormant StoreKit scaffolding + foss/appstore build-channel seam, and bumps versioning/docs.
Reviewed changes
Copilot reviewed 21 out of 22 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| ios/RustyNES/SettingsView.swift | Adds Settings sections for Game Center + screen recording; enhances palette picker accessibility/localization. |
| ios/RustyNES/ScreenRecorder.swift | Implements ReplayKit recording + preview presentation host. |
| ios/RustyNES/SaveStatesView.swift | Improves save-state UI accessibility (decorative icon hiding; button labels). |
| ios/RustyNES/PrivacyInfo.xcprivacy | Updates privacy manifest for optional RA/Game Center + required-reason APIs. |
| ios/RustyNES/PillMenu.swift | Adds Record pill and expands VoiceOver labels/hints + Dynamic Type-friendly icon sizing. |
| ios/RustyNES/OnboardingView.swift | Adjusts onboarding strings to flow through String Catalog + improves accessibility. |
| ios/RustyNES/Localizable.xcstrings | Adds EN source String Catalog with ES translations for key strings. |
| ios/RustyNES/Info.plist | Declares supported localizations (en, es). |
| ios/RustyNES/GameView.swift | Wires pill menu recorder + preview host and improves top-bar accessibility. |
| ios/RustyNES/GameCenterModel.swift | Adds opt-in Game Center model + SwiftUI host for GameKit auth UI. |
| ios/RustyNES/Entitlements.swift | Adds dormant StoreKit 2 scaffold (StoreManager) and links StoreKit. |
| ios/RustyNES/ContentView.swift | Adds Game Center auth host and improves library tile accessibility. |
| ios/RustyNES/BuildChannel.swift | Adds foss vs App Store compile-time channel seam. |
| ios/RustyNES/AppModel.swift | Wires new iOS models and adds built-in accessibility palette resolution + localized error string usage. |
| ios/RustyNES/AccessibilityPalettes.swift | Adds deterministic built-in accessibility palettes and lookup helpers. |
| ios/RustyNES/AboutView.swift | Makes ownership notice localizable via String Catalog. |
| ios/project.yml | Links ReplayKit/GameKit/StoreKit and documents build-channel/i18n notes for XcodeGen. |
| docs/STATUS.md | Updates status matrix for v1.9.8 release. |
| CHANGELOG.md | Adds v1.9.8 release notes. |
| Cargo.toml | Bumps workspace version to 1.9.8. |
| Cargo.lock | Bumps crate versions to 1.9.8. |
| android/app/build.gradle.kts | Bumps Android versionCode/versionName to 1.9.8. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
… reset, ES localization Resolve the v1.9.8 "Horizon" bot-review threads (all confined to ios/RustyNES/): - GameCenterModel: clear authenticateHandler + deactivate access point + reset published state on disable so stale callbacks can't fire; guard `enabled` inside the handler before presenting. - Entitlements.reconcileEntitlements: track unlock in a local var and assign purchased after the loop so a refund/revoke flips purchased back to false. - Localization: route the palette + HD-pack load errors through String(format: String(localized:), ...); wrap interpolated accessibility labels/hints/values (save-state slots, library item, mute control) in Text(...) so they resolve as LocalizedStringKey and pick up the ES catalog. - Localizable.xcstrings: add 8 EN source keys + ES translations. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v1.9.8 "Horizon" — iOS store-readiness
The iOS store-readiness pass (the analogue of Android v1.8.8 "Atlas") — a broad pre-launch release. All SwiftUI-shell work — no Rust / core / bridge changes; only
CARGO_PKG_VERSIONmoves 1.9.7 → 1.9.8, so the core stays byte-identical and AccuracyCoin holds 100% (139/139). Interim TestFlight.Added
AccessibilityPalettes.swift, generated deterministically, feeding the sameloadPalettepath), selectable globally + per-game.Localizable.xcstrings, sourceen, 84 ES entries) via SwiftUI's automaticLocalizedStringKeylookup; English stays byte-identical (keys are the English strings);CFBundleLocalizationsen,es. Full coverage incremental.RPScreenRecorderstart/stop →RPPreviewViewController; graceful not-available/denied; PiP a documented follow-up.GameCenterModel, opt-in, default off) —GKLocalPlayerauth +GKAccessPoint; no app-defined achievements (RetroAchievements stays the system);GKSavedGameflagged as a future CloudKit alternative; no-op when unavailable.UserID, linked/functionality) + Game Center (OtherDataTypes); CloudKit intentionally not listed (the user's own private DB);FileTimestamp C617.1+UserDefaults CA92.1reasons added;NSPrivacyTrackingfalse.APPSTORE_BUILDcompile flag (SWIFT_ACTIVE_COMPILATION_CONDITIONS, defaultfoss;BuildChannel.swift) + a dormantStoreManager(real StoreKit 2 calls behind a default-off guard) — both channels behave identically (fully unlocked, no ads) until the v2.1.0 split (ADR 0027). ReplayKit/GameKit/StoreKit frameworks linked.Host gates green (fmt / clippy
-D warnings) + markdownlint clean; the String Catalog + plists validated. On-device validation of the accessibility/i18n/ReplayKit/Game-Center paths remains the documented TestFlight carryover.🤖 Generated with Claude Code