SwiftUI preview fails to build when using enum as namespace

Viewed 52

Since Swift has no real first-class namespace support, enums are often used as a replacement. When doing so, SwiftUI previews don't build anymore:

import SwiftUI

enum Namespace { }

extension Namespace {
    struct ContentView: View {
        var body: some View {
            VStack {
                Text("ContentView")
                    .padding()
                InnerView()
                    .padding()
            }
        }
    }

    struct InnerView: View {
        var body: some View {
            Text("InnerView")
        }
    }
}

#if DEBUG
struct NamespaceContentViewPreviews: PreviewProvider {
    static var previews: some View {
        Namespace.ContentView()
    }
}
#endif

This produces a "Failed to build ContentView.swift" error with the diagnostic

Compiling failed: cannot find 'InnerView' in scope
.../NamespaceTest/ContentView.swift:19:17: error: cannot find 'InnerView' in scope

Note that the code compiles and runs just fine when building the app, it's just the previews I'm having problems with.

Adding typealias Innerview = Namespace.Innerview after the #if DEBUG helps, but for larger views with many subview this gets really tedious really fast.

Adding the PreviewProvider to the namespace does not help, unfortunately - simply moving it inside an extension Namespace{} disables previews entirely, and forwarding to a namespaced struct also doesn't help, either:

extension Namespace {
    struct ContentViewPreviews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
}

struct NamespaceContentViewPreviews: PreviewProvider {
    static var previews: some View {
        Namespace.ContentViewPreviews.previews
    }
}

leads to two "cannot find X in scope" errors :-(

The issue is present in Xcode 13.4.1 as well as Xcode 14 beta 6.

Is there another/easier way to get SwiftUI previews to work inside enums/namespaces?

1 Answers

You have two issues with your code:

First, Namespace is a SwiftUI keyword to name related MatchedGeometry. You were having a conflict in namespaces, ironically. I changed your Namespace to MyNamespace, and the errors went away. But is still won't run.

Second, once you go with a namespace, you have to use the namespace. So, you have to refer to InnerView() in Namespace.ContentView as Namespace.InnerView(). It will then build and run.

So, your code would be:

enum MyNamespace { } // Here

extension MyNamespace { // Here
    struct ContentView: View {
        var body: some View {
            VStack {
                Text("ContentView")
                    .padding()
                MyNamespace.InnerView() // Here
                    .padding()
            }
        }
    }

    struct InnerView: View {
        var body: some View {
            Text("InnerView")
        }
    }
}

struct Namespace_Previews: PreviewProvider {
    static var previews: some View {
        MyNamespace.ContentView()
    }
}
Related