Documentation Index
Fetch the complete documentation index at: https://285e39fd5e337e58f16290.sightscreen.app/llms.txt
Use this file to discover all available pages before exploring further.
Core philosophy
The iOS app is a dumb view layer. The backend owns all formatting, string construction, and business logic. The app’s job is to receive pre-formatted data and render it.What the iOS app does NOT do
- Format scores, overs, run rates, or player stats
- Decide what to show based on match state (e.g., innings break vs. live)
- Apply conditional logic to determine display layouts based on match phase
- Transform raw data into user-facing text
What the iOS app DOES do
- Render pre-formatted display states from the backend
- Manage UI navigation and presentation
- Handle device-level concerns (Live Activities, push tokens, permissions)
- Cache and poll for data
- Send user actions back to the backend
MVVM with @Observable and @Environment DI
The app uses MVVM powered by Swift’s@Observable macro (Observation framework), with dependency injection through @Environment.
| Layer | Responsibility |
|---|---|
| View | SwiftUI views that read from observable models and render UI |
| ViewModel | @Observable classes that hold view state and call services |
| Service | Protocol-based services injected via @Environment, each with Real + Fake implementations |
| Backend | Source of truth for all data and display formatting |
- Real — hits the actual backend API
- Fake — returns canned data for previews and testing
ViewModels are
@Observable classes, not ObservableObject with @Published. This uses the newer Observation framework available from iOS 17, with backcompat handled where needed.Facade pattern: LiveActivityService
The most complex subsystem uses the facade pattern.LiveActivityService is a thin coordinator that composes three focused managers:
Views and other services only interact with the facade. The three managers are internal implementation details.
| Manager | Single responsibility |
|---|---|
TokenManager | Push-to-start + update token observation, deduplication, backend registration |
LifecycleManager | Activity.request(), adopt push-started LAs, state observation, stale check, restore |
FollowManager | Follow/unfollow API, local persistence, retry, bell icon state, sync from server |
DataSource + ViewSubscriber
A generic pattern for any data that needs to be fetched, cached, and optionally polled.DataSource protocol
ViewSubscriber modifier
A SwiftUI view modifier that binds aDataSource to a view’s lifecycle.
| Behavior | Detail |
|---|---|
| Lifecycle binding | onAppear starts polling, onDisappear stops |
| ScenePhase awareness | Pauses in background, resumes on foreground |
| Cache-first | Shows cached data immediately, refreshes in background |
Concrete data sources
| DataSource | Behavior |
|---|---|
LiveMatchesDataSource | Always polls. pollIntervalMs is server-driven via retryAfterMs in response |
ScheduleDataSource | Per-date caching. Only polls for today’s date (historical dates are static) |
Cache-first reads with stale checking
The app follows a cache-first strategy for all data:MatchCacheStoreindexes matches by date with TTL based on match state (live = short TTL, completed = long TTL)ImageCacheServiceuses a two-tier cache (memory + disk in app group container)ScheduleServiceuses a sliding-window cache for N days of schedule data
Cache-first means the UI never shows a loading spinner for data the user has already seen. Stale checks happen in the background and the UI updates reactively via
@Observable.Swift 6 actor isolation
The app uses Swift 6 strict concurrency. Key patterns:| Pattern | Where used |
|---|---|
@MainActor | All @Observable classes, ViewModels, and UI-bound state |
Sendable conformance | All model types (generated and hand-written) |
nonisolated methods | Pure computation and encoding/decoding that does not touch UI state |
| Structured concurrency | Task {} blocks scoped to view lifecycle via ViewSubscriber |
Data flow
Backend returns pre-formatted data
The API responds with display-ready models — formatted strings, ordered arrays, resolved image URLs.
Service layer fetches and caches
Protocol-based services make the API call, decode the response, and cache results (via
MatchCacheStore or service-local caches).ViewModel exposes state
@Observable view models hold the decoded data. SwiftUI views automatically re-render when properties change.Views render
SwiftUI views bind directly to view model properties. No transformation — just layout and styling.
Tech constraints
| Constraint | Value |
|---|---|
| UI framework | SwiftUI only (no UIKit) |
| Minimum iOS version | 16.1 |
| Reason for iOS 16.1 | Live Activities require ActivityKit, available from iOS 16.1 |
| Concurrency model | Swift 6 strict concurrency |
| Architecture | MVVM with @Observable |
| DI mechanism | @Environment with protocol-based services |
| Data format | All display data pre-formatted by backend |