Building NextOutcome: Polymarket, Native on iOS
Polymarket lives in a browser. I built NextOutcome to put it on iOS properly — SwiftUI feeds, Swift Charts, live WebSocket order books, no web views anywhere. Watch-only today, trading on the roadmap. Three problems made it worth writing about.
Order books that never die
The connection will drop; the book must come back on its own. Each subscription is one
AsyncStream whose producer owns the whole connect/reconnect loop — when the SwiftUI view
disappears, the stream's onTermination cancels the task and the socket tears itself down.
Reconnects use exponential back-off with jitter:
/// 0.5s, 1s, 2s … capped at 30s, with ±20% jitter so
/// clients don't reconnect in lockstep.
private func backoffNanos(_ attempt: Int) -> UInt64 {
let base = min(30.0, 0.5 * pow(2.0, Double(attempt - 1)))
let jittered = base * Double.random(in: 0.8...1.2)
return UInt64(jittered * 1_000_000_000)
}The jitter is the part people skip: without it, every client reconnects at the same instant after a server blip — in waves. The ±20% spreads the herd.
A World Cup on a globe
The hub's Map tab is a draggable SceneKit globe where each nation's win odds float over the country. Input: the "World Cup winner" futures market, one outcome per nation. The surprise problem was clustering — South America stacks its contenders along one meridian and the pills pile up. Fix: a small relaxation pass that pushes overlapping pills apart with bounded drift, so Brazil's odds still hover over Brazil. The builder is a pure function (market in, pills out), so it's unit-tested without touching SceneKit.
Architecture that says no
Clean Architecture in vertical slices, ~18 SPM targets. Two rules pay rent: Presentation never imports Data (wire-format changes never touch view files), and the Trading module — order signing, wallet proxy — is never linked by the read-only app. Not a feature flag: the binary physically can't sign anything. In this space, "can't" beats "doesn't."
Code's public: github.com/sokpichdev/NextOutcome, no API keys needed. Screenshots on the landing page.