Skip to content

Instantly share code, notes, and snippets.

@pdarcey
Last active July 24, 2024 23:04
Show Gist options
  • Save pdarcey/981b99bcc436a64df222cd8e3dd92871 to your computer and use it in GitHub Desktop.
Save pdarcey/981b99bcc436a64df222cd8e3dd92871 to your computer and use it in GitHub Desktop.
SwiftData storage on the Mac

SwiftData storage on the Mac

Where does SwiftData store things on the Mac?

Default Storage Location

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.

How to set your own location and/or name for the model

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:

  1. a Schema, which describes your model
  2. optionally, a MigrationPlan, which describes how SwiftData can migrate data between versions (I strongly suggest you version your model and have a MigrationPlan)
  3. a ModelConfiguration, which contains your Schema 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)
    }
}

Where does SwiftData store properties marked @Attribute(.externalStorage)?

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment