本地Swift Package实例化Storyboard报找不到storyboard如何解决
问题描述
从本地Swift Package(以下简称SPM)中实例化ViewController时,始终收到Could not find a storyboard错误,当前实现代码如下:
let myViewController = UIStoryboard( name: "MyStoryboard", bundle: Bundle( url: Bundle.main.url( forResource: "myModule_myModule", withExtension: "bundle" )! ) ).instantiateViewController(withIdentifier: "MyViewController")
上述代码在引用远程SPM依赖时可正常运行,但将SPM直接拖入Xcode作为本地依赖使用时就会触发报错。
根本原因
两种SPM引入方式的资源打包逻辑存在差异:
- 远程引入的SPM依赖,资源会被打包为命名格式为
[模块名]_[模块名].bundle的独立资源包,存放在主工程Bundle路径下,原有硬编码bundle名称的查找逻辑可以正常命中 - 本地直接拖入的SPM依赖不会生成上述命名格式的独立资源bundle,资源直接挂载在对应模块的类关联的Bundle路径下,硬编码查找
myModule_myModule.bundle会得到空值,最终导致Storyboard查找失败。
修复方法
放弃硬编码资源bundle名称的写法,使用SPM原生的Bundle定位逻辑,可同时兼容本地、远程两种引入场景:
- 如果代码写在对应SPM模块内部,直接使用SPM自动生成的
Bundle.module访问模块资源即可,写法最简洁:
let myViewController = UIStoryboard( name: "MyStoryboard", bundle: .module ).instantiateViewController(withIdentifier: "MyViewController")
- 如果是在主工程或者其他模块跨模块调用该SPM的资源,先在目标SPM内声明一个公开的占位类,再通过
Bundle(for:)方法定位对应模块的Bundle:- 在目标SPM模块内新增公开类(无需额外实现逻辑,仅用于Bundle定位)
public class ModuleBundleAnchor: NSObject {}- 替换原有的Bundle初始化逻辑
let myViewController = UIStoryboard( name: "MyStoryboard", bundle: Bundle(for: ModuleBundleAnchor.self) ).instantiateViewController(withIdentifier: "MyViewController")
注意:
Bundle.module是SPM编译时自动生成的属性,仅能在对应模块的源码范围内访问,跨模块调用时无法直接使用,这类场景选择Bundle(for: 模块内类.self)的写法即可。
内容的提问来源于stack exchange,提问作者Bernhard Harrer
相关产品推荐
相关产品推荐

