One Way to Solve Inexplicable Race Conditions in Tests
Today I wrote my first asynchronous test in my latest project and suddenly, during the waitForExpectations
run loop, a totally unrelated test's stuff crashed. To make things worse, it was part of an RxSwift subscription. No idea who kept that alive and where the EXC_BAD_ACCESS
really originated.
Jiggle GCD Queues to Find Problems
To debug my threading issues and help bring forth future problems, I have created a simmple object that slows the current queue down:
#!swift
let IsRunningTests = NSClassFromString("XCTestCase") != nil
struct QueueJigglePoint {
/// Randomly interfere with the thread.
static func jiggle() {
guard !IsRunningTests else { return }
#if DEBUG
usleep(2*1000000) // 2 seconds
#endif
}
}
I got this idea from Brett Schuchert on pages 188–90 of Uncle Bob's Clean Code. A Handbook of Agile Software Craftsmanship. There, interference with traditional threading should randomly sleep, yield, or fall through. Enqueued blocks are a lot less volatile, so I only came up with sleeping.
Randomizing the sleep interval is up next. But a fixed number of 2–10 seconds helps find UI-blocking code already.
Just throw in a QueueJigglePoint.jiggle()
in NSManagedObjectContext.performBlock
executions, when dispatching async to the background, or when reading files, for example.
Ideas to Solve Background Fetching and Saving Race Conditions with Core Data
I ran into problems with my Core Data Unit of Work/Transaction implementation the other day. I was not exercising good NSManagedObjectContext
hygiene and end up with conflicts from time to time. Race conditions. Hate 'em. The problem is that the parent–child setup of Core Data managed object contexts seems to work just fine for so-called "scratch pad" contexts: create a new child context, perform changes on in, then discard, or save to pass changes to the parent context.
Return Types can Capture Async Processes and Failures

I've written about using the Result enum to return values or errors already. Result
guards against errors in the process. There's also a way to guard against latency, often called "Futures". There exist well-crafted solutions in Swift already.