Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwiftUI preview view's spm dependency can not locate bundle #2977

Open
jeffhodsdon opened this issue Mar 26, 2024 · 8 comments
Open

SwiftUI preview view's spm dependency can not locate bundle #2977

jeffhodsdon opened this issue Mar 26, 2024 · 8 comments
Labels
bug Something isn't working

Comments

@jeffhodsdon
Copy link

jeffhodsdon commented Mar 26, 2024

Description

Hello! Thank all for this project. It is the best and helps the ecosystem very much.

I'm running into an issue getting a SwiftUI preview working. Particularly one that relies on a third party decency that has bundled data — PhoneNumberKit

I believe I'm getting a crash on the generated Bundle.module extension. Info from an Apple Tool Dev on .module

I've looked into the working directory of the swift ui preview and do see the bundle.

Screenshot 2024-03-26 at 3 48 25 PM

The search method PhonenumberKit looks for the bundle —

https://github.com/marmelroy/PhoneNumberKit/blob/3.4.5/PhoneNumberKit/Bundle%2BResources.swift

Incident Identifier: 9E70831C-02AE-4FAA-A82B-9C846CD2C1CF
CrashReporter Key:   FEF7EC6B-5117-73BC-5FE9-E92D845BB5AA
Hardware Model:      Mac15,9
Process:             XCPreviewAgent [63928]
Path:                /Users/USER/Library/Developer/Xcode/UserData/Previews/Simulator Devices/9E3CBD03-D536-46F5-81AC-D319E5848FDE/data/Containers/Bundle/Application/39728000-7610-4B44-8078-86522E554620/XCPreviewAgent.app/XCPreviewAgent
Identifier:          com.apple.dt.PreviewAgent.iOS
Version:             15.3 (21.30.34)
Code Type:           ARM-64 (Native)
Role:                Foreground
Parent Process:      launchd_sim [60029]
Coalition:           com.apple.CoreSimulator.SimDevice.9E3CBD03-D536-46F5-81AC-D319E5848FDE [10563]
Responsible Process: SimulatorTrampoline [812]

Date/Time:           2024-03-26 13:53:46.3266 -0400
Launch Time:         2024-03-26 13:53:46.0438 -0400
OS Version:          macOS 14.4 (23E214)
Release Type:        User
Report Version:      104

Exception Type:  EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x0000000192fcb2e0
Termination Reason: SIGNAL 5 Trace/BPT trap: 5
Terminating Process: exc handler [63928]

Triggered by Thread:  0

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libswiftCore.dylib            	       0x192fcb2e0 _assertionFailure(_:_:file:line:flags:) + 244
1   AuthView.1.preview-thunk.dylib	       0x1077301a0 closure #1 in variable initialization expression of static NSBundle.module + 4424 (PhoneNumberKit.rspm_resource_bundle_accessor_ResourceBundleAccessor.swift:42)
2   AuthView.1.preview-thunk.dylib	       0x10772f048 one-time initialization function for module + 12 (PhoneNumberKit.rspm_resource_bundle_accessor_ResourceBundleAccessor.swift:11)
3   libdispatch.dylib             	       0x180171978 _dispatch_client_callout + 16
4   libdispatch.dylib             	       0x1801731b0 _dispatch_once_callout + 28
5   AuthView.1.preview-thunk.dylib	       0x107730374 NSBundle.module.unsafeMutableAddressor + 80 (PhoneNumberKit.rspm_resource_bundle_accessor_ResourceBundleAccessor.swift:11)
6   AuthView.1.preview-thunk.dylib	       0x1076af500 closure #1 in variable initialization expression of static NSBundle.phoneNumberKit + 2704 (Bundle+Resources.swift:35)
7   AuthView.1.preview-thunk.dylib	       0x1076aea60 one-time initialization function for phoneNumberKit + 12 (Bundle+Resources.swift:11)
8   libdispatch.dylib             	       0x180171978 _dispatch_client_callout + 16
9   libdispatch.dylib             	       0x1801731b0 _dispatch_once_callout + 28
10  AuthView.1.preview-thunk.dylib	       0x1076af950 NSBundle.phoneNumberKit.unsafeMutableAddressor + 80 (Bundle+Resources.swift:11)
11  AuthView.1.preview-thunk.dylib	       0x1077048b0 static PhoneNumberKit.defaultMetadataCallback() + 136 (PhoneNumberKit.swift:329)

Reproduction steps

Rules —

swift_library(
    name = "AuthUI",
    srcs = glob(["UI/**/*.swift"]),
    module_name = "AuthUI",
    visibility = ["//visibility:public"],
    data = [
        ":Resources"
    ],
    deps = [
        "@swiftpkg_iphonenumberfield//:iPhoneNumberField"
    ],
)

ios_framework(
    name = "AuthUI.framework",
    bundle_id = "com.foo.authui",
    minimum_os_version = "17.0",
    families = ["iphone"],
    infoplists = ["UI/Info.plist"],
    visibility = ["//visibility:public"],
    deps = [
        ":AuthUI",
    ],
)

