Preview Macro behaviors unexpectedly

Hey everyone,

I ran into an interesting quirk while working on a SwiftUI project involving the #Preview macro. I tried using it to test my views under different conditions, much like I would with the PreviewProvider struct. However, I noticed some unexpected behavior. Let me show you the difference between the two approaches with a quick example:

struct Test_Previews: PreviewProvider {
	static var previews: some View {
	    Group {
			Text("First Preview")
				.previewDisplayName("1")
			Text("Second Preview")                 
				.previewDisplayName("2")
		}
	}
}

This code should generate two previews in the canvas, each with a different name. And it works perfectly!

But when I use #Previews, things go differently:

#Preview {
	Group {
	    Text("First Preview")             
		    .previewDisplayName("1")         
		Text("Second Preview")             
			.previewDisplayName("2")
	    }
}

Instead of two previews, it only creates one, combining all the views into a single preview.

In trying to find a solution, I explored two avenues:

  1. Creating my own macro to mimic the struct's behavior.
  2. Understanding why the #Preview macro doesn't behave as expected.

After some digging, here's what I found:

  1. Crafting a custom macro seems tricky because Xcode is responsible for opening the canvas, and it seems to rely on internal triggers like the preview macro or explicit struct instantiation.
  2. The discrepancy with the original macro might stem from the type of closure it accepts. Despite documentation stating it should be a @ViewBuilder, the macro's definition specifies a closure of type body: @escaping @MainActor() -> any View, without mentioning @ViewBuilder.

While using multiple #Preview instances can generate several previews, it gets tricky when testing constructs like ForEach, where reverting to the struct becomes necessary.

Overall, I don't see a good reason for the original macro to deviate from the struct's behavior.

Am I missing anything here? Let's discuss it in the comments.

5 Likes

If you're wondering about the purpose behind this functionality, it's primarily to enable testing of views with diverse input data.

Xcode does very special and proprietary things with the #Preview macro. For example, it declares an arbitrarily named (seemingly) at the global scope but the macro declaration doesn't declare any names.

Additionally, #Preview and preview providers are part of SwiftUI and these are the forums for the Swift language, so there won't be much help here.