# Engine gives SwiftUI components a style system without AnyView

> Engine is a Swift framework for building SwiftUI libraries and apps, built around a ViewStyle protocol and a StyledView macro so any view can take a customisable appearance. The design point is that it avoids AnyView, which is what makes it usable in a component library, and the cost is a macro dependency that needs a flag in CI.

**nathantannar4/Engine** — Engine makes it easier to create idiomatic APIs and Views that feel natural in SwiftUI without sacrificing performance

- Repository: https://github.com/nathantannar4/Engine
- Stars: 578 · Forks: 36
- Language: Swift
- License: BSD-2-Clause
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/nathantannar4-engine

## A style system for components, without the AnyView tax

Engine is described as a performance driven framework for developing SwiftUI frameworks and apps, and the README's summary is that it makes it easier to create idiomatic APIs and Views that feel natural in SwiftUI without sacrificing performance.

The reason to build something like this is visible in SwiftUI itself. A design system for a reusable component, where a label can be laid out horizontally in one style and vertically in another, is normally written with a `ViewModifier` or with a configuration closure, and the modifier route is the one that erases type information and pushes the view through `AnyView`. The README says this directly: unlike some other styling solutions you may have come across, `ViewStyle` works without relying on `AnyView` so it is very performant.

That claim is the whole design argument. The type of the style and the type of the body are associated types on a protocol, so the compiler keeps the concrete view type, and the framework can hand a different body back without boxing it.

The project is Swift, licensed under BSD 2-Clause, with a `Package.swift` at the root, `Sources/` for the code, an `Example/` project and a `.spi.yml` for Swift Package Index publishing. The releases are 3.0.0 from 2026-10-16, 2.18.0 from 2026-09-17 and 2.17.0 from 2026-09-12, and the last push was on 2026-10-16, so the version line is moving quickly.

## Installing it in Xcode and in Package.swift

For an Xcode project the install is a menu path. Select `File` -> `Swift Packages` -> `Add Package Dependency` and enter `https://github.com/nathantannar4/Engine`.

For a Swift Package Manager project, Engine is a dependency in `Package.swift`:

```swift
let package = Package(
    //...
    dependencies: [
        .package(url: "https://github.com/nathantannar4/Engine"),
    ],
    targets: [
        .target(
            name: "YourPackageTarget",
            dependencies: [
                .product(name: "Engine", package: "Engine"),
                .product(name: "EngineMacros", package: "Engine"), // Optional
            ],
            //...
        ),
        //...
    ],
    //...
)
```

The `EngineMacros` product is marked optional in the example, which is the detail that matters. The macro implementation lives in a separate product, so a project that only wants the protocols does not have to take the macro dependency.

The requirements are iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0 or visionOS 1.0 as deployment targets, and Xcode 16.4 or newer. The documentation lives in the Swift Package Index, and the README suggests building and running the included Example project as the fastest way to get sample code in front of you.

## The macro is what CI has to know about

There is one operational detail that will stop a build if you do not know it in advance. `EngineMacros` includes a Swift macro, which requires user validation to enable or the build will fail, and the README says that when configuring your CI you should pass the flag `-skipMacroValidation` to `xcodebuild` to fix it.

The reason is that Swift macros are code that runs at compile time, so the compiler asks for explicit approval before expanding them, and a headless build has nobody to click the button. The flag suppresses that prompt for the build.

The README also notes a consequence for the developer experience: Xcode's syntax highlighting currently does not work for types generated by a macro. So a project that leans on the macro will have types the editor cannot colour, and the documentation has to carry more of the explanatory weight than it otherwise would.

That is the cost side of the same decision that buys the type safety. The macro generates the boilerplate that connects a view to its style, and the price is a build flag in CI and a gap in editor support.

## The StyledView macro turns a view into a styleable view

The macro side of the framework is small. The public surface is:

```swift
public macro StyledView()

/// A protocol intended to be used with the ``@StyledView`` macro define a
/// ``ViewStyle`` and all it's related components.
public protocol StyledView: View { }
```

With the `@StyledView` macro, nearly any `View` can be transformed into one that has `ViewStyle` style support, and you simply attach the macro to any `StyledView`.

The example that follows is the part worth reading, because it shows what the macro actually saves you. A `LabeledView` is declared as a `StyledView` with a label and a content, its body is an `HStack` of the two, and then a `LabelViewStyle` protocol, a vertical style and a bordered style are written against the same configuration. Every one of those styles would otherwise have been hand written against the concrete view type, and the macro is what allows them to share one configuration shape.

