iOS 构建时支持动态指定构建模式和 bundleId

在 《Flutter 搭建 iOS 命令行服务打包发布全保姆式流程》 里介绍过如何通过自定义配置,完成一套自己企业内部的自定义构建过程,当然也有一些建议如使用:fastlanejenkinsappcenter 等等,事实上也尝试过这些平台,也在上面使用过一段时间,但是这里解释为什么不用这些平台:

  • 打包机器不登录开发者账号,需要本地开发机器是 Automatic ,而打包机上使用 Manual;
  • 一个项目需要支持打包时指定 mobileprovisionbundleId,例如 QA 和 Prod 打包后是两个不同的 bundleId,两个 ipa 可以同时存在手机上;
  • 自定义构建时修改某些信息;

所以基于这些,最终决定了自己构建一套 命令行的打包模式 ,大概总结是:

  • 通过 PlistBuddy 在编译时修改 plist 信息;
  • 生产不同的 mobileprovision 文件;
  • 在 Xcode 取消 automatically manage signing,选择导入 Profile 文件,然后通过 git 生成 .patch ,在打包机器上执行 git apply;
  • 通过 xcodebuild 打包构建;
  • 通过 ExportOptions.plist 模版进行 xcodebuild -exportArchive 得到 ipa;

详细流程可以看上面原文,但是这个流程其实一直有一个问题,那就是通过 git 生成 .patch 文件,每次一旦 project.pbxproj 出现变化, 就可能会导致 git apply 失败。

Xcode 作为高度 UI 化的开发工具,经常出现调整一个配置就会导致 project.pbxproj 出现大量更改的情况,所以后面开始寻找一种更为官方的方式,来实现打包时动态替换 mobileprovisionbundleId

通过对比之前的 git diff 文件,可以看到改变还是有规律的,从 Automatic 到 Manual 指定 mobile provision 文件,主要变化的部分有:

  • 新增的 ProvisioningStyle 、 CODE_SIGN_IDENTITY、CODE_SIGN_STYLE 和 PROVISIONING_PROFILE_SPECIFIER 这几个更改;
  • 除了 ProvisioningStyle 之外,其他更改在 debug、profile、release 配置下都规律性出现变化;

首先解释下这几个配置:

  • ProvisioningStyle = Manual 表示了打包时采用手动签名的模式;
  • CODE_SIGN_IDENTITY 表示打包模式的 Inentity;
  • CODE_SIGN_STYLE 表示对应打包模式下的签名模式;
  • PROVISIONING_PROFILE_SPECIFIER 表示指定的 mobileprovision 的 name;
  • DEVELOPEMNT_TEAM 就是你开发者账号所在的 team Id;

所以到这里,可以考虑在打包时通过直接通过系统 sed 命令来实现动态调整,事实上 网上 还真有类似的建议,比如:

sed -i ‘’ ‘s/ProvisioningStyle = Automatic;/ProvisioningStyle = Manual;/’ MyProj.xcodeproj/project.pbxproj
sed -i ‘’ “s/DevelopmentTeam = ${DevelopmentTeamID};/DevelopmentTeam = \”\”;/” MyProj.xcodeproj/project.pbxproj
sed -i ‘’ “s/DEVELOPMENT_TEAM = ${DevelopmentTeamID};/DEVELOPMENT_TEAM = \”${TEAM_ID}\”;/” MyProj.xcodeproj/project.pbxproj

从这段脚本可以看到,就是通过 sed 去调整 ProvisioningStyle 和 DevelopmentTeam 等,但是这里有个问题,就是你的 project.pbxproj 不一定有 ProvisioningStyle 配置,因为如果是默认 automatically manage signing ,可能 project.pbxproj 文件下是没有这个参数。

但是 DevelopmentTeam 和 DEVELOPMENT_TEAM 一定是有,所以可以灵活变通一下,将命令改为

///改为 Manual

sed -i '' 's/DevelopmentTeam = 你的teamId;/DevelopmentTeam = 你的teamId;\nProvisioningStyle = Manual;/' ios/Runner.xcodeproj/project.pbxproj


/// option 1、改为 Manual 和指定 provision

sed -i '' 's/PRODUCT_BUNDLE_IDENTIFIER = 原来的bundleID;/PRODUCT_BUNDLE_IDENTIFIER = 需要替换的bundleId;\nCODE_SIGN_IDENTITY = "iPhone Distribution";\nCODE_SIGN_STYLE = Manual;\nPROVISIONING_PROFILE_SPECIFIER = "描述文件的name";/' ios/Runner.xcodeproj/project.pbxproj

///option 2、改为 Manual 和指定 provision,但是不需要修改 bundleId 的

sed -i '' 's/DEVELOPMENT_TEAM = 你的teamId;/DEVELOPMENT_TEAM = 你的teamId;\nCODE_SIGN_IDENTITY = "iPhone Distribution";\nCODE_SIGN_STYLE = Manual;\nPROVISIONING_PROFILE_SPECIFIER = "描述文件名字";/' ios/Runner.xcodeproj/project.pbxproj

运行后的结果就是在 DevelopmentTeam 和 DEVELOPMENT_TEAM 下添加对应所需的信息,从而达到指定 mobileprovision 和 Manual 签名的目的:

  • 需要替换 bundleId 的可以使用 PRODUCT_BUNDLE_IDENTIFIER 作为替换入口;
  • 不需要替换 bundleId 的可以使用 DEVELOPMENT_TEAM 作为替换入口;

最后提一句,这里构建的前提是,每次打包时 clone 一个全新的目录,构建成功后删除目录的过程,所以整个构建每次都是全新的,如果对于这部分内容感兴趣的,还可以详细参考以下资料:

《Flutter 搭建 iOS 命令行服务打包发布全保姆式流程》

《混编 Swift 遭遇动态库和静态库问题填坑》

最后不得不吐槽一句, Xcode 和 iOS 的在构建打包部分的资料真的少,这大概也是因为 Xcode 的高度 UI 化的贡献吧