Xcode 15 Preview not working with generic nested view

It seem that Xcode preview compiler fail correctly parse generic and produce a "Failed to build" error:

Proposed example:

^^^^^^^^^^^^^^^^^^^^^^^^^^^

import CoreBluetooth
import SwiftUI

/// Working in preview
/*
struct InitialView<S>: View where S: StringProtocol {
    var body: some View {
        NestedView()
    }

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

struct WorkingView: View {
    var body: some View {
        InitialView<String>()
    }
}
*/

/// Not working in preview
struct NotWorking: View {
    var body: some View {
        InitialView<String>()
    }

    struct InitialView<S>: View where S: StringProtocol {
        var body: some View {
            NestedView()
        }

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

struct ContentView_PreviewProviders: PreviewProvider {
    static var previews: some View {
        NotWorking()
    }
}

^^^^^^^^^^^^^^^^^^^^^^^^^^^

investigating the SampleView.1.preview-thunk.swift from ~/Library/Developer/Xcode/DerivedData/.../ directory, I found that compiler translate the above code into pre-compiled stage using typealias without considering the Generic condition-

^^^^^^^^^^^^^^^^^^^^^^^^^^^

...
extension NotWorking.InitialView.NestedView {
typealias InitialView = NotWorking.InitialView
typealias NestedView = NotWorking.InitialView.NestedView

    @_dynamicReplacement(for: body) private var __preview__body: some View {
        #sourceLocation(file: "/Users/giuseppe/Development/Private/Dev/MyPlayground/MyPlayground/SampleView.swift", line: 40)
                Text(__designTimeString("#32812.[2].[1].[1].[0].property.[0].[0].arg[0].value", fallback: "Hello"))
            
#sourceLocation()
    }
}

extension NotWorking.InitialView {
typealias InitialView = NotWorking.InitialView
typealias NestedView = NotWorking.InitialView.NestedView

    @_dynamicReplacement(for: body) private var __preview__body: some View {
        #sourceLocation(file: "/Users/giuseppe/Development/Private/Dev/MyPlayground/MyPlayground/SampleView.swift", line: 35)
            NestedView()
        
#sourceLocation()
    }
}

extension NotWorking {
    @_dynamicReplacement(for: body) private var __preview__body: some View {
        #sourceLocation(file: "/Users/giuseppe/Development/Private/Dev/MyPlayground/MyPlayground/SampleView.swift", line: 30)
        InitialView<String>()
    
#sourceLocation()
    }
}
...

^^^^^^^^^^^^^^^^^^^^^^^^^^^

Should be considered a compiler bug or I missed something in my code?

Replies

Here the Preview reported error:

== PREVIEW UPDATE ERROR:

CompileDylibError: Failed to build SampleView.swift

Compiling failed: reference to generic type 'NotWorking.InitialView' requires arguments in <...>

/Users/giuseppe/Library/Developer/Xcode/DerivedData/MyPlayground-avscbrxoycipyhbsfadustywnrmd/Build/Intermediates.noindex/> Previews/MyPlayground/Intermediates.noindex/MyPlayground.build/Debug-iphonesimulator/MyPlayground.build/Objects-normal/x86_64/> SampleView.1.preview-thunk.swift:35:35: error: reference to generic type 'NotWorking.InitialView' requires arguments in <...>
typealias NestedView = NotWorking.InitialView.NestedView
                                  ^
                                             <<#S: StringProtocol#>>
/Users/giuseppe/Development/Private/Dev/MyPlayground/MyPlayground/SampleView.swift:33:12: note: generic type 'InitialView' > declared here
    struct InitialView<S>: View where S: StringProtocol {
           ^
/Users/giuseppe/Library/Developer/Xcode/DerivedData/MyPlayground-avscbrxoycipyhbsfadustywnrmd/Build/Intermediates.noindex/> Previews/MyPlayground/Intermediates.noindex/MyPlayground.build/Debug-iphonesimulator/MyPlayground.build/Objects-normal/x86_64/> SampleView.1.preview-thunk.swift:21:34: error: ambiguous type name 'NestedView' in 'NotWorking.InitialView'
extension NotWorking.InitialView.NestedView {
          ~~~~~~~~~~~~~~~~~~~~~~ ^
/Users/giuseppe/Development/Private/Dev/MyPlayground/MyPlayground/SampleView.swift:38:16: note: found candidate with type > 'NotWorking.InitialView.NestedView'
        struct NestedView: View {
               ^
/Users/giuseppe/Library/Developer/Xcode/DerivedData/MyPlayground-avscbrxoycipyhbsfadustywnrmd/Build/Intermediates.noindex/> Previews/MyPlayground/Intermediates.noindex/MyPlayground.build/Debug-iphonesimulator/MyPlayground.build/Objects-normal/x86_64/> SampleView.1.preview-thunk.swift:35:11: note: found candidate with type '<<error type>>'
typealias NestedView = NotWorking.InitialView.NestedView
          ^
/Users/giuseppe/Library/Developer/Xcode/DerivedData/MyPlayground-avscbrxoycipyhbsfadustywnrmd/Build/Intermediates.noindex/> Previews/MyPlayground/Intermediates.noindex/MyPlayground.build/Debug-iphonesimulator/MyPlayground.build/Objects-normal/x86_64/> SampleView.1.preview-thunk.swift:23:47: error: ambiguous type name 'NestedView' in 'NotWorking.InitialView'
typealias NestedView = NotWorking.InitialView.NestedView
                       ~~~~~~~~~~~~~~~~~~~~~~ ^
/Users/giuseppe/Development/Private/Dev/MyPlayground/MyPlayground/SampleView.swift:38:16: note: found candidate with type > 'NotWorking.InitialView.NestedView'
        struct NestedView: View {
               ^
/Users/giuseppe/Library/Developer/Xcode/DerivedData/MyPlayground-avscbrxoycipyhbsfadustywnrmd/Build/Intermediates.noindex/> Previews/MyPlayground/Intermediates.noindex/MyPlayground.build/Debug-iphonesimulator/MyPlayground.build/Objects-normal/x86_64/> SampleView.1.preview-thunk.swift:35:11: note: found candidate with type '<<error type>>'
typealias NestedView = NotWorking.InitialView.NestedView
          ^

Because you can't! Each struct View should be single level!

