用 Python 写手机应用这件事,Flet 算是目前体验最顺的一条路------底层是 Flutter,你只写 Python。但发 iOS 版的时候有个绕不开的前提:flet build ipa 底层还是走 Flutter 那套,最终要调 Xcode,所以必须在 macOS 上构建,而且要装 Xcode 15 或更新的版本。Windows 上跑这条命令是不行的。
我的分工是:Python 代码在 Windows 上写,出包丢给 macOS 机器;证书、描述文件、IPA 上传这些用开心上架(AppUploader)在 Windows 上处理。下面把签名那部分怎么配讲清楚,这是 Flet 出 iOS 包时最容易卡住的地方。
先说结论:哪一步真绕不开 Mac
| 环节 | 我实际怎么做的 |
|---|---|
| 注册 Bundle ID、签发证书、生成描述文件 | Windows 上做完 |
flet build ipa |
要 macOS + Xcode 15 或更新版本 |
| 提交 App Store | Windows 上传 |
| 装到 iPhone 测试 | 扫码注册 UDID,再扫码装 |
说明一下:我用的签证书工具本身在 macOS 上也能用(网页版不挑系统,桌面客户端有 macOS 版)。表里"不需要 Mac"是指这一步不再需要 Xcode、钥匙串访问或 Transporter。
第一步:定好 Bundle ID
Flet 的 iOS 包标识可以在 pyproject.toml 里配,也可以用 flet build ipa 的命令行参数传。定成反向域名,比如 com.yourcompany.appname。
后面注册的 Bundle ID 和描述文件绑定的 App ID 都要和它完全一致。
第二步:在 Windows 上签出证书和描述文件
注册 Bundle ID :开心上架的「Bundle ID」页面点「新建 Bundle ID」,Identifier 填上一步那个值。创建后改不了 。用到推送、相册这些能力的,在这个 Bundle ID 的「权限」页面勾上再保存。

签发证书 :「证书」页面点「新建证书」,上架一定要选发布签名证书 。这点值得强调------Flet 的官方文档专门提醒过,上架要用 distribution 证书签,用开发证书或者 ad hoc 签出来的包传上去会被判签名无效。绑定上面的 Bundle ID,填名称邮箱,设一个 .p12 保护密码,服务器不保存它,忘了只能吊销重签。
勾上「使用开心上架服务同步证书」的话,构建机上能直接下载同一张证书,不用拿 U 盘拷。
生成描述文件 :「描述文件」页面点「新建描述文件」,上架选 App Store 类型,内测选 Ad Hoc 并勾上测试设备,关联上面的 Bundle ID 和证书,提交后下载 .mobileprovision。
第三步:在 macOS 构建机上导入
bash
security import dist.p12 -k ~/Library/Keychains/login.keychain-db -P "你的p12密码" -T /usr/bin/codesign
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp AppStore.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/
第四步:配签名参数并构建
Flet 的签名配置有两种写法。一种写在 pyproject.toml 里:
toml
[tool.flet.ios]
team_id = "你的TeamID"
signing_certificate = "Apple Distribution: Your Company (TEAMID)"
provisioning_profile = "你的描述文件名称"
另一种直接作为参数传给命令:
bash
flet build ipa \
--team <你的TeamID> \
--ios-signing-certificate "Apple Distribution: Your Company (TEAMID)" \
--ios-provisioning-profile "你的描述文件名称"
具体参数名在不同 Flet 版本里有过调整,以你装的那个版本 flet build ipa --help 输出的为准。
几个值从哪来:
- Team ID 就是开发者账号那串 10 位字符,在开心上架的账号概览里能直接看到;
- signing certificate 填证书的通用名称,也就是钥匙串里显示的那一整串;
- provisioning profile 填描述文件的名称------你在开心上架创建时填的那个名字,不是文件名也不是 UUID。
Flet 社区里报过一批上传时报签名无效的问题,大部分最后查出来是这三个值有一个对不上,或者用错了证书类型。构建完先别急着传,下一步会讲怎么先自查。
第五步:把 IPA 传上去
拿到 IPA 之后我不直接传,先在开心上架的「文件&提交」页面把包打开看一眼------Bundle ID 对不对、用的是不是 App Store 类型的描述文件、证书是不是发布类型、有没有过期。上面说的那类签名问题,在这一步基本就能看出来,比传上去等 Apple 退回来快多了。
确认没问题再点「提交 App Store」,日志实时滚。
命令行版本,Runner 是 Windows 或 Linux 也能跑:
bash
appuploader-cli upload -f build/ipa/app.ipa -u dev@example.com -p abcd-efgh-ijkl-mnop
-p 是 Apple 生成的 App 专用密码(xxxx-xxxx-xxxx-xxxx),不是 Apple ID 登录密码。也可以用 API 密钥:
bash
appuploader-cli upload -f build/ipa/app.ipa --api-key <KeyID> --api-issuer <IssuerID> --private-key Auth_Key.p8
密钥方式只收 .ipa,且 App 要先在 App Store Connect 里建好。
第六步:发内测包
「设备」页面点「注册设备」,二维码发出去让人扫上报 UDID(一年 100 台,免费账号 3 台)。回描述文件那边点「重建」,生成一份包含这些设备的 Ad Hoc 描述文件。
把签名配置里的描述文件换成新的重新构建,然后在「文件&提交」页面生成二维码让人扫码装。
写在最后
Flet 让 Python 开发者不用学 Dart 也能出移动端应用,代价是 iOS 那次构建得借 macOS。但真正折腾人的从来不是那条命令,而是签名配置里那三个值------把它们的来源搞清楚,后面就是重复劳动了。