如何将App主Main Storyboard存入Swift Package并兼容info.plist配置
解决方案
前置修正:完善Package.swift配置
你当前的Package.swift存在两处需要调整的问题:
- 测试Target名称和主Target重名,会导致编译报错
- 没有显式声明资源文件,正式打包时可能出现资源丢失
修正后的完整代码如下:
// swift-tools-version:5.5 import PackageDescription let package = Package( name: "FooPackage", defaultLocalization: "en", products: [ .library(name: "FooPackage", targets: ["FooPackage"]), ], targets: [ .target( name: "FooPackage", dependencies: [], // 声明资源目录 resources: [.process("Resources")] ), // 修正测试Target名称 .testTarget( name: "FooPackageTests", dependencies: ["FooPackage"]), ] )
可行方案(两种可选)
方案1:放弃info.plist的NSMainStoryboardFile配置,手动加载主Storyboard(更推荐,无兼容性问题)
该方案完全避开info.plist只能读取主Bundle资源的限制,是苹果生态推荐的标准处理逻辑:
- 首先在Swift Package内暴露公共的Storyboard访问API,添加代码到
FooPackage.swift:
import AppKit public extension NSStoryboard { // 对外暴露包内的主Storyboard实例 static let packageMainMenu = NSStoryboard(name: "Main Menu", bundle: .module) } // 也可以直接暴露初始化好的入口控制器,减少宿主侧代码 public func getPackageMainInitialController() -> NSViewController? { NSStoryboard.packageMainMenu.instantiateInitialController() as? NSViewController }
- 宿主App侧调整配置和逻辑:
- 删除info.plist中
Main storyboard file base name (macOS)配置项 - 在AppDelegate中手动初始化窗口,加载包内的Storyboard:
import AppKit import FooPackage class AppDelegate: NSObject, NSApplicationDelegate { var window: NSWindow! func applicationDidFinishLaunching(_ aNotification: Notification) { guard let initialController = getPackageMainInitialController() else { fatalError("加载包内主Storyboard失败") } window = NSWindow(contentViewController: initialController) window.makeKeyAndOrderFront(nil) NSApp.activate(ignoringOtherApps: true) } }
方案2:编译时拷贝Storyboard到宿主App主Bundle(兼容原有info.plist配置)
如果不想修改原有App的启动逻辑,可以通过编译阶段配置把包内的Storyboard拷贝到宿主主Bundle,直接适配原有info.plist的配置:
- 选中宿主App的Xcode工程,点击对应Target,切换到Build Phases标签
- 点击左上角+号,选择New Copy Files Phase
- 目标位置选择
Resources,可按需勾选Copy only when installing - 点击阶段下方的+号,找到FooPackage下的
Base.lproj/Main Menu.storyboard以及所有本地化版本的对应storyboard文件添加到列表中 - 保留info.plist中的
NSMainStoryboardFile配置不变即可正常使用
注意事项
如果是iOS项目,上述逻辑完全通用,只需要把NSStoryboard替换为UIStoryboard,窗口初始化逻辑调整为iOS对应的实现即可。
内容的提问来源于stack exchange,提问作者Alexander
相关产品推荐
相关产品推荐

