Skip to main content

Schema Freezing

Schema freezing is the process of creating immutable snapshots of your SwiftData schemas at specific versions. These snapshots serve as the source of truth for detecting schema drift and testing migrations.

What is Schema Freezing?

When you “freeze” a schema, FreezeRay creates a permanent record of your database structure at that moment in time. This record includes:
  • SQLite database: A real database file with the exact schema structure
  • JSON representation: Human-readable schema metadata
  • SHA256 checksum: A cryptographic hash for drift detection
  • Export metadata: Timestamp and version information
Think of it like package-lock.json for your database schema - you know exactly what structure shipped with each version.

Why Freeze Schemas?

The Problem: SwiftData’s Hidden Dangers

SwiftData uses internal hashes to match schemas. If you modify a shipped VersionedSchema:
What happens:
  1. Fresh installs work fine (no migration needed)
  2. Your simulator tests pass (fresh installs)
  3. Production users with existing databases crash on launch
SwiftData’s error messages are cryptic:

The Solution: Frozen Fixtures

FreezeRay captures the actual database from each shipped version:
These fixtures enable:
  • Drift detection: Checksum comparison catches accidental changes
  • Migration testing: Load real v1.0.0 database and test migration to v2.0.0
  • Audit trail: Git history shows exactly what shipped

How Freezing Works

1. Annotate Your Schema

Add the @FreezeSchema macro with a version string:
The @FreezeSchema(version:) string is used by FreezeRay for fixture organization. It’s separate from SwiftData’s versionIdentifier.
The macro generates:
  • __freezeray_freeze_1_0_0() - Exports schema to SQLite
  • __freezeray_check_1_0_0() - Verifies checksum matches frozen version

2. Run the Freeze Command

What happens:
  1. CLI auto-discovers your project (no config file needed)
  2. Parses source files to find @FreezeSchema(version: "1.0.0")
  3. Builds your project
  4. Runs tests in iOS Simulator
  5. Calls the macro-generated freeze function
  6. Extracts SQLite files from simulator container
  7. Generates SHA256 checksums
  8. Creates scaffolded tests for drift detection
  9. Copies fixtures to FreezeRay/Fixtures/1.0.0/
FreezeRay uses SwiftSyntax to automatically discover schemas. No manual configuration needed!

3. Commit to Git

Now your schema is permanently frozen. Any changes trigger drift detection.

Fixture Anatomy

SQLite Database (App-1_0_0.sqlite)

A real SQLite database with your schema structure:

JSON Schema (schema-1_0_0.json)

Human-readable representation:

Export Metadata (export_metadata.txt)

Freeze details:

When to Freeze

Required: Before Major Schema Changes

Freeze before releasing a new schema version to production:

Optional: During Development

Freeze interim versions for testing:

Not Required: Every Commit

Don’t freeze on every schema change - only when you want to seal a version as immutable.

Schema Version Numbering

Schema Versions ≠ App Versions

Schema versions are independent from app versions:

Common Patterns

Incremental (Simplest):
Semantic Versioning (More Descriptive):
Date-based (Time-ordered):
Choose a versioning scheme that makes sense for your team. FreezeRay doesn’t enforce any specific format.

Best Practices

Never modify frozen schemas. If you need changes, create a new version (AppSchemaV2).
Always include ALL models in each schema version. Missing models cause “unknown model version” errors.

Migration Plan Structure

Keep your migration plan synchronized with frozen schemas:

Type Aliases for Current Schema

Use type aliases to reference the current version throughout your app:
When you create V4, just update the typealias - no need to change app code.

Next Steps

Drift Detection

Learn how checksums catch accidental schema changes

Migration Testing

Test migrations between frozen versions

First Freeze Guide

Step-by-step tutorial for your first freeze

@FreezeSchema Macro

Macro reference and generated code