ios_application(
    name = "FooMain",
    app_icons = glob([
        "Resources/Assets.xcassets/AppIcon.appiconset/**",
    ]),
    bundle_id = "com.foo.app",
    families = ["iphone"],
    infoplists = [":Info.plist"],
    launch_storyboard = "Launch.storyboard",
    minimum_os_version = "17.0",
    provisioning_profile = ":xcode_profile",
    resources = glob(
        exclude = ["Resources/Assets.xcassets/AppIcon.appiconset/**"],
    ) + ["//ios/Components/Auth:Resources"],
    visibility = ["//visibility:public"],
    frameworks = ["//ios/Components/Auth:AuthUI.framework"],
    deps = [":FooMain.lib"],
)

xcodeproj(
    name = "xcodeproj",
    project_name = "FooMain",
    top_level_targets = [
        top_level_target(
            ":FooMain",
            target_environments = [
                "device",
                "simulator",
            ],
        ),
        "//ios/Components/Auth:AuthUI.framework"
    ],
)

Expected behavior

Swift UI preview to work.

rules_xcodeproj version

1.18.0

Xcode version

15.3

Bazel version

7.1.0

rules_apple version

3.3.0

rules_swift version

1.17.0

Additional information

Test SwiftUI View —

import PhoneNumberKit
import SwiftUI

struct AuthConfirmOTPView: View {
    var body: some View {
        Button("Test") {
            let f = try? PhoneNumberKit().parse("2125181004")
            print(f)
        }
    }
}

#Preview {
    AuthConfirmOTPView()
}
Screenshot 2024-03-26 at 4 02 41 PM
@jeffhodsdon jeffhodsdon added the bug Something isn't working label Mar 26, 2024
@mattrobmattrob
Copy link
Contributor

Are you using https://github.com/cgrindel/rules_swift_package_manager to create these targets? Does it run on a simulator outside of Xcode using bazel run :AltaMain and not have bundle accessor issues?

@jeffhodsdon
Copy link
Author

jeffhodsdon commented Mar 26, 2024

@mattrobmattrob yes, using https://github.com/cgrindel/rules_swift_package_manager. PhoneNumberKit does run on the simulator but not the preview.

@jeffhodsdon jeffhodsdon changed the title SwiftUI preview view's swift pm dependency can not locate bundle SwiftUI preview view's spm dependency can not locate bundle Mar 26, 2024
@mattrobmattrob
Copy link
Contributor

Hmm, it seems odd that it's crashing in the generated resource_bundle_accessor from rules_swift_package_manager when it has its own bundle accessor (PhoneNumberKit/Bundle+Resources.swift). Something similar WRT SwiftUI previews is even mentioned:

https://github.com/marmelroy/PhoneNumberKit/blob/104a53d62c55aa5794faf30b29ccad319653b552/PhoneNumberKit/Bundle%2BResources.swift#L5-L9

Have you seen this before, @cgrindel? Can the generation of that resource_bundle_accessor be disabled, @jeffhodsdon?

@cgrindel
Copy link
Contributor

The accessors have different names. I presume that PhoneNumberKit is using their custom accessor internally. What is it about SwiftUI preview that would just call the generated accessor? 🤔

Since all Swift packages with resources have this accessor generated, do all Swift packages fail in SwiftUI preview?

@luispadron
Copy link
Contributor

FWIW in our Cash App code we ran into dynamic framework paths being the root cause of crashes when loading bundles. A lot of code assumes Bundle.main (or equivalent) is the path to the resources but when dynamically linking/in previews this is not the case.

@luispadron
Copy link
Contributor

This is what we updated our resource bundle generator stencil to look like:

import Foundation

extension Bundle {

    private final class {{param.moduleName}}Sentinel {}

    @objc({{param.lowerCamelCaseModuleName}}ResourcesBundle)
    {{param.accessLevel}} static var {{param.lowerCamelCaseModuleName}}Resources: Bundle {
        let container = Bundle(for: {{param.moduleName}}Sentinel.self)
        let resourceBundleName = "{{param.moduleName}}Resources"

        if let resources = container.url(forResource: resourceBundleName, withExtension: "bundle") {
            return Bundle(url: resources)!
        }

        #if targetEnvironment(simulator)
        // During Xcode Previews the framework is run from an XCPreviewAgent.
        // The resource and framework live outside of this apps bundle.
        // Xcode will set `DYLD_FRAMEWORK_PATH` to the path of `BUILT_PRODUCTS_DIR` for the framework.
        // This allows us to get the path to the framework and load the resources from there.
        if ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1" {
            let dyldPath = ProcessInfo.processInfo.environment["DYLD_FRAMEWORK_PATH"].map(URL.init(fileURLWithPath:))
            guard
                let dyldBaseName = dyldPath?.lastPathComponent,
                let frameworkURL = dyldPath?.appendingPathComponent("\(dyldBaseName).framework"),
                let framework = Bundle(url: frameworkURL),
                let resourcesURL = framework.url(forResource: resourceBundleName, withExtension: "bundle"),
                let resources = Bundle(url: resourcesURL)
            else {
                fatalError("Unable to load \(resourceBundleName) for Xcode Previews")
            }
            return resources
        }
        #endif

        fatalError("Unable to load \(resourceBundleName)")
    }

}

@cgrindel
Copy link
Contributor

cgrindel commented Apr 3, 2024

@luispadron Should we update the accessors that we generate in rules_swift_package_manager to be something like this?

@mattrobmattrob
Copy link
Contributor

I wouldn't want to differ from the bundle accessor that SPM generates at all, if possible.

Are you all generating that to overcome this SPM related issue or just making Swift Previews work with your bundle accessor, @luispadron?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants