Swift Package 或 Swift Playgrounds 写的 App 本身装不进 iPhone,也打不出 ipa。本文在 Windows 上把它转成 iOS App 工程,设置 Bundle ID、图标和权限说明后真机调试,继续加依赖,最后打包上架。
Swift Package 转 iOS App 只要一步:在 Windows 上用 VS Code 插件或
kxbuild project spm2app,把可执行 target 是 App 的包生成一个 iOS App 工程,然后装到 iPhone 断点调试、打包 ipa 上架。包本身能编译,却装不进手机、也打不出 ipa,在 Mac 上也一样,所以要先转换。转换只做一次,App 工程直接编译包里的源码,之后照常改包里的代码,App 跟着一起变。
开始前准备好一部 iPhone 和数据线、Apple 开发者账号下的证书,以及装好 KXBuild 和 VS Code 插件的电脑,还没装的按[安装页]操作。
能转的是可执行 target 本身就是 App 的包,也就是可执行 target 里有
@main struct …: App 这样的 SwiftUI 入口,或者 UIKit 的
UIApplicationDelegate。其他几种情况这样处理。
下面用一个最小的包演示,目录是
D:\projects\Hello。
Hello 是 App,
HelloCore 是 App 用到的一个库 target。
1Hello\2├─ Package.swift3└─ Sources\4 ├─ Hello\5 │ └─ HelloApp.swift6 └─ HelloCore\7 └─ Greeting.swift
1// swift-tools-version:5.9 2import PackageDescription 3 4let package = Package( 5 name: "Hello", 6 platforms: [.iOS(.v16)], 7 products: [ 8 .executable(name: "Hello", targets: ["Hello"]) 9 ],10 targets: [11 .executableTarget(name: "Hello", dependencies: ["HelloCore"]),12 .target(name: "HelloCore")13 ]14)
1import SwiftUI 2import HelloCore 3 4@main 5struct HelloApp: App { 6 var body: some Scene { 7 WindowGroup { 8 Text(Greeting.text) 9 }10 }11}
在 VS Code 中「文件 → 打开文件夹」打开 Package.swift 所在目录。打开 .swift 文件就有补全和错误提示,SwiftUI、UIKit 的 API 按 iOS 补全。转换前可以先在包目录编译检查一次。
1kxbuild build .
以下两种做法任选其一。
KXBuild: 将 Swift Package 转为 App 工程,或者点 KXBuild 视图里标着「点击生成 App 工程」的那一行。按提示填 App 名和 Bundle ID,App 名留空就用可执行 target 的名字。完成后新工程自动设为启动项目。1kxbuild project spm2app . -n Hello -b com.yourname.hello
Bundle ID 换成你自己的反向域名,后面生成描述文件、在 App Store Connect 建 App 都用它。转换完成后包目录下多了一个和 App 同名的子目录,里面就是 App 工程。
1Hello\ 2├─ Package.swift 3├─ Sources\ 4│ ├─ Hello\ 5│ └─ HelloCore\ 6└─ Hello\ 7 ├─ .gitignore 8 ├─ Hello.xcodeproj 9 └─ Hello\10 └─ Assets.xcassets
App 工程里没有复制源码,它直接编译
Sources\Hello 目录,包里的其他 target 按本地包引用。所以改包里的代码就是改 App,这个子目录要和包一起提交到版本库。子目录里自带的
.gitignore 已经排除了构建产物和证书目录。
示例里 App 用到了
HelloCore,而包里没有暴露它的库产品,App 工程引用不到。这时转换会在 Package.swift 的 products 里补一行,插件会弹出提示,命令行输出的
manifestChanges 里也会列出来。
转换完成后检查并提交 Package.swift 的这处改动。
1 products: [2 .executable(name: "Hello", targets: ["Hello"]),3 .library(4 name: "HelloCore",5 targets: [ "HelloCore" ]6 ),7 ],
手边还没有证书时,可以先不签名编译一次,确认工程没问题。
1kxbuild build .\Hello\Hello.xcodeproj -D CODE_SIGNING_ALLOWED=NO
命令行用
--executable 指定要转的那个,每个 App 各转一次,工程分别建在各自的子目录里。
1kxbuild project spm2app . -n HelloPro -b com.yourname.hellopro --executable HelloPro
可执行 target 的源码目录就是包根目录(Package.swift 里写了
path: "."),工程建在包里会被当成 App 源码一起编译。用
-o 把工程放到包目录外,例如
-o ..。
Swift Playgrounds 建的 App 项目是一个
.swiftpm 目录,Package.swift 里有
import AppleProductTypes,用
.iOSApplication 描述 App。这种包目前不能直接转换,运行转换会提示暂不支持。做法是另建一个普通的包,把源码复制过去再转换,原来的
.swiftpm 不动,在 iPad 上照常打开。
下面以
D:\projects\PGApp.swiftpm 为例。先把其中的 .swift 文件连同子文件夹复制到新包的
Sources\PGApp 目录,Package.swift 除外。
1robocopy D:\projects\PGApp.swiftpm D:\projects\PGApp\Sources\PGApp *.swift /S /XF Package.swift
再在
D:\projects\PGApp 下新建 Package.swift,写入下面的内容,最低 iOS 版本照原项目的
platforms 填。
1// swift-tools-version: 5.9 2import PackageDescription 3 4let package = Package( 5 name: "PGApp", 6 platforms: [.iOS(.v16)], 7 targets: [ 8 .executableTarget(name: "PGApp") 9 ]10)
然后按上一节转换。原项目
.iOSApplication 里写的 Bundle ID 用
-b 带上,版本号、显示名称、图标在下一节的编辑器里改。原项目有 Assets.xcassets 的,把里面的图片集和颜色集复制到新 App 工程的
Assets.xcassets 中。
1cd D:\projects\PGApp2kxbuild project spm2app . -b com.yourname.pgapp
在 KXBuild 视图中右键 Hello target 选「KXBuild: 编辑目标设置」,General 页可以改 Display Name、Bundle Identifier、版本号和最低 iOS 版本,Ctrl+S 保存。各字段见[工程设置编辑器]。

