Skip to main content

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.
Never compute display strings on the client. Values like "87/6", "RR: 8.50", or "Rohit Sharma 45 (32)" arrive ready-to-render from the API. If you find yourself building a display string in Swift, you are violating the architecture.

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

The app uses MVVM powered by Swift’s @Observable macro (Observation framework).
LayerResponsibility
ViewSwiftUI views that read from observable models and render UI
ViewModel@Observable classes that hold view state and call services
ServiceProtocol-based services that fetch, cache, and mutate data
BackendSource of truth for all data and display formatting
ViewModels are @Observable classes, not ObservableObject with @Published. This uses the newer Observation framework available from iOS 17, with backcompat handled where needed.

Dependency injection

DI is done through SwiftUI’s @Environment. Each service protocol has an EnvironmentKey extension, and views access services declaratively.
// Accessing a service in a view
struct MatchListView: View {
    @Environment(\.scheduleService) private var scheduleService

    var body: some View {
        // ...
    }
}
// Defining the environment key
private struct ScheduleServiceKey: EnvironmentKey {
    static let defaultValue: ScheduleService = RealScheduleService()
}

extension EnvironmentValues {
    var scheduleService: ScheduleService {
        get { self[ScheduleServiceKey.self] }
        set { self[ScheduleServiceKey.self] = newValue }
    }
}
Every protocol has two implementations:
  • Real — hits the actual backend API
  • Fake — returns canned data for previews and testing
The app entry point injects the real implementations. SwiftUI previews and tests inject fakes.

Data flow

1

Backend returns pre-formatted data

The API responds with display-ready models — formatted strings, ordered arrays, resolved image URLs.
2

Service layer fetches and caches

Protocol-based services make the API call, decode the response, and cache results in memory.
3

ViewModel exposes state

@Observable view models hold the decoded data. SwiftUI views automatically re-render when properties change.
4

Views render

SwiftUI views bind directly to view model properties. No transformation — just layout and styling.
5

User actions flow back

Taps, follows, and other interactions call service methods, which hit the backend API.

Tech constraints

ConstraintValue
UI frameworkSwiftUI only (no UIKit)
Minimum iOS version16.1
Reason for iOS 16.1Live Activities require ActivityKit, available from iOS 16.1
ArchitectureMVVM with @Observable
DI mechanism@Environment with protocol-based services
Data formatAll display data pre-formatted by backend
The iOS 16.1 minimum means you can use NavigationStack, @Observable (with backcompat), and the full ActivityKit API surface.