Prefire

πŸ”₯ A library based on Xcode Preview, for easy generation: Playbook view, Snapshot and Accessibility tests. SwiftUI and UIKit supported!

370
26
Swift

Prefire

Release Platform Swift6 Swift Package Manager Swift Package Manager

πŸ”₯ What is Prefire?

Prefire transforms your #Preview blocks into:

  • βœ… Snapshot tests
  • βœ… Playbook views
  • βœ… Visual flows with states and user stories
  • βœ… Living documentation β€” fully automated

πŸš€ Key Features

Playbook
  • 🧠 Smart Preview Parsing β€” including #Preview, @Previewable
  • πŸ“Έ Snapshot Testing β€” automatic test generation from previews
  • πŸ“š Playbook View β€” auto-generated interactive component catalog
  • πŸƒ Flow-aware β€” build user stories from multiple preview steps
  • 🧩 UIKit Support β€” support for UIView and UIViewController
  • βš™οΈ SPM + Xcode Plugins β€” works in CLI, Xcode build phases, or CI
  • 🧠 Fast Caching β€” fingerprint-based AST and body caching avoids redundant work
  • ✍️ Stencil Templates β€” customize output with your own templates

Why Prefire?

  • πŸ”₯ Save Time - Generate tests and documentation automatically
  • πŸ”₯ Stay Consistent - Keep previews and tests always in sync
  • πŸ”₯ Improve Quality - Catch visual regressions before users do
  • πŸ”₯ Boost Collaboration - Share living documentation with your team


⚑️ Quick Start

πŸ“¦ Example project available at: Prefire Example

1. Add Prefire to Your Project

// Package.swift
dependencies: [
    .package(url: "https://github.com/BarredEwe/Prefire.git", from: "4.0.0")
],
.testTarget(
    plugins: [
        // For Snapshot Tests
        .plugin(name: "PrefireTestsPlugin", package: "Prefire")
    ]
)

2. Write #Preview

#Preview {
    Button("Submit")
}

3. Run tests

Just run the test target πŸš€ β€” Prefire will auto-generate snapshots based on your previews.

πŸ’‘ If your test target is empty, Prefire will still generate files and snapshot code during build.


πŸ“¦ Installation

Supports:

  • βœ… SPM Plugin (Package.swift)
  • βœ… Xcode Build Tool Plugin
  • βœ… CLI (brew install prefire)
  • βœ… GitHub Actions / CI

See detailed setup in the Installation guide

🧠 How It Works

πŸ” 1. Parses all source files

  • Finds all #Preview and PreviewProvider blocks
  • Supports modifiers: .prefireEnabled(), .prefireIgnored()

πŸ“‚ 2. Caches Types and PreviewBodies

  • Based on file modification date + SHA-256 of inputs
  • Avoids re-parsing if nothing changed

πŸ”’ 3. Generates Snapshot Tests

  • Uses Stencil templates
  • Respects .prefire.yml configuration

πŸ“˜ 4. Generates Playbook View

  • Groups by UserStory, State
  • Outputs PreviewModels.generated.swift

πŸ›  Advanced Usage

To generate tests and playbook, simply mark your preview using the PrefireProvider protocol:

struct Text_Previews: PreviewProvider, PrefireProvider {
    static var previews: some View { ... }
}

If you use the #Preview macro, πŸ”₯Prefire will automatically find it!

If you don’t need it, mark view - .prefireIgnored():

#Preview {
    Text("")
        .prefireIgnored()
}

If you want to disable the automatic get of all previews, use the setting preview_default_enabled: false. Then to include preview in the test, you need to call the .prefireEnabled():

#Preview {
    Text("")
        .prefireEnabled()
}

Playbook (Demo) View

To use Playbook, simply use PlaybookView

  • If you want to see a list of all the Views, use isComponent: true
  • If you want to sort by UserStory, use isComponent: false
import Prefire 

struct ContentView: View {
    var body: some View {
        PlaybookView(isComponent: true, previewModels: PreviewModels.models)
    }
}

Snapshot tests

Just run generated tests πŸš€
All tests will be generated in the DerivedData folder.

Plugin PrefireTestsPlugin will handle everything for you πŸ› οΈ

For detailed instruction, check out swift-snapshot-testing or examine an example project.


API

Prefire provide new commands for previews:

  • You can set the delay, precision and perceptualPrecision parameters for the snapshot:

    .snapshot(delay: 0.3, precision: 0.95, perceptualPrecision: 0.98)
    
    static var previews: some View {
        TestView()
            .snapshot(delay: 0.3, precision: 0.95, perceptualPrecision: 0.98)
    }
    
  • Function for connecting preview together in one Flow:

    .previewUserStory(.auth)
    
    static var previews: some View {
        PrefireView()
            .previewUserStory(.auth)
    }
    
    static var previews: some View {
        AuthView()
            .previewUserStory(.auth)
    }
    

    For example Authorization flow: LoginView, OTPView and PincodeView


  • If a preview contains more than one View, you can mark State for these views.

    .previewState(.loading)
    
    static var previews: some View {
        TestView("Default")
    
        TestView("Loading")
            .previewState(.loading)
    }
    


🧰 API Summary

Feature Modifier
Include in snapshot .prefireEnabled()
Exclude from snapshot .prefireIgnored()
Group in a flow .previewUserStory(.auth)
Mark a UI state .previewState(.error)
Customize snapshot .snapshot(delay: 0.3, precision: 0.95)

πŸ’‘ Advanced: CLI Usage

# Generate snapshot tests
prefire tests

# Generate playbook models
prefire playbook

Run prefire tests --help or prefire playbook --help for more options.


πŸ—‚ Configuration: .prefire.yml

See detailed configuration in the Configuration guide

test_configuration:
  target: MyApp

playbook_configuration:
  preview_default_enabled: true

Distribution

When preparing for distribution, you may want to exclude your PreviewProvider and mock data from release builds. This can be achieved by wrapping them in #if DEBUG compiler directives. Alternatively, you can pass a compiler flag to exclude PreviewModels from release builds.

To exclude PreviewModels using Swift Package Manager, pass the PLAYBOOK_DISABLED swift setting in the package that links PrefirePlaybookPlugin:

swiftSettings: [
    .define("PLAYBOOK_DISABLED", .when(configuration: .release)),
]

If you are using Xcode, you can pass the compiler flag in the Xcode build settings:

SWIFT_ACTIVE_COMPILATION_CONDITIONS = PLAYBOOK_DISABLED;

🧠 Internal Architecture

  • PrefireCore β€” AST + preview parsing, caching, logic
  • PrefireGenerator β€” handles stencil templating + snapshot generation
  • PrefireCacheManager β€” unifies caching for Types and Previews
  • PrefireTestsPlugin / PrefirePlaybookPlugin β€” SPM/Xcode integrations
  • prefire β€” CLI entry point, calls shared generator code

Requirements

  • Swift 5.6 or higher
  • Xcode 14.0 or higher
  • iOS 14 or higher

Troubleshooting

NavigationView in Preview not supported for Playbook

  • Consider using other views or layouts for your Playbook needs.

Running Prefire via CI

  • To run Prefire via Continuous Integration (CI), you need to configure permissions:
    defaults write com.apple.dt.Xcode IDESkipPackagePluginFingerprintValidatation -bool YES

Xcode is unable to generate tests in a custom path.

  • To resolve this, you’ll need to disable the sandbox for file generation by running the following command in your terminal:
    defaults write com.apple.dt.Xcode IDEPackageSupportDisablePluginExecutionSandbox -bool YES

🀝 Contributing

We welcome contributions! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch
  3. Submit a Pull Request

πŸ“„ License

Prefire is released under the Apache License 2.0. See LICENSE for details.