canImport and SwiftUI Previews

Hello all!

I'm scratching my head with the problem:
Is it possible to import a module that contains a sample preview data, let's call it Mocks, using #if canImport(Mocks) but only in DEBUG?
Modules are setup using package.swift like so:

import PackageDescription

let package = Package(
    name: "Module",
    platforms: [
        .macOS(.v10_15), .iOS(.v15)
    ],
    products: [
        .library(
            name: "Module",
            targets: ["Module"]
        ),
        .library(
            name: "ModuleInterface",
            targets: ["ModuleInterface"]
        ),
        .library(
            name: "ModuleMocks",
            targets: ["ModuleMocks"]
        )
    ],
    targets: [
        .target(
            name: "Module",
            dependencies: [
                "ModuleInterface"
            ]
        ),
        .target(
            name: "ModuleInterface"
        ),
        .target(
            name: "ModuleMocks",
            dependencies: [
                "ModuleInterface"
            ]
        ),
        .testTarget(
            name: "ModuleTests",
            dependencies: [
                "Module",
                "ModuleInterface",
                "ModuleMocks"
            ]
        ),
    ]
)

code:

#if canImport(ModuleMocks)
import ModuleMocks
#Preview {
    Text(MyModuleMock().method().name)
}
#endif

Idea is to have a reusable set of components that can provide test data for testing and previews, but with a guarantee that it will not be shipped in the production project when compiled in Release config.

What is blocking me atm, is linker error when build in the preview panel in Xcode, error is:

== PREVIEW UPDATE ERROR:

    SchemeBuildError: Failed to build the scheme ”Submodules”
    
    linker command failed with exit code 1 (use -v to see invocation)
    
    Link Module_1A39B208D6406C_PackageProduct (arm64):
    ld: Undefined symbols:
      ModuleMocks.MyModuleMock.__allocating_init() -> ModuleMocks.MyModuleMock, referenced from:
          closure #1 @Swift.MainActor () -> SwiftUI.View in static Module.$s6Module33_FEB919C066B33920805A0FAA8DF556D4Ll7PreviewfMf_15PreviewRegistryfMu_.makePreview() throws -> DeveloperToolsSupport.Preview in Module.o
      type metadata accessor for ModuleMocks.MyModuleMock, referenced from:
          closure #1 @Swift.MainActor () -> SwiftUI.View in static Module.$s6Module33_FEB919C066B33920805A0FAA8DF556D4Ll7PreviewfMf_15PreviewRegistryfMu_.makePreview() throws -> DeveloperToolsSupport.Preview in Module.o
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    
    
    Link Submodules (arm64):
    clang: error: no such file or directory: '/Users/mega/Library/Developer/Xcode/DerivedData/Submodules-enrfgjxdlhzoadgdtvdcfvzpnhgj/Build/Intermediates.noindex/Previews/Submodules/Products/Debug-iphonesimulator/PackageFrameworks/Module_1A39B208D6406C_PackageProduct.framework/Module_1A39B208D6406C_PackageProduct'

here's an example project, file of interest is Module.swift

Ugh. Your idea should totally work. There are some known issues using Previews with Swift packages when there are dependencies across more than one level of target. Can you file a feedback with the preview diagnostics?

Ok, thanks for reassuring, at least I'm not doing anything super silly. Why do you mean by file a feedback?

That's full diagnostics from Preview

== DATE:

    Friday, 1 December 2023 at 21:13:54 Central European Standard Time
    
    2023-12-01T20:13:54Z



== PREVIEW UPDATE ERROR:

    SchemeBuildError: Failed to build the scheme ”Submodules”
    
    linker command failed with exit code 1 (use -v to see invocation)
    
    Link Module_1A39B208D6406C_PackageProduct (arm64):
    ld: Undefined symbols:
      ModuleMocks.MyModuleMock.__allocating_init() -> ModuleMocks.MyModuleMock, referenced from:
          closure #1 @Swift.MainActor () -> SwiftUI.View in static Module.$s6Module33_FEB919C066B33920805A0FAA8DF556D4Ll7PreviewfMf_15PreviewRegistryfMu_.makePreview() throws -> DeveloperToolsSupport.Preview in Module.o
      type metadata accessor for ModuleMocks.MyModuleMock, referenced from:
          closure #1 @Swift.MainActor () -> SwiftUI.View in static Module.$s6Module33_FEB919C066B33920805A0FAA8DF556D4Ll7PreviewfMf_15PreviewRegistryfMu_.makePreview() throws -> DeveloperToolsSupport.Preview in Module.o
    clang: error: linker command failed with exit code 1 (use -v to see invocation)
    
    
    Link Submodules (arm64):
    clang: error: no such file or directory: '/Users/mega/Library/Developer/Xcode/DerivedData/Submodules-enrfgjxdlhzoadgdtvdcfvzpnhgj/Build/Intermediates.noindex/Previews/Submodules/Products/Debug-iphonesimulator/PackageFrameworks/Module_1A39B208D6406C_PackageProduct.framework/Module_1A39B208D6406C_PackageProduct'
    



== VERSION INFO:

    Tools: 15A240d
    OS:    22G91
    PID:   2970
    Model: MacBook Pro
    Arch:  arm64e



== ENVIRONMENT:

    openFiles = [
        /Users/mega/Desktop/Submodules/Module/Sources/Module/Module.swift
    ]
    wantsNewBuildSystem = true
    newBuildSystemAvailable = true
    activeScheme = Submodules
    activeRunDestination = iPhone 15 Pro variant iphonesimulator arm64
    workspaceArena = [x]
    buildArena = [x]
    buildableEntries = [
        Module
        Module
    ]
    runMode = Dynamic Replacement



== SELECTED RUN DESTINATION:

    name = iPhone 15 Pro
    eligible = true
    sdk = Optional(<DVTSDK:0x157f17880:'iphonesimulator17.0':Simulator - iOS 17.0:<DVTFilePath:0x600002bbb100:'/Applications/Xcode_RC.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator17.0.sdk'>>)
    variant = Optional("iphonesimulator")
    device = Optional(<DVTiPhoneSimulator: 0x157fe49b0> {
    		SimDevice: iPhone 15 Pro (E482D2DE-2407-4AF0-A4D0-3BE938B00CBA, iOS 17.0, Shutdown)
    			PairedSim: <DVTiPhoneSimulator: 0x157fe2210> {
    		SimDevice: Apple Watch Series 9 (45mm) (66F279DD-EAD3-4D49-9194-518CB27120C4, watchOS 10.0, Shutdown)
    }
    })



== SELECTED RUN DESTINATION:

    Simulator - iOS 17.0 | iphonesimulator | arm64 | iPhone 15 Pro | Apple Watch Series 9 (45mm)



== PACKAGE RESOLUTION ERRORS:

    



== REFERENCED SOURCE PACKAGES:

    <IDESwiftPackageCore.IDESwiftPackage:0x2c23b6eb0 path:'/Users/mega/Desktop/Submodules/Module'>



== SESSION GROUP 290:

    workspace identifier: WorkspaceIdentifier(identifier: 68BFF3F6-4E5C-42C4-B017-875E0A8B699F)
    providers: [
        Preview Provider | Registry-Module.swift#1 [Editor(259)]
    ]
    translation units: [
        /Users/mega/Desktop/Submodules/Module/Sources/Module/Module.swift
    ]
    attributes: [
        Editor(259):     []
    ]
    session: 291
    request sessions: [
        Registry[Module.swift #1 (line 12)]: not completed
    ]



== UPDATE SESSION 291:

    Start Date: Friday, 1 December 2023 at 21:13:40 Central European Standard Time
    Preview Provider {
        Simulator {
            platform: none
            device: 3CACA696-A931-4C1C-A4A6-B98B632CEFFA iPhone 15 Pro
            buildNumber: 21A328
            runtimePath: /Library/Developer/CoreSimulator/Volumes/iOS_21A328/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.0.simruntime
        }
    }
    Preview Preflight {
        Simulator {
            platform: none
            device: 3CACA696-A931-4C1C-A4A6-B98B632CEFFA iPhone 15 Pro
            buildNumber: 21A328
            runtimePath: /Library/Developer/CoreSimulator/Volumes/iOS_21A328/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 17.0.simruntime
        }
    }
    Build Graph {
        target dynamicPackageProduct Module (#1)
           target dynamicPackageProduct Module (#2)
              target dynamicPackageProduct ModuleInterface (#3)
              translationUnit Module.swift (#4)
              Preview Provider | Registry-Module.swift#1 [Editor(259)] Preview Provider | Registry-Module.swift#1 [Editor(259)] (#5)
              Preview Preflight | Registry-Module.swift#1: from Editor(259) for local Preview Preflight | Registry-Module.swift#1: from Editor(259) for local (#6)
        target dynamicPackageProduct ModuleInterface (#7)
           target dynamicPackageProduct ModuleInterface (#3)
        target dynamicPackageProduct ModuleMocks (#8)
           target dynamicPackageProduct ModuleMocks (#9)
              target dynamicPackageProduct ModuleInterface (#3)
    }
    Update Plan {
        iOS [arm64 iphonesimulator17.0 iphonesimulator] (iPhone 15 Pro, E482D2DE-2407-4AF0-A4D0-3BE938B00CBA-iphonesimulator17.0-arm64-iphonesimulator), ["Module", "Module"], thinning disabled) {
            Destination: iPhone 15 Pro E482D2DE-2407-4AF0-A4D0-3BE938B00CBA | default device for iphonesimulator [
                Framework Agent - Previews {
                    execution points [
                        point Preview Preflight | Registry-Module.swift#1: from Editor(259) for local
                        provider Module.Registry[Module.swift #1 (line 12)]
                    ]
                    translation units [
                        Module.swift (in Module)
                    ]
                    loadable products [
                        Description(buildableName: "Module", moduleName: "Module")
                    ]
                    modules [
                        Module
                        ModuleInterface
                    ]
                }
            ]
        }
    }



== POWER STATE LOGS:

    1/12/2023, 21:13 Received power source state: Battery Powered (lowPowerMode: false, status: charging, level: 84%)
    1/12/2023, 21:13 No device power state user override user default value.Current power state: Full Power

Why do you mean by file a feedback ?

Through here: Bug Reporting - Apple Developer