图标在 App 工程的
Hello\Hello\Assets.xcassets 里。在 KXBuild 视图中点工程节点下的资源目录打开它,把 AppIcon 的占位图换成一张 1024×1024 的 PNG,见[资源目录编辑器]。
转换出的工程没有 Info.plist 文件,构建时自动生成。用到相机、相册、定位这类权限时,把说明文字写成
INFOPLIST_KEY_ 开头的构建设置,构建后会出现在 App 的 Info.plist 里。以相机为例,以下两种做法任选其一。
INFOPLIST_KEY_NSCameraUsageDescription,值填给用户看的说明,Ctrl+S 保存。1kxbuild project add-setting -k INFOPLIST_KEY_NSCameraUsageDescription -v "用于扫描二维码" -t Hello -P .\Hello\Hello.xcodeproj
真机运行需要 Apple Development 证书(.p12)和包含这部手机 UDID 的 Development 描述文件。在 App 工程目录
Hello\Hello 下新建
keychain 文件夹,把两个文件放进去,再新建
password.properties,写一行「文件名=密码」。想让所有工程共用的,放到 KXBuild 安装目录下的
keychain 文件夹。证书的获取方式见 [Windows 给 iOS App 签名]。
1development.p12=123456
用数据线接上 iPhone,在 KXBuild 视图的「设备」节点选中它。第一次连接要安装设备驱动、在手机上点信任并打开开发者模式,见[设备准备]。
确认启动项目是 Hello,在
Sources\Hello 的代码里打断点,按 F5。KXBuild 构建、签名、安装、启动 App 并停在断点上,
HelloCore 这类库 target 里的代码同样能断点。改完代码先 Shift+F5 停止再按 F5。变量查看、调试控制台和崩溃定位见 [在 Windows 上真机调试 iOS App]。
转换只做一次,之后照常在包里开发。按改动的类型处理。
Sources\Hello 里新建或删除 .swift 文件。直接构建或按 F5,App 工程跟着这个目录走,不用做别的。no such module。新增的依赖是包里的另一个 target 时,先在 Package.swift 的 products 里给它加一个库产品,例如
.library(name: "HelloExtra", targets: ["HelloExtra"])。然后以下两种做法任选其一。
KXBuild: 添加 Swift Package 依赖…,选择包目录
D:\projects\Hello 作为本地包,产品选 HelloExtra。1kxbuild project add-package -P .\Hello\Hello.xcodeproj --path . --product HelloExtra
新增的依赖是远程包时,App 工程加同一个包和产品,版本规则和 Package.swift 里写的保持一致。以下两种做法任选其一。
KXBuild: 添加 Swift Package 依赖…,输入包的 Git 地址,选版本规则和产品。1kxbuild project add-package -P .\Hello\Hello.xcodeproj --url https://github.com/Alamofire/Alamofire.git --from 5.9.0 --product Alamofire
上架用 Apple Distribution 证书和 App Store 类型的描述文件,放进同一个
keychain 目录,在
password.properties 里加一行密码。然后以下两种做法任选其一。
KXBuild: 构建。1kxbuild build .\Hello\Hello.xcodeproj -c Release
完成后生成
Hello\build\Hello.ipa,Swift Package 转 iOS App 到上架的准备就齐了。在 App Store Connect 建 App、上传 ipa、填写资料和提交审核的完整流程见[在 Windows 上上架 App Store]。
这种包不用转换,把它作为本地包加到 App 工程里即可。它只有库产品、没有 App,先新建一个 App 工程再添加,见 [Swift 技术栈]。
原因是可执行 target 里找不到 App 入口,它是命令行程序。确认入口文件里有
@main 标记的
App 结构体,或者
UIApplicationDelegate。
要处理,可执行 target 在 Package.swift 里写的几样设置不会带到 App 工程,输出里会逐条列出。
Bundle.module,读取资源的代码改用
Bundle.main。设置环境变量
KXBUILD_SWIFT_WHOLE_MODULE=all 改为整模块编译,重开 VS Code 后再构建即可。Debug 构建在链接阶段报
undefined symbol: …vpfi,是开源 Swift 编译器的已知问题:
@State private var count = 0 这类带初始值的属性,所在结构体的 init 写在另一个文件里就会出现。App 的代码在包里,属于本地包,默认按文件编译,所以会碰到。
1setx KXBUILD_SWIFT_WHOLE_MODULE all
可以加
--force 在原目录重新生成,但整个 App 工程会重建,之前在工程设置里改过的内容和换过的图标都会还原。只是加了依赖的,按[上文]添加即可,不必重新转换。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70046796/viewspace-3187363/ ,如需转载,请注明出处,否则将追究法律责任。