Skip to content

BarredEwe/Prefire

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

375 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

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: "5.4.0")
    .package(url: "https://github.com/pointfreeco/swift-snapshot-testing", from: "1.18.0"),
],
.testTarget(
    dependencies: [
      .product(name: "Prefire", package: "Prefire"),
      .product(name: "SnapshotTesting", package: "swift-snapshot-testing"),
    ],
    plugins: [
        .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.

About

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

Topics

Resources

License

Stars

Watchers

Forks

Sponsor this project

Contributors

โšก