SwiftUI Previewable Macro

Apple added the Previewable macro to iOS 18. This generates the boilerplate wrapper view you need to preview a view with State bindings.

What’s The Problem?

I have mixed feelings about SwiftUI previews. When they work they can make creating a view an interactive experience. They can also be slow, break with confusing errors, and need boilerplate container views to create any state bindings.

For that last point consider this form view that expects a binding to a bool:

struct SettingsView: View {
  @Binding var showMessages: Bool
    
  var body: some View {
    NavigationStack {
      Form {
        Section("General Settings") {
          Toggle("Show messages", isOn: $showMessages)
        }
        ...
      }
      .navigationTitle("Settings")
    }
  }
}

Settings form with a toggle switch (on) to show messages

When previewing this view we need a binding. One way is to provide a constant value:

#Preview {
  SettingsView(showMessages: .constant(true))
}

That works but prevents us from interacting with the view. A better approach is to create a wrapper view that supplies the mutable state:

private struct ContainerView: View {
    @State var showMessages: Bool = false
    var body: some View {
        SettingsView(showMessages: $showMessages)
    }
}

#Preview {
  ContainerView()
}

That gives us a preview with a working toggle button, but creating a wrapper view for our state each time gets to be tiresome.

The Previewable Macro

In Xcode 16, you can replace that boilerplate wrapper code with the Swift Previewable macro:

#Preview {
  @Previewable @State var showMessages: Bool = false
  SettingsView(showMessages: $showMessages)
}

Declare any @State properties you need at the root level of the Preview and mark them with the @Previewable macro. A SwiftUI wrapper view is automatically generated containing the state properties. Expanding the Preview macro (abbreviated):

static func makePreview() throws -> DeveloperToolsSupport.Preview {
  DeveloperToolsSupport.Preview {
    struct __P_Previewable_Transform_Wrapper: SwiftUI.View {
      @State var showMessages: Bool = false

      var body: some SwiftUI.View {
        SettingsView(showMessages: $showMessages)
      }
    }
    return __P_Previewable_Transform_Wrapper()
  }
}

That looks pretty close to our original wrapper view.

Note: The @Previewable macro deploys back to iOS 17.0, macOS 14.0, tvOS 17.0, visionOS 1.0, and watchOS 10.0.

Learn More