Skip to main content

freezeray freeze

Freeze a schema version by generating immutable fixture artifacts from the actual SwiftData schema running in iOS Simulator.

Synopsis

Description

The freeze command captures an immutable snapshot of your SwiftData schema at a specific version. It:
  1. Auto-detects your Xcode project and scheme
  2. Discovers @FreezeSchema(version: "X.X.X") annotations in source files
  3. Builds and runs your project in iOS Simulator
  4. Extracts fixtures from the simulator container to FreezeRay/Fixtures/
  5. Scaffolds drift test (if it doesn’t already exist)
  6. Updates Xcode project to include test files and fixture resources
This process ensures fixtures are based on the actual database schema that SwiftData creates, not a theoretical representation.
Migration tests are NOT generated by freeze. Use freezeray generate migration-tests when starting work on a new schema version.

Arguments

<VERSION>

Required. The version string to freeze (e.g., “1.0.0”, “2.0.0”). Must match a @FreezeSchema(version: "X.X.X") annotation in your source code:

Options

--simulator <NAME>

Default: iPhone 17 Specify which iOS Simulator to use for freezing:
Always use a consistent simulator across your team to avoid subtle schema differences.

--scheme <NAME>

Default: Auto-detected Specify the Xcode scheme to build:
If not specified, FreezeRay attempts to auto-detect the scheme from your Xcode project.

--force

Dangerous! Overwrite existing frozen fixtures for the specified version.
Only use --force during development before shipping v1.0.0 to production. Overwriting frozen fixtures breaks the immutability guarantee.
When to use:
  • You’re iterating on an unreleased schema version
  • Fixtures were corrupted and need regeneration
Never use if:
  • The version has been shipped to production users
  • Other developers depend on the frozen fixtures

--output <PATH>

Default: FreezeRay/Fixtures/<VERSION>/ Override the output directory for fixtures:
Custom output paths are useful for monorepos or non-standard project structures.

--config <PATH>

Experimental. Specify path to .freezeray.yml config file:
Currently unused but reserved for future configuration features.

What Gets Created

After running freezeray freeze 1.0.0, you’ll find:

Fixtures: FreezeRay/Fixtures/1.0.0/

Tests: FreezeRay/Tests/

Drift test files are scaffolded once and then owned by you. Customize them with assertions as needed.Migration tests are NOT created by freeze. Use freezeray generate migration-tests when starting work on a new schema version.

Examples

Basic usage

Output:

Complete workflow with migration

Output of freeze 2.0.0:
Note: freeze does NOT generate migration tests. You created MigrateV1_0_0toV2_0_0_Tests.swift earlier with the generate command.

Custom simulator

Uses iPhone 15 Pro instead of the default iPhone 17.

Force overwrite (development only)

Output:

Requirements

  • macOS 14+
  • Xcode 15+
  • iOS Simulator (iPhone 17 recommended)
  • Project structure:
    • Must have @FreezeSchema(version: "X.X.X") annotation in source code
    • Must have a test target (for running freeze tests)
    • Xcode project or Swift Package with iOS target

Common Errors

”No @Freeze(version: “X.X.X”) annotation found”

Cause: The specified version doesn’t match any @FreezeSchema annotation in your source files. Fix:
Then run:

“Fixtures for vX.X.X already exist”

Cause: You’ve already frozen this version. Solution: If you haven’t shipped to production yet:
If you have shipped to production:

“Build failed” or “Tests failed”

Cause: Your project doesn’t build or tests are failing. Fix:
  1. Ensure your project builds in Xcode: ⌘B
  2. Ensure your tests pass in Xcode: ⌘U
  3. Check the Xcode console for specific errors
Common issues:
  • Missing dependencies
  • Syntax errors in schema
  • Test target not configured correctly

”Simulator not found: iPhone 17”

Cause: The specified simulator doesn’t exist. Fix: List available simulators:
Use an existing simulator:

“Could not update Xcode project”

Cause: Xcode project manipulation failed (rare). Solution: Manually add files to Xcode:
  1. Open Xcode
  2. Drag FreezeRay/Tests/ to your test target
  3. Add FreezeRay/ folder to test target’s Copy Bundle Resources:
    • Select test target
    • Build Phases → Copy Bundle Resources → + → Add FreezeRay/ folder

How It Works

Step 1: Project Discovery

FreezeRay auto-detects:
  • Xcode project (*.xcodeproj) or Swift Package (Package.swift)
  • Scheme (first available scheme or user-specified)
  • Test target (inferred from scheme name)

Step 2: Source Parsing

Uses SwiftSyntax to parse all .swift files and find:
  • @FreezeSchema(version: "X.X.X") annotations
  • SchemaMigrationPlan types
No configuration file needed!

Step 3: Test Generation

Creates a temporary test file:
This test calls the macro-generated freeze function.

Step 4: Simulator Execution

Runs:
The test function:
  1. Creates a ModelContainer with your schema
  2. Generates SQLite database in simulator’s Documents directory
  3. Exports fixtures to /tmp/FreezeRay/Fixtures/1.0.0/

Step 5: Fixture Extraction

CLI copies fixtures from /tmp/FreezeRay/Fixtures/1.0.0/ to your project’s FreezeRay/Fixtures/1.0.0/.
iOS Simulator can’t write directly to your source tree, so fixtures go through /tmp as an intermediary.

Step 6: Test Scaffolding

Generates permanent test files: Drift test:
Migration test (if previous version exists):

Step 7: Xcode Project Update

For Xcode projects (not Swift Packages):
  • Adds test files to test target’s sources
  • Adds FreezeRay/ folder to test target’s resources

Best Practices

Never freeze on a dirty working tree. Commit all changes before freezing so the fixtures are traceable to a specific commit.

Commit Fixtures Immediately

This ensures:
  • Fixtures are in version control
  • CI can run drift/migration tests
  • Team members get the fixtures

Use Semantic Versioning

Semantic versioning helps communicate the impact of schema changes.

Freeze Before Releasing

Freeze your schema before submitting to App Store:
This ensures production users get the exact schema you tested.

Run Tests After Freezing

Drift tests should pass immediately after freezing. If they fail, something went wrong during the freeze.

Next Steps

Drift Detection

Understand how frozen fixtures detect schema changes

Migration Testing

Learn to test migrations between frozen versions

First Freeze Guide

Step-by-step tutorial for freezing your first schema

@FreezeSchema Macro

See what code the macro generates