1

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?

Gereon
  • 17,258
  • 4
  • 42
  • 73

1 Answers1

0

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()
    }
}
Yrb
  • 8,103
  • 2
  • 14
  • 44
  • I think you're referring to the `@Namespace` property wrapper - this does not collide in any way with the (example) `Namespace` enum I'm using. My test app builds and runs just fine, it's only the previews that generate errors. Regarding your second point: Swift's type resolution works from inner types outwards and finds the first match, so referring to just `InnerView` from within `Namespace` works as expected. See e.g. https://docs.swift.org/swift-book/LanguageGuide/NestedTypes.html for an "official" example. – Gereon Aug 28 '22 at 09:03
  • Well, have you actually tried the code? Further, you are dealing with `structs` in an `enum`, not `enum cases`. – Yrb Aug 28 '22 at 12:49
  • Yes I did, and it did not really address the issue I'm having. This question is not about the View itself - like I said, it compiles and runs just fine. The question is about why this valid piece of code does not work in the context of previews. – Gereon Aug 28 '22 at 15:48
  • My code works perfectly in previews. Added preview provider code. – Yrb Aug 28 '22 at 16:04