Technical Documentation · 27 Jun 2026

Cricket
Score Board

A SwiftUI app that runs a whole match from your pocket — ball-by-ball scoring, player careers, scorecards and self-contained season portals. Built for gully, club and backyard cricket, no scoring service required.

SwiftUI · iOS / iPadOS Swift 5 Local-first JSON + Keychain 0 third-party deps
LIVE SCORING 2nd Innings · Chasing 201
Riverside CC
181-3 Overs17.0
Run Rate10.6
Required7.0
Need7 off 18
This Over0 runs
1 4 W 6
A. Mehta74 (41)
R. Singh52 (38)
⬤ J. Patel3.4 — 1/28
01 The match card

A full innings of engineering

One native app target, no package manager, no backend — and a surprising amount of depth packed into local-first storage. The numbers that define the project at a glance.

0
Apple frameworks
SwiftUI, UIKit, Combine, AVKit, CryptoKit & more — zero third-party dependencies.
+
State managers
ObservableObject services acting as view-models across the app.
0
Navigation routes
From welcome through scoreboard to recoveryBin.
0
iOS deploy target
Builds against iOS / iPadOS 26.2, Swift 5 mode.
02 The home screen

One app, six pathways

The home screen organizes the whole surface area into six clear routes, each a self-contained module so an in-progress match never collides with a maintenance task. iPad shows a hero panel beside a clean menu; iPhone stacks the same six as full-width cards.

01

Start a Match

Setup, toss and live scoring — the full lifecycle of a single game.

02

Players

Roster management and per-player career statistics across all matches.

03

Match History

A browsable archive of completed matches with full scorecards and media.

04

Photos & Videos

Attach media to matches and browse it back through a gallery view.

05

Cricket Rules

All 42 ICC standard rules, searchable and filterable by category.

06

Settings

Display name, explicit backup & restore via the Files app — no accounts, no sync.

03 What it does

From toss to scorecard

Everything a scorer needs across the life of a match — and the whole season after it. Each surface is a substantial SwiftUI screen backed by its own manager and persistence.

Live ball-by-ball scoring

Runs, wickets, extras, strike rotation, bowler changes, over summaries, undo, haptics and chase logic — the heart of the app.

ScoreBoardViewScoreboardStateManager

Player careers & rankings

Profiles with photos, roles and styles. Batting/bowling averages, strike rates and economy are rebuilt from match history, with charts, rankings and head-to-head comparison.

PlayerManagerPlayerStatsCharts

Match history & scorecards

Detailed scorecards, media attachments, season grouping by Spring/Summer/Fall/Winter, archiving and full export.

MatchHistoryManagerMatchDetailView

Animated toss & setup

Team names, captains, overs, roster selection and an animated coin toss that feeds the batting decision back into setup.

CoinTossViewMatchSetupModel

External scoreboard display

Projects a live score snapshot onto a connected external display through a dedicated scene declared in the Info.plist.

LiveScoreProjectionCenter

Backup, import & HTML portal

JSON backups of players, matches, media and rules — plus a self-contained HTML season portal with embedded data, avatars and charts.

AppBackupServiceAppHTMLPortalExportService

30-day recovery bin

Soft-deletes for players, matches, rules and media. Entries are recoverable for 30 days before purge.

RecoveryBinManager

Custom & bundled rules

A built-in cricket rulebook plus full create / edit / delete of custom rules, persisted to JSON with legacy migration.

CricketRulesViewCustomRulesManager

Global search & sharing

Cross-entity search over players and matches, with image, CSV, text, JSON and print-optimised export across share sheets.

GlobalSearchViewMatchExportManager

Speed warnings & penalties

One tap flags a bowler warning; a second opens a dead-ball runs dialog. Penalties dock −5 or −10, with their own ball and wicket rules.

WarnP5P10

Live stats popup

Aggregated player and match statistics on demand — an always-visible card on iPad, a full-screen sheet on iPhone to keep the scoring grid clear.

Top PerformersStats Cards

Broadcast strip

In landscape, a persistent banner shows live runs, wickets, overs and target in large readable type — and anchors the foot of the iPad scoreboard.

CurrentPlayersCardlandscape
04 How it's built

SwiftUI, with managers at the crease

Primarily SwiftUI with ObservableObject state managers. It resembles MVVM in places without being a strict implementation — views own real UI state while managers act as view-models and services over a file-backed persistence layer.

State management

State is local and in-memory while the app runs, then saved to JSON, UserDefaults or Keychain. AppCoordinator owns long-lived managers with @StateObject; screens receive them via @ObservedObject, bindings or callbacks.

ScoreboardStateManager holds live match state and nested MatchPerformanceTracker instances per innings.

Dependency injection

Lightweight and pragmatic. Views commonly receive managers as initializer parameters. SessionManager accepts optional AuthService, KeychainManager and UserDefaults for testability.

Several services are static or singleton-based — convenient, but a noted limit on testability.

App & Navlaunch · routing
Cricket_Score_BoardAppAppCoordinatorAppDelegateNavigationDestination
PresentationSwiftUI screens
ScoreBoardViewPlayerDetailViewMatchDetailViewWelcomeView…*View.swift
Managersstate · lifecycle
ScoreboardStateManagerPlayerManagerMatchHistoryManagerSessionManagerCustomRulesManager
Domain ModelsCodable structs
PlayerMatchRecordMatchSeasonBatsmanPerformanceMatchDeliveryRecordCricketRule
PersistenceJSON · Keychain
DurableJSONStoreMatchMediaStorageKeychainManagerUserDefaults
Export & Shareportals · reports
AppBackupServiceAppHTMLPortalExportServiceMatchExportManagerRankingsImageGenerator
Design Systemtokens · controls
AppAppearanceMatchSetupDesignSystemRankingsDesignSystemModernBackButton
04.b Component hierarchy

Four branches off the coordinator

The view tree is rooted at AppCoordinator, which dispatches into four self-contained branches. Communication is unidirectional — views read and write state managers, managers reach services, services reach disk; views never touch the data layer directly.

AppCoordinator — owns root state managers
Welcome

Hero section and menu cards — the entry point to all features.

Hero → Menu Cards
Match

The full lifecycle of one game, the only multi-step linear flow.

Setup → Toss → Scoreboard → Result
Player

Roster management and per-player career statistics.

Players List ↔ Player Detail
History

A browsable archive of completed matches with media.

Match List ↔ Match Detail
05 The scoring engine

Every ball, accounted for

The core loop lives in ScoreBoardView and ScoreboardStateManager. Each delivery flows through validation, state snapshots and performance tracking — fully reversible via undo.

01

scoringAction(_:)

Gate the over. Scoring stops the moment max overs, a completed chase, or a chase loss has already happened.

02

Validate selections

A legal delivery requires a striker, a bowler, and usually a non-striker before runs can be recorded.

03

saveState()

Snapshot score, partnership, active player IDs, tracker arrays, extras and delivery progress — the basis for undo.

04

addRuns(_:)

Increment score and balls, update the partnership, append a delivery token, update batsman & bowler figures, handle over completion, strike changes and match-completion checks.

05

Extras & wickets

Wides, no-balls, byes/leg-byes, penalties and dismissal metadata each route through dedicated helpers.

06

undoLastAction()

Restore both scoreboard state and the nested MatchPerformanceTracker data from the saved ScoreboardGameState.

Delivery notation

·Dot ball — legal delivery, no run
11–3 — runs off the bat
4Four — boundary along the ground
6Six — over the rope
WWicket — dismissal recorded with metadata
wdExtras — wide / no-ball don't count as legal balls
05.b Deciding the result

completeMatch() reads the chase

Chase succeeds

Batting side wins

Second-innings score passes the first. Margin is reported as wickets remaining.

Chase falls short

Defending side wins

Second-innings score is lower. Margin is reported as the runs difference.

Scores level

Match tied

Equal totals resolve to a tie. The completed snapshot is written to MatchRecord.

ScoreBoardView.swift — addRuns(_:)
func addRuns(_ runs: Int) {
    score += runs
    balls += 1
    if runs == 4 { triggerCelebration(type: "FOUR") }
    if runs == 6 { triggerCelebration(type: "SIX") }
    deliveries.append("\(runs)")
    stateManager.currentPerformanceTracker.recordRuns(runs)

    if var bowler = stateManager.currentPerformanceTracker.currentBowler {
        bowler.runsConceded += runs
        bowler.balls += 1
        if bowler.balls >= 6 {              // over complete
            bowler.overs += 1; bowler.balls = 0
            speedWarningGivenForCurrentOver = false
            stateManager.currentPerformanceTracker.endOver()
            showBowlerSelection = true      // prompt next bowler
        }
        stateManager.currentPerformanceTracker.currentBowler = bowler
    }
    checkMatchCompletion()
}
06 The notation

Every token, decoded

The deliveries scroller speaks a precise visual language. Each ball becomes a colour-coded token so a scorer can verify an over at a glance — runs, extras with bonuses, warnings, penalties and wickets that carry runs.

Runs off the bat

·Dot ball — legal delivery, no run scoredlegal
1–3Runs — odd totals rotate the strikelegal
4Four — boundary, triggers a celebrationlegal
6Six — maximum, triggers a celebrationlegal

Extras

WWide — adds one run, no ball consumedno ball
W+3Wide + bonus — extra runs added to the wideno ball
NBNo-ball — adds one run, no ball consumedno ball
NB+2No-ball + bonus — bonus credited to the strikerno ball

Warnings & bonus

WarnSpeed warning — flags the current bowlerdead ball
Warn+3Warning + dead-ball runs — second-tap dialogdead ball
Bon+2Bonus runs — added without consuming a ballno ball

Penalties & wickets

P5Penalty −5 — consumes a legal balllegal
P10Penalty −10 — consumes a ball and records a wicketlegal
WkWicket — opens the dismissal-type popuplegal
Wk+4Wicket with runs — runs scored on the dismissal balllegal
06.b The rules of scoring

How the engine keeps the book

Overs & legal balls

Overs are integer division of balls by six. Wides, no-balls, warnings and bonuses don't increment the legal ball count.

Strike switching

Strike rotates on odd run totals and odd-run extras, and automatically at the end of every over alongside the bowler-selection prompt.

Speed warnings

First tap toggles a Warn flag for the bowler; a second tap opens a dialog to add dead-ball runs without consuming a ball.

Penalty behaviour

P5 docks five runs and consumes a legal ball. P10 docks ten, consumes a ball, and records a wicket.

Wicket replacement

End of over: the striker changes and the new batter goes to the non-striker's end. Mid-over: the new batter takes strike.

Required run rate

During a chase, RRR is recomputed live from runs remaining over balls left, helping the scorer read the pace of the game.

07 The interface

Six screens, iPad-first

The iPad layout is the canonical design surface — each flow uses the width as a primary-content / contextual-sidebar split. Here are the six core screens in the order a scorer meets them.

01
WelcomeHERO + MENU
A hero panel frames the app's identity — cricket ball photo, a personalised "Welcome, Rehan" and a one-line value summary — beside the six top-level menu items. Every destination sits above the fold: no hamburger, no scrolling to discover features.
02
Match SetupTEAMS · OVERS · SQUADS
Three grouped cards — Team Information, Match Settings (sliders for overs and players per team) and an opt-in Team Players picker. The sidebar mirrors choices in a live preview, killing the "did I set this right?" moment before tapping Next.
03
Coin TossANIMATED FLIP
A tappable animated coin with a result chip, then three stacked steps: who won the toss, the contextual Bat / Bowl choice, and a Ready to Start confirmation. Re-flipping is a single tap — the toss has no downstream state until Start Match.
04
Live ScoreboardTWO-COLUMN SCORING
Left: batsmen, bowler selection and a live Top Performers summary. Right: the action grid — runs, Wide / No Ball / Wicket, −5 / −10 / Bonus / End / Reset, and Undo / Warning. A persistent footer keeps the broadcast strip in view through every input.
05
Cricket Rules42 ICC STANDARD RULES
A structured browser: a search box, a category filter grid (Players, Equipment, Pitch, Innings, Match, Scoring, Bowling, Dismissal, Fielding, Conduct, Umpires) and a scrolling list of numbered rule cards that expand inline. A counter shows the active filter unambiguously.
06
SettingsSMALL BY DESIGN
A centred modal with three groups: Profile (display name), Backup (Export / Import via the Files app) and a developer credit. No account, no sync toggle, no telemetry — backups are explicit, user-triggered and human-portable.

On iPhone — the same six, reflowed

  • Single-column reflow — iPad sidebar content becomes inline cards above or below the primary content.
  • Live Stats becomes a button — it expands into a full-screen sheet rather than crowding the scoring grid.
  • Action grid stays at the bottom — within thumb reach, most-used inputs on the top row.
  • Setup uses sliders, not steppers — a faster read of the valid range in vertical space.
08 Where it all lives

Local-first, durable by design

No Core Data, SwiftData, SQLite or network store. Everything is JSON files, UserDefaults, Keychain and copied media — orchestrated by a single resilient store helper.

DurableJSONStore<Element>

Writes primary + backup JSON to both Documents and Application Support, then loads the best candidate by non-empty status, save date, item count and priority. Supports legacy UserDefaults migration and lossy decoding to skip corrupt elements.

Persisted stores
DataStore
PlayerssavedPlayers.json
legacy key SavedPlayers
MatchessavedMatches.json
legacy key savedMatches
Custom rulescustomCricketRules.json
Recovery binrecoveryBin.json
Match media…/MatchMediaLibrary/<id>/
Archived seasonsUserDefaults archivedMatchSeasonIDs
Auth profileUserDefaults auth.current.profile
Session tokenKeychain auth.session.token
Password verifiersKeychain auth.local.passwordVerifier.*

Migration, handled defensively

No schema version field — compatibility comes from decodeIfPresent defaults, backup recovery, lossy array decoding, legacy media migration and stat backfills like backfillManOfTheMatchAwards().

09 Under the hood

Apple frameworks, nothing else

No Package.swift, no CocoaPods, no Carthage, no vendored binaries. The whole app stands on the system SDK.

SwiftUIThe entire app UI.
UIKitApp delegate, orientation, image rendering, share sheets, pickers.
CombineObservableObject, @Published and nested change propagation.
FoundationCodable, dates, file I/O, JSON, UserDefaults.
PhotosUIMatch media import from the photo library.
AVKit · AVFoundationVideo viewing and thumbnail generation.
UniformTypeIdentifiersFile documents and media type detection.
SecurityKeychain storage for tokens & verifiers.
CryptoKitSHA-256 local password verifier hashing.

Concurrency

async/await drives auth, media import, share/export and splash. Task {} launches from SwiftUI actions, SessionManager is @MainActor, and DispatchQueue.main handles animation timing. No custom actors or operation queues.

Security posture

Keychain items use kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly; passwords are stored as SHA256(salt:password) verifiers, never plaintext. Auth is local-only with explicit backend integration points — better than plaintext, not production remote auth.

10 The quality bar

Built to be trusted

Scoring has to be right the first time. These are the non-functional commitments that keep the book accurate, fast and usable for everyone.

Quality assurance

Manual sequences plus Swift Testing cover every scoring path — extras, warnings, penalties, undo integrity and innings transitions.

Performance

SwiftUI diffing minimises redraws; undo serialization and stat computation run asynchronously, and the deliveries scroller lazy-loads.

Accessibility

VoiceOver announces the striker, bowler, deliveries and warnings; controls carry descriptive labels and meet contrast and dynamic-type guidelines.

Security & privacy

All data stays on-device with no network calls. Inputs are validated to prevent malformed state, and writes are guarded against corruption on suspension.

Maintainability

A modular codebase with documented methods and structured tests, so new delivery types or screens attach without disturbing existing logic.

11 Common questions

Scorer's FAQ

The edge cases scorers actually hit during a match.

+What if I close the app mid-match?

The current match state persists automatically. On relaunch the app resumes from the last recorded ball, with every player's stats intact.

+Why don't some deliveries count as legal balls?

Wides, no-balls, warnings and bonus runs are extras or special cases — they add to the score but don't increment the legal ball count, so the over isn't shortened.

+How do bonus runs affect the strike?

Bonus runs don't consume a ball and don't rotate the strike. Odd runs off the bat — and odd-run extras — do switch the striker.

+How does undo handle a wicket or penalty?

Undo restores the entire state snapshot from just before the action — score, balls, deliveries and every batsman and bowler figure — so complex actions revert cleanly.

+Can I score without picking a striker or bowler?

No. The app prompts for the required player selections before a legal delivery so stats and roles stay accurate.

+Can I move my data between devices?

Yes — Settings → Export Records writes a single backup file you can share via the Files app. On another device, Import Records merges players, matches and media into the local database.

+How is the required run rate calculated?

It divides the runs still needed by the balls remaining (converted to overs), recomputed live through the second-innings chase.

+Are haptics and celebrations on every device?

They're supported on iOS devices with the hardware for it, and degrade gracefully where that support isn't present.

12 An honest scorecard

Known limits & the road ahead

The documentation doesn't pretend the codebase is finished. A handful of very large SwiftUI files concentrate logic that would be easier to test if extracted.

ScoreBoardView.swift
6,563
PlayerDetailView.swift
4,683
MatchDetailView.swift
3,450
GlobalSearchView.swift
3,150
PlayersListView.swift
2,921

Risks & fragile areas

  • Scoring rules live in the view rather than an isolated domain service, making ball-by-ball logic harder to unit test.
  • No automated tests despite complex scoring, persistence and migration paths.
  • Persistence errors are often swallowed — partial save/export failures may be silent.
  • Auth is local-only; password verifiers use plain salted SHA-256 rather than a memory-hard scheme.
  • No schema versioning for persisted JSON; large backups can embed media blobs in memory.

Suggested improvements

  • Extract scoring into a testable ScoringEngine / reducer.
  • Split the largest views into feature subviews and view-models.
  • Add unit tests for match rules, stat derivation, duplicate detection and migration.
  • Introduce a schema / version field for persisted JSON.
  • Replace local auth with a real backend, or label accounts clearly as device-local.
  • Add CI that builds and runs tests.
13 The language of the game

Glossary, in code

How cricket's vocabulary maps onto the codebase.

Over
Six legal deliveries. Stored as balls, displayed as overs.balls.
Legal delivery
A ball that counts toward the over. Wides and no-balls do not.
Extras
Runs not credited to the batter: wides, no-balls, byes and leg-byes.
Strike / Non-striker
Which batter faces the next ball, and the partner at the other end.
Chase
The second innings' attempt to pass the first innings score.
Target
First innings score plus one.
All out
Wickets reach the configured player count.
Minus 10
A custom penalty mode — DismissalType.minusTen.
Man of the Match
A MatchAward chosen from player performances.
Recovery bin
Soft-delete store keeping items recoverable for 30 days.
Season
Date-derived grouping: Winter, Spring, Summer, Fall.
Portal
A self-contained HTML season report from AppHTMLPortalExportService.
Delivery token
A short string for a ball outcome — W, NB, ·, Wk+4.
Speed warning
A cautionary bowler flag (Warn) with optional dead-ball runs.
Bonus runs
Runs added without consuming a ball — Bon+X.
Penalty runs
Negative runs that consume a ball: P5 and P10.
Broadcast strip
The persistent landscape score banner — runs, wickets, overs.
Performance tracker
The per-innings ledger promoted to career PlayerStats at innings end.
Game-state snapshot
ScoreboardGameState — the full state captured for undo.