xcode - 从嵌套的 Swift 包访问 Assets 时,SwiftUI 预览会崩溃

标签 xcode swiftui swift-package-manager

一个模块
我有一个具有两个结构的模块 (A)。

  • 一种直接在代码中启动的颜色。
  • 一种从 Assets 加载的颜色。
  • public struct CodeColor {
        public init() { }
        public let value = SwiftUI.Color(#colorLiteral(red: 0.8549019694, green: 0.250980407, blue: 0.4784313738, alpha: 1))
    }
    
    public struct AssetColor {
        public init() { }
        public let value = SwiftUI.Color("Legacy/Material/Gold", bundle: .module)
    }
    
    预览效果很好:
    A preview

    B模块
    第二个模块 (B) 应使用前一个 (A) 作为依赖项从以下位置加载颜色:
    import A
    
    public struct CodeColor {
        public init() { }
        public var value: SwiftUI.Color { A.CodeColor().value }
    }
    
    public struct AssetColor {
        public init() { }
        public var value: SwiftUI.Color { A.AssetColor().value }
    }
    
    但是一旦它触及模块(A)中的 Assets ,预览崩溃:
    B Preview
    🛑 错误:

    Can not preview in this file. Failed to update preview.

    RemoteHumanReadableError: Failed to update preview.
    
    The preview process appears to have crashed.
    
    Error encountered when sending 'previewInstances' message to agent.
    
    ==================================
    
    |  RemoteHumanReadableError: The operation couldn’t be completed. (BSServiceConnectionErrorDomain error 3.)
    |  
    |  BSServiceConnectionErrorDomain (3):
    |  ==BSErrorCodeDescription: OperationFailed
    
    那是为什么?
    注:奇怪的是,如果它在实际应用程序中(而不是另一个包),则确切的 B 预览代码正在工作
    Here is the full code on github

    最佳答案

    我在开发者论坛中找到了解决方案:
    友情链接
    https://developer.apple.com/forums/thread/664295?login=true#reply-to-this-question
    注意 - 本地
    解决方案是本地包。你定义

    let bundleNameIOS = "LocalPackages_TargetName"
    let bundleNameMacOs = "PackageName_TargetName"
    
    远程(例如来自 GitHub 的包)
    如果你在 GitHub 上有你的包并远程获取它,你不能定义本地包,你必须稍微改变它:
    let bundleNameIOS = "TargetName_TargetName"
    let bundleNameMacOs = "TargetName_TargetName"
    
    示例
    这是一个完整的实现示例,您必须将其与资源一起放入您的包中:
    // Inside your package with the resources:
    // Extend Bundle to access it in other packages
    extension Bundle {
    
        // public static let assets = Bundle.module
        // Updated with workaround
        public static let assets = Bundle.myModule
        
    }
    
    定义您的捆绑包
    private class CurrentBundleFinder {}
    extension Foundation.Bundle {
        static var myModule: Bundle = {
            /* The name of your package. You may have same PackageName and TargetName*/
            let bundleNameIOS = "TargetName_TargetName"
            let bundleNameMacOs = "TargetName_TargetName"
            
            let candidates = [
                /* Bundle should be present here when the package is linked into an App. */
                Bundle.main.resourceURL,
                /* Bundle should be present here when the package is linked into a framework. */
                Bundle(for: CurrentBundleFinder.self).resourceURL,
                // -> Optional UI Tests
                /* Bundle should be present here when the package is used in UI Tests. */
                Bundle(for: CurrentBundleFinder.self).resourceURL?.deletingLastPathComponent(),
                /* For command-line tools. */
                Bundle.main.bundleURL,
                /* Bundle should be present here when running previews from a different package (this is the path to "…/Debug-iphonesimulator/"). */
                Bundle(for: CurrentBundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent(),
                Bundle(for: CurrentBundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent(),
            ]
            
            for candidate in candidates {
                let bundlePathiOS = candidate?.appendingPathComponent(bundleNameIOS + ".bundle")
                let bundlePathMacOS = candidate?.appendingPathComponent(bundleNameMacOs + ".bundle")
                if let bundle = bundlePathiOS.flatMap(Bundle.init(url:)) {
                    return bundle
                } else if let bundle = bundlePathMacOS.flatMap(Bundle.init(url:)) {
                    return bundle
                }
            }
            fatalError("unable to find bundle")
        }()
    }
    
    访问包 B 中的包 A 资源
    您现在可以从包 B 中的包 A 访问资源,例如:
    let example = UIImage(named: "Exampe", in: Bundle.assets, with: nil)!
    

    关于xcode - 从嵌套的 Swift 包访问 Assets 时,SwiftUI 预览会崩溃,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67570615/

    相关文章:

    像 CocoaPods 一样带有子模块的 Swift 包

    iphone - 访问外部包中的图像

    ios - 横向模式下的 Xcode iPad 分辨率

    swiftui - 在 SwiftUI 中从 UIViewController 调用函数

    ios - SwiftUI 列表未更新内拉以刷新 View

    ios - Swift 包管理器 - 自定义框架

    ios - 将 CocoaPods 与 Xcode 一起使用时出现 Apple Mach-O 链接器错误

    ios - 在 iPhone 中创建树状结构

    swiftui - 根据其元素动态调整 GeometryReader 高度

    swift - 如何使用 Swift 包管理器指定 Swift 依赖项的 Beta 版本