mirror of
https://github.com/yattee/yattee.git
synced 2026-06-04 13:54:19 +00:00
New Release-DeveloperID configuration gates Sparkle behind a SPARKLE compile flag so the App Store Release build stays Sparkle-free. Adds SPUStandardUpdaterController wrapper, Check for Updates menu command, Advanced Settings section with beta channel toggle, and a Ruby script plus GitHub Actions job that signs each release and publishes the appcast to gh-pages for consumption by Sparkle and Homebrew cask.
72 lines
3.8 KiB
Markdown
72 lines
3.8 KiB
Markdown
# Yattee Development Guide for AI Agents
|
|
|
|
## Deployment Targets
|
|
|
|
**iOS:** 18.0+ | **macOS:** 15.0+ | **tvOS:** 18.0+
|
|
|
|
This project targets the latest OS versions only - use newest APIs freely without availability checks.
|
|
|
|
## Build & Test Commands
|
|
|
|
**Build:** `xcodebuild -scheme Yattee -configuration Debug`
|
|
**Test (all):** `xcodebuild test -scheme Yattee -destination 'platform=macOS'`
|
|
**Test (single):** `xcodebuild test -scheme Yattee -destination 'platform=macOS' -only-testing:YatteeTests/TestSuiteName/testMethodName`
|
|
**Lint:** `periphery scan` (config: `.periphery.yml`)
|
|
|
|
## Build Configurations
|
|
|
|
Three configurations exist, mapped to distribution channels:
|
|
|
|
| Configuration | Sparkle (`#if SPARKLE`) | Used for |
|
|
|---|---|---|
|
|
| `Debug` | off | local development, tests |
|
|
| `Release` | off | App Store / TestFlight (`fastlane mac beta`) — must stay Sparkle-free, App Review rejects auto-update frameworks |
|
|
| `Release-DeveloperID` | **on** | Developer ID notarized build (`fastlane mac build_and_notarize`), distributed via GitHub Releases + Homebrew cask, receives Sparkle updates |
|
|
|
|
All Sparkle-dependent code must be wrapped in `#if SPARKLE ... #endif` so the `Release` variant links zero Sparkle symbols. When adding new Sparkle features, test both configs build clean on macOS.
|
|
|
|
## Code Style
|
|
|
|
**Language:** Swift 5.0+ with strict concurrency (Swift 6 mode enabled)
|
|
**UI:** SwiftUI with `@Observable` macro for view models (not `ObservableObject`)
|
|
**Concurrency:** Use `actor` for services, `@MainActor` for UI-related code, `async/await` everywhere
|
|
**Testing:** Swift Testing framework (`@Test`, `@Suite`, `#expect`) - NOT XCTest
|
|
|
|
## Imports & Organization
|
|
|
|
**Import order:** Foundation first, then SwiftUI, then @testable imports
|
|
**File headers:** Include `// FileName.swift`, `// Yattee`, and brief comment describing purpose
|
|
**MARK comments:** Use `// MARK: - Section Name` to organize code sections
|
|
**Sendable:** All models, errors, and actors must conform to `Sendable`
|
|
|
|
## Types & Naming
|
|
|
|
**Models:** Immutable structs with `Codable, Hashable, Sendable` conformance
|
|
**Services:** Use `actor` for thread-safe services, `final class` for `@Observable` view models
|
|
**Enums:** Use associated values for typed errors (see `APIError.swift`)
|
|
**Optionals:** Prefer guard-let unwrapping; use `if let` for simple cases
|
|
**Naming:** camelCase for variables/functions, PascalCase for types, clear descriptive names
|
|
|
|
## Error Handling
|
|
|
|
**Errors:** Define typed enum errors conforming to `Error, LocalizedError, Equatable, Sendable`
|
|
**Async throws:** All async network/IO operations should throw typed errors
|
|
**Logging:** Use `LoggingService.shared` for all logging (see `HTTPClient.swift` for patterns)
|
|
**User feedback:** Provide localized error descriptions via `errorDescription`
|
|
|
|
## Testing & Debugging
|
|
|
|
**Add logging/visual clues** (borders, backgrounds) when debugging issues - then ask user for results
|
|
**If first fix doesn't work:** Add debug code before second attempt to understand the issue better
|
|
|
|
## UI Testing (Ruby/RSpec with AXe CLI)
|
|
|
|
**Run UI tests:** `./bin/ui-test --skip-build --keep-simulator`
|
|
**Run single spec:** `SKIP_BUILD=1 KEEP_SIMULATOR=1 bundle exec rspec spec/ui/smoke/search_spec.rb`
|
|
|
|
**Accessibility labels vs identifiers:** On iOS 26+, `.accessibilityIdentifier()` doesn't work reliably on `Group`, `ScrollView`, and some container views (AXUniqueId comes back empty). Use `.accessibilityLabel()` instead, which maps to `AXLabel` and can be detected via AXe's `text_visible?()` method.
|
|
|
|
**iOS 26 TabView search:** The search field is integrated into the bottom tab bar with `Tab(role: .search)`. Typing `\n` doesn't submit - use hardware key press via `press_return` (AXe key 40).
|
|
|
|
**ScrollView children:** Video rows inside `LazyVStack`/`ScrollView` aren't exposed in the accessibility tree. Use coordinate-based tapping instead.
|