Engineering case study · Sleep Map
A promisethe codehas to keep.
Keep the night and a real place becomes yours. Miss it and nothing is taken back. How that promise holds across 24,062 places, every time zone and thirty builds.
1,190 cells3,479 neighbour edges
A bad night slows progress but never destroys already-claimed territory.
The one rule that outranks every feature. What follows is that sentence, enforced.
The shape of it
Everything a night needs is on the phone.
One SwiftUI app with a SwiftData store that never syncs, and a Python pipeline that builds the world it ships with. The night is judged on the device. Pictures and signed words come down; only claimed ground goes out, to friends who asked for it.
A world of cells
You name cells. You don’t build them.
region_places.jsonthe namescells_land.binwhat the map paintscells_polygonsthe shapes: hit-test, hometowncells.jsonthe graph: neighbours, centroids
01 / 05
Population cells ship jagged.
Raw cells came in as shards: perimeter²/4π·area ran as high as 201.
02 / 05
Every cell takes a real town.
Each is named for a town anchor, and the anchor becomes its seed.
03 / 05
Voronoi, at the anchors.
repartition.py grows each cell from its seed. Spikiness falls to about 1.4.
04 / 05
Four files, four jobs.
Graph, shapes, painted land, names: four layers, one key.
05 / 05
A hometown has a limit.
Point Nemo once resolved to Hanga Roa, 2,700 km away. The fallback now stops near 500 km.
The coast, not the world
Draw the coast. Don’t move the world.
Until 1.12 the map painted whole cells, so coasts bulged into the sea. Clipping the cells would have changed the graph, the IDs and everyone’s ground. So only the drawing changed: coast_clip.py bakes each cell’s land and shoreline into a file the map paints, and a tap still lands on the original polygon.
Taiwan · 22 places held, grown from Zhongzheng
- Shoreline is subtractiona cell’s land edge minus every other cell’s land
- One coveragesimplified as one world, so neighbours keep identical borders
- Tap the sea, get the islandhit-testing stays on the polygon; a speck island gets a 6 pt dot
- ~16 MB, baked offline
cells_land.bin; a cell missing from it keeps its ring
Fast enough to open
61 MB of text, on every cold launch.
The cell shapes ship as a packed binary now, built by a build phase, with a test that keeps the JSON out of the bundle. Here is the load at real speed, as measured on the Debug simulator.
cells_polygons.json61 MB of text · JSONDecoder
2,370 ms
cells_polygons.binSMPG v1 · Int32 × 1e7
1,051 msready
- SMPG
- v1
- count
- id length
- cell ID
- points
- Int32 × 2 per point
- …
Ground that can’t be lost
Every shipped store, opened again.
Claimed ground is append-only, and the store on the phone is the only place it lives. Every schema change leaves a fixture: a real store from the last build that shipped the old shape. A store that still won’t open is renamed aside, never deleted, and refilled from a shadow copy.
StoreUpgradeTests opens every shipped build’s real store under today’s schema
19 / 19 open as .opened
- ✓1.1.1b5
- ✓1.2.0b6
- ✓1.3.0b7
- ✓1.4.0b8
- ✓1.4.0b9
- ✓1.4.1b10
- ✓1.5.0b11
- ✓1.6.0b12
- ✓1.7.0b13
- ✓1.7.1b14
- ✓1.7.2b15
- ✓1.7.3b16
- ✓1.8.0b17
- ✓1.8.1b18
- ✓1.9.0b20
- ✓1.9.1b21
- ✓1.10.0b22
- ✓1.10.1b23
- ✓1.11.0b24
- 1.12.0V3 · today
.recoveredFromFailedStore
let movedTo = try moveAside(configuration.url)note("moved the failed store to \(movedTo)")let container = try make(configuration)if let shadow { try shadow.restore(into: container.mainContext) }return (container, .recoveredFromFailedStore(movedTo: movedTo))
A night is absolute dates
Judge a night by the rules it started under.
At Good night, clock minutes and the tap become absolute deadlines, once. Grace is baked in, so a setting changed at 3 AM can’t move the goalposts, and the verdict is a pure function, tested as a table. Try it.
The clock you read it on
Grace when you tapped Good night
Kept. The place is claimed.Nothing about the clock you read it on changes that.
if tap > schedule.bedtimeDeadline {→ .sleptLateif wake < schedule.wakeOpen {→ .wokeEarly / .brokeMidNightif wake > schedule.wakeClose {→ .oversleptreturn NightVerdict(succeeded: true, failReason: nil, actualWake: wake)
Time zones
A night belongs to where you slept it.
Each night stores the zone it began in, and every surface dates it as lived. The first night after a jump of three hours or more is judged but can never be a miss. Before 1.12, a one-hour trip west could reset a streak on a good night.
- Thu, Nov 5Calendar.current
- Fri, Nov 6last night, as lived ✓
- Sat, Nov 7
Read through Calendar.current in Los Angeles, the night lands a day early: a gap, and a good night’s streak could reset. 1.12 files it under the day you slept it.
What crosses the wire
Signed words in. Only ground out.
Stories, names and photos can update without App Review, as a signed revision built for one exact bundle. Nothing else can ride along. A Circle publishes claimed places only, so a missed night is simply silence.
In · signed words
- signed: checked against the bundled public key
- its baseline ID is this exact bundle’s
- applied at the next launch
Never · whatever the signature
Code, map topology, sleep rules and saved progress change only with an App Store build.
Out · to a Circle
- sleep times
- health data
- missed nights
static func mayPublish(tainted: Bool, succeeded: Bool) -> Bool {
!tainted && succeeded
}Tests and a headless release
Taps can’t be scripted, so nothing needs them.
Every screen opens from a debug launch argument, so tests and screenshots drive the app without a tap. A preflight audits the Release build, and one script takes it from a green tree to App Review.
- iOS tests
- 1,007
- pipeline tests
- 908
- story voice gate
- exit 0
XCTest, one simulator, never parallel
pytest, over the code that builds the world
voice_report.py, before any story ships
preflight.sh · against the Release build
- ✓OTA baseline matches the bundle
- ✓the globe’s texture is absent
- ✓privacy manifest at the bundle root
- ✓zero DEBUG symbols in the binary
- ✓manifest counts for the listing
- ✓the iOS suite is green
- ✓a store fixture for the last shipped build
- ✓every ModelConfiguration says .none
- ✓the string catalog covers the build
asc.py · App Store Connect, headless
- status
- create-version
- what’s new × 9
- review-notes
- attach-build 30
- submit
- Waiting for review1.12.0 · build 30
One lesson the runbook keeps: cancelling a submission does not spend its build.
What I take from it
Make the promise something a test can break.
- Keep the truth apart from the drawing.The coastline changed what the map paints, and not one claimed place.
- Freeze a night when it starts.Absolute dates at the tap, so settings, updates and zones can’t reach back into it.
- Every shipped store is a fixture.A promise about someone’s ground holds only if today’s build opens what every earlier one wrote.
Keep the night. Keep the place.
Set tonight’s hours and see which real place is waiting next door to home.