The mechanism is the usual one for a SwiftUI style system. A configuration type carries the data a style needs, the style protocol has a `makeBody` that takes the configuration and returns a view, and the component declares which style is its default. The `StyledView` macro is the piece that lets an arbitrary existing view adopt that shape without writing the associated types by hand.

## ViewStyle is the protocol, and it is implemented manually

The manual path is documented next, and the README says it is necessary for some optional features of a `ViewStyle`. A `ViewStyledView` can have a `body` which is necessary to implement if you want the styled view to have a root implementation that every style would be applied on, such as a `ViewModifier` that is always added.

The protocols are short:

```swift
public protocol ViewStyle {
    associatedtype Configuration
    associatedtype Body: View

    @ViewBuilder
    func makeBody(configuration: Configuration) -> Body
}

public protocol ViewStyledView: View {
    associatedtype Configuration
    var configuration: Configuration { get }

    associatedtype DefaultStyle: ViewStyle where DefaultStyle.Configuration == Configuration
    static var defaultStyle: DefaultStyle { get }
}
```

So a style has a configuration type and a body type, and a styled view has a configuration, a default style, and the constraint that the default style's configuration matches the view's configuration. The `@ViewBuilder` on `makeBody` is what lets a style return several views without writing a `VStack` by hand.

The `ViewStyle` route is what a framework author reaches for when a component needs a configurable appearance. The README's example is a `StepperView` that defaults to `Stepper` but accepts a different custom style, and the styles share common behaviour such as font and colours while allowing complete customisation of the appearance and layout. The step by step construction is instructive: define the style protocol, define the configuration, define the default style, define your custom styles, add an extension on `View` to set the style, and then define the component.

## Where the framework stops

The limits are visible in the documentation, and they are reasonable ones.

The first is that this is a styling layer, not a component library. Engine gives you the pattern and the protocols; the components are still yours to write. The `StepperView` in the README is an example in the documentation, not something the package ships.

The second is the macro dependency. If your project takes `EngineMacros`, it takes a compile-time code generation dependency with a validation step, a CI flag and a loss of syntax highlighting for the generated types. A project that only needs the `ViewStyle` protocols can take the `Engine` product alone and write the boilerplate itself, which the README says is the manual path.

The third is that the framework has a house style. `ViewStyle` is a particular shape for expressing configurable appearance, and a team whose components already have their own configuration system has no reason to adopt it.

The alternative approach, for most SwiftUI work, is to use a plain `ViewModifier` and accept the `AnyView` cost, or to use generics with a configuration closure, which keeps the type but makes every call site carry the configuration. The difference in approach is where the type erasure lands. Engine keeps the type on both sides, at the price of a protocol-shaped style system and a macro.

## Conclusion

Adopt Engine when you are building a SwiftUI component library or an app that needs themable components, and when the AnyView tax of the usual approach is what is slowing you down. Do not adopt it for a small app that needs one custom control, and do not adopt it if you cannot add the macro validation flag to your CI. Verify first that your deployment targets are covered, iOS 13, macOS 10.15, tvOS 13, watchOS 6 or visionOS 1, that you are on Xcode 16.4 or newer, that your build passes `-skipMacroValidation` to xcodebuild, and that the styling model you want, a protocol with a configuration, fits the components you already have.

## FAQ

### What does the Engine framework do for SwiftUI?

Engine is a performance driven framework for developing SwiftUI frameworks and apps. It adds ViewStyle support so nearly any View can be given a customisable appearance through the @StyledView macro, and the README stresses that ViewStyle works without relying on AnyView.

### How do I add Engine to a Swift package?

Add `.package(url: "https://github.com/nathantannar4/Engine")` to the dependencies of your Package.swift and depend on the `Engine` product, with `EngineMacros` listed as optional. In Xcode you use File, Swift Packages, Add Package Dependency and enter the repository URL.

### Why does my CI build fail with Engine?

EngineMacros includes a Swift macro, and macros require user validation to enable, so the build fails without it. The README says to pass the flag `-skipMacroValidation` to xcodebuild when configuring your CI.

### What are the platform requirements for Engine?

The deployment targets are iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0 or visionOS 1.0, and the README requires Xcode 16.4 or newer. The Example project is included so you can build and run sample code.

## Sources

- [Issues](https://github.com/nathantannar4/Engine/issues)
- [License: BSD-2-Clause](https://github.com/nathantannar4/Engine/blob/main/LICENSE)
- [nathantannar4/Engine on GitHub](https://github.com/nathantannar4/Engine)
- [README](https://github.com/nathantannar4/Engine/blob/main/README.md)
- [Releases](https://github.com/nathantannar4/Engine/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nathantannar4-engine
