The buttons weren't broken: the main thread was busy
118 green tests, a clean simulator pass, and dead buttons on a real phone. The bug was a @MainActor service doing synchronous work between awaits.
118 green tests, a clean simulator pass, and dead buttons on a real phone. The bug was a @MainActor service doing synchronous work between awaits.
@MainActor eliminates data races, not interleaving races. A generation token, a stale-failure guard, and a deterministic race test with no sleeps.
Every catch branch was dead code in tests. A 15-line failure seam fixed that, and then one unannotated test suite took down the whole runner.
Five ~700-line slices, briefs that ask what's well done too, and the verification step that separated real bugs from plausible ones.
Adopting a linter on code that never had one, a framework-free pre-commit hook, and three things the macOS runner did that the docs didn't mention.
The purchase API is the easy part. The seams between StoreKit 2, Swift 6 isolation, and SwiftUI observation are where the time went.
An inline mini-player when MusicKit playback is silent in the simulator: a two-method protocol, an honest lie of an indicator, and a bug left in on purpose.
Five tests reporting a bare "Crash": a tidy helper let the ModelContainer deallocate while the store kept its context. A context is a borrowed thing.
The region-based isolation checker tells me to file a bug, and the unfashionable unstructured Task turns out to be the correct tool.
Building the add-to-library flow and learning that MusicKit's library and catalog copies of one album are unrelated objects, as far as their IDs go.
Page 3 of 4