Where does SwiftData store things on the Mac?
On iOS, this directory is in the app's own storage location (app_UUID/Library/Application Support) but, on the Mac, it's a shared location in the user's Library.
By default on the Mac, SwiftData stores its model in the /~/Library/Application Support directory as default.store
. (It will also add two other files, default.store-shm
and default.store-wal
, as the model is stored as a SQLite database, and these are these additional files are part of how SQLite works.)
This means that, if two (or more) apps use the default location and name, they'll either overwrite the others' models, or crash because the model on disk doesn't match the model described in the app. So, it's a bad idea to use the default location/name on the Mac.
There are a few steps to manually set it all up. They're not difficult. Basically, you need to set up a ModelContainer, which contains three items:
- a
Schema
, which describes your model - optionally, a
MigrationPlan
, which describes how SwiftData can migrate data between versions (I strongly suggest you version your model and have a MigrationPlan) - a
ModelConfiguration
, which contains yourSchema
and the location to store it
let modelContainer: ModelContainer
// Set up default location in Application Support directory
let fileManager = FileManager.default
let appSupportURL = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
let directoryURL = appSupportURL.appendingPathComponent("Example")
// Set the path to the name of the store you want to set up
let fileURL = directoryURL.appendingPathComponent("Example.store")
// Create a schema for your model (**Item 1**)
let schema = Schema([Example.self])
do {
// This next line will create a new directory called Example in Application Support if one doesn't already exist, and will do nothing if one already exists, so we have a valid place to put our store
try fileManager.createDirectory (at: directoryURL, withIntermediateDirectories: true, attributes: nil)
// Create our `ModelConfiguration` (**Item 3**)
let defaultConfiguration = ModelConfiguration("Example", schema: schema, url: fileURL)
do {
// Create our `ModelContainer`
modelContainer = try ModelContainer(
for: schema,
migrationPlan: MigrationPlan.self,
configurations: defaultConfiguration
)
} catch {
fatalError("Could not initialise the container…")
}
} catch {
fatalError("Could not find/create Example folder in Application Support")
}
Typically, you create your ModelContainer
during initialisation of the app, and assign it using the .modelContainer
modifier:
@main
struct ExampleApp: App {
let modelContainer: ModelContainer // See above
var body: some Scene {
WindowGroup {
ContentView()
}
.modelContainer(modelContainer)
}
}
In the directory where the model is stored (see above), SwiftData creates a hidden directory called '.<modelName>_SUPPORT', (where <modelName> is the name of your model, e.g. '.default_SUPPORT').
Within that directory, it creates another directory called _EXTERNAL_DATA. That directory will hold one file for each item that is to be stored external to the model. Each item is named with a UUID (e.g. 0F5D57DD-CF86-4774-963F-1AAFB229E783).
In my testing, I created a model which stored images external to the model, e.g:
@Model
final class Example: Identifiable {
let id: UUID
var name: String
@Attribute(.externalStorage) var image: Data?
}
When viewed in the Finder, each file will appear with a folder icon, as they don't have any extension identifying the file type. The data I provided for the image
was TIFF data, and I found that, if I renamed a file in the _EXTERNAL_DATA directory (e.g. 0F5D57DD-CF86-4774-963F-1AAFB229E783 to 0F5D57DD-CF86-4774-963F-1AAFB229E783.tiff), it would display the image, as expected.
Don't rename these files manually or you'll break your model! I just did it for testing.