SwiftData on macOS: Your Default Store Location Is a Data Loss Trap
About two weeks into building Xisper, I opened the app and stared at an empty transcription list. All 207 transcription records—about two weeks of voice input data—were gone. Not corrupted. Just gone. SwiftData returned zero results.
It took an afternoon to understand what happened, and the fix was three lines of code. But the root cause is a footgun Apple never warns you about.
The default store is a shared path
When you create a SwiftData ModelContainer in the obvious way:
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(for: TranscribeRecord.self)
}
}
SwiftData creates a SQLite store at ~/Library/Application Support/default.store.
Not ~/Library/Application Support/YourAppName/default.store. Not inside a container. The filename is literally default.store at the root of Application Support.
This directory is shared. Any non-sandboxed app—and most developer tools aren't sandboxed—can read and write to ~/Library/Application Support/. If another app uses CoreData or SwiftData and also uses the default path, or stumbles across your store, it can modify or migrate it.
What actually happened
On April 15, 2026, I ran icloudmailagent, a helper tool that processes iCloud Mail. It triggered a CoreData lightweight migration on ~/Library/Application Support/default.store. CoreData saw a SQLite file it thought it owned, applied a schema transformation, and overwrote the data. All 207 TranscribeRecord rows destroyed.
No error. No warning. SwiftData silently opened the migrated store and returned an empty array. The schema still matched—the table structure was intact—but every row was gone.
The fix: app-specific directory
Three lines:
let appSupport = FileManager.default.urls(
for: .applicationSupportDirectory, in: .userDomainMask
).first!
let storeURL = appSupport.appendingPathComponent("Xisper/TranscribeRecords.store")
let config = ModelConfiguration(url: storeURL)
let container = try ModelContainer(for: TranscribeRecord.self, configurations: config)
The store now lives at ~/Library/Application Support/Xisper/TranscribeRecords.store. It's nested inside an app-specific subdirectory. No other app will find it by accident.
I added a one-time migration: if the old default.store exists at the shared path and the new store doesn't exist yet, read from the old location and write to the new one. After that, everything uses the app-specific path.
This isn't a SwiftData bug—it's a design choice
Apple's documentation doesn't call this out as a risk. The ModelContainer initializer that takes no ModelConfiguration is the one every tutorial shows. It's the shortest path to a working app, and it's fine for sandboxed iOS apps where the filesystem is isolated per app.
But macOS is not iOS. Non-sandboxed apps live in the same filesystem. If you're building a macOS app that isn't sandboxed—which is common for developer tools, utilities, and anything that needs broad filesystem access—you need to own your storage path.
How to check if you're affected
Run this in Terminal:
ls -la ~/Library/Application\ Support/default.store*
If you see default.store, default.store-shm, or default.store-wal, and you didn't explicitly put them there, something is using the shared path.
I lost two weeks of transcription data to this. A coworker pointed out that "at least it happened early"—and he was right. Better to lose 207 records during development than 2,000 records six months after shipping.
The commit that fixed this is in the Xisper repository: April 16, 2026. Three lines. Two weeks of data.