  • @MobileTen can u explain the reason or link an Apple reference guide that require a View should not be segregated into a Namespace?

Add a Comment

Because you can't, the notion of a namespace as implemented in other languages like Java, C++, and C# is non-existent in Swift and SwiftUI. What you're doing is nesting value types and not creating namespaces. Nesting views in SwiftUI: https://luomein.medium.com/composable-generic-swiftui-view-part-2-d695d87b0f96

Namespaces (which is not SwiftUI @Namespace property wrapper) in their basic form are a way to group related areas of code in the form of dot separated syntax. The given benefit of that is the capability to distinguish between code blocks that share same name in a different path (e.g. UI.CoordinatorScreen.Fragment vs UI.ScannerScreen.Fragment). This mechanic is naturally enabled when u nest class/struct or enum types in Swift. The problem i mentioned is not related to the fact that nested generic objects doesn't work, because its a fact that it work perfectly when compile and run. The problem is related to a specific behaviour for Xcode preview (not Simulator) where compiler translate the related preview swift code section into an explicit version of the same file using typealias that are not made generic while this is required. For this reason in a second stage of compile it fail.

Thx for your reply, but this isn't the answer i'm looking for.

Once again you're are misinterpreting the concept of a namespace as it pretains to other Object Orient Languages. Continue on your merry way and all the best.

Hi,

Thanks for supplying the code to reproduce. Best next step will be to file a feedback with diagnostics so we can make sure the team knows about the issue and has a way to notify you when a fix is available.

Steps to generate helpful diagnostics:

  1. Reproduce the issue
  2. Click the "Diagnostics" button in the error banner in Previews' Canvas area (or if the banner is missing you can use the menu: Editor > Canvas > Diagnostics)
  3. In the sheet that appears, click "Generate Report" in the bottom left of the sheet
  4. Attach (or make from the folder) the resulting zip file to the bug (will be named something like previews-diagnostics-0123456789.zip)
  5. Zip up the parent folder containing the sample source and xcodeproj, and attach this too
  6. Report back here with the feedback ID

Then I'll make sure it is routed to the right team right away. Thanks!

Giuma is correct -- if syntactically correct code compiles and runs on the device or Simulator, then the failure of Preview is a bug, period. I can reproduce the problem in ten lines of code (not counting closing braces):

struct Outer {
    static let hello = "Hello"
    // Move MyText here and Preview compiles and runs.
}

extension Outer {
// With MyText here app compiles and runs correctly but
// Preview fails with "Compiling failed: cannot find 'hello' in scope"
    struct MyText: View {
        var body: some View {
            Text(hello) 
           // This does work with Preview: Text(Outer.hello) 
        }
    }
}

struct ContentView: View {
    var body: some View {
        Outer.MyText()
    }
}

#Preview {
    ContentView()
}
  • As a workaround, one can move the Preview lines to another file.

Add a Comment