Dev

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

2026-01-17·3min read
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.