Skip to content

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

License

Notifications You must be signed in to change notification settings

BarredEwe/Prefire

Repository files navigation

Prefire

A library for easily generating automatic Playbook (Demo) view, Tests using Preview

Works with: UI-components, screens and flows

Release Platform Swift6 Swift Package Manager Swift Package Manager

Prefire

Do you like SwiftUI Preview and use it? Then you must try πŸ”₯Prefire!

You can try πŸ”₯Prefire starting from example project.

πŸš€ Features

Playbook

  • βœ… Zero-Boilerplate Playbook - Automatically generate interactive component catalogs
  • βœ… Snapshot Testing - Catch UI regressions with automatic test generation
  • βœ… Smart Previews - Enhance SwiftUI previews with states and user stories
  • βœ… CI-Ready - Seamless integration with GitHub Actions and other CI systems
  • βœ… Xcode & CLI - Choose your workflow: IDE integration or command line

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

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 generated tests πŸš€ All tests will be generated in the DerivedData folder.


πŸ“¦ Installation

Prefire can be installed for an Xcode Project or only for one Package or CLI tool.

Xcode Project Plugin

You can integrate Prefire as an Xcode Build Tool Plug-in if you're working on a project in Xcode.

  1. Add Prefire as a package dependency to your project without linking any of the products.
  1. Select the target to which you want to add linting and open the Build Phases inspector. Open Run Build Tool Plug-ins and select the + button. From the list, select PrefirePlaybookPlugin or PrefireTestsPlugin, and add it to the project.

Swift Package Plugin

You can integrate Prefire as a Swift Package Manager Plug-in if you're working with a Swift Package with a Package.swift manifest.

  1. Add Prefire as a package dependency to your Package.swift file.
dependencies: [
    .package(url: "https://github.com/BarredEwe/Prefire", from: "4.0.0")
]
  1. Add Prefire to a target using the plugins parameter.
.target(
    plugins: [
        // For Playbook (Demo) view
        .plugin(name: "PrefirePlaybookPlugin", package: "Prefire")
    ]
),
.testTarget(
    plugins: [
        // For Snapshot Tests
        .plugin(name: "PrefireTestsPlugin", package: "Prefire")
    ]
)

Command line tool (CLI)

Prefire provides a command-line interface for generating snapshot tests from your previews.

Installation

Download Pre-built Binary

curl -L https://github.com/BarredEwe/Prefire/raw/refs/heads/main/Binaries/PrefireBinary.artifactbundle/prefire-3.6.0-macos/bin/prefire -o prefire

Make executable and move to bin

chmod +x prefire;
sudo mv prefire /usr/local/bin/

Basic Commands

Generate Tests

Generate snapshot tests from your preview providers:

prefire tests --template /SomeTemplate.stencil

Tip: Use prefire tests --help for complete documentation

Generate Playbook

Generate a playbook file documenting all your previews:

prefire playbook

Tip: Use prefire playbook --help for complete documentation


πŸ›  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)
    }


βš™ Configuration

To further customize Prefire, you can create a .prefire.yml file in the root directory of your project. Here's an example of its content:

test_configuration:
  - target: PrefireExample 
  - test_file_path: PrefireExampleTests/PreviewTests.generated.swift
  - template_file_path: CustomPreviewTests.stencil
  - simulator_device: "iPhone15,2"
  - required_os: 16
  - preview_default_enabled: true
  - sources:
    - ${PROJECT_DIR}/Sources/
  - snapshot_devices:
  	- iPhone 14
  	- iPad
  - imports:
    - UIKit
    - SwiftUI
  - testable_imports:
    - Prefire

playbook_configuration:
  - preview_default_enabled: true
  - template_file_path: CustomModels.stencil
  - imports:
    - UIKit
    - Foundation
  - testable_imports:
    - SwiftUI

Configuration keys and their descriptions

  • target - Your project Target for Snapshot tests. Default: FirstTarget
  • test_file_path - Filepath to generated file. Default: DerivedData
  • template_file_path - Stencil file for generated file. Optional parameter.
    For test plugin Default: Templates/PreviewTests.stencil from the package.
    For playbook plugin Default: Templates/PreviewModels.stencil from the package
  • simulator_device - Device for Snapshot testing. Optional parameter.
  • required_os - iOS version for Snapshot testing. Optional parameter.
  • snapshot_devices - the list of devices snapshots should be generated for. The simulator_device specified above will still be required and used, but snapshotting will take on the traits of the snapshot_devices. The displayScale will default to 2.0 and device specific safe areas will be .zero. Optional parameter.
  • preview_default_enabled - Do I need to automatically add all previews based on the new syntax to the tests. Default: true
  • imports - Additional imports for the generated Playbook/Tests. Optional parameter.
  • testable_imports - Additional @testable imports for the generated Playbook/Tests. Optional parameter.
  • sources - Paths to swift file or directory sources. Default: File paths of a specific target or project

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;

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 MIT License. See LICENSE for details.