SMART LOTTO Dev Log #2 — Why I Bundled 1,000 Draws Inside the App

The First Problem to Solve
When I decided to build statistics features, the first question wasn't about UI or algorithms.
Where does the data come from, and how do I get it efficiently?
SMART LOTTO's statistics are based on the complete history of Korean Lotto 6/45 winning numbers — from draw #1 in 2002 to the present. That's approximately 1,000 draws at the time I started, now over 1,200.
The Donghaeng Lottery API
I found that Donghaeng Lottery (the official Korean lottery operator) had an endpoint that returned draw results. Not an official open API — but a URL that accepted a draw number as a parameter and returned the winning numbers as JSON:
https://www.dhlottery.co.kr/common.do?method=getLottoNumber&drwNo=1
Change drwNo and you get any draw. The question was how to use this efficiently.
"Just Call It 1,000 Times" Doesn't Work
The naive approach: call the API from draw 1 to the latest draw every time the app opens.
This fails for obvious reasons:
- Hundreds of HTTP requests on every launch
- Any network interruption breaks the sequence
- Fetching already-stored data repeatedly is wasteful
- It puts unnecessary load on Donghaeng's servers
That approach was eliminated immediately.
The Solution: Three-Stage Data Architecture
Stage 1 — Bundle initial data as JSON
I built a JSON file containing approximately 1,000 historical draw results and bundled it inside the app. On first launch, the app loads this data — no network required.
Stage 2 — Fetch only the gap on launch
On each launch, the app compares the date of the most recent stored draw to today's date. If there's a gap (i.e., new draws have occurred), it fetches only those missing draws from the API.
Stage 3 — Persist everything in Realm Swift
All fetched data is saved locally using Realm Swift. Once a draw is stored, it's never fetched again.
Recursive API Calls
The gap-filling is implemented as a recursive function — fetching draws in sequence until the latest one is reached:
func fetchMissingRounds(from round: Int, to latestRound: Int) {
guard round <= latestRound else { return }
API.fetchLotto(round: round) { result in
self.save(result)
self.fetchMissingRounds(from: round + 1, to: latestRound)
}
}
Why This Architecture Worked
- First launch is instant — most data is already bundled
- After each weekly draw, the app fetches exactly one new result
- Statistics work completely offline after the initial sync
Getting this design right early meant the statistics feature had a solid foundation. If the data layer had been broken, everything built on top of it would have been too.
Next: the App Store rejection that nobody expected.