构建一个 iOS 应用并上传到 TestFlight
效果演示
- 提交一个
commit
到Github仓库
的main
分支.bashgit push
- 观察到
Xcode Cloud
自动开始构建应用, 并将构建结果发布到TestFlight
上.
前提条件
- 你的
Apple ID
已加入苹果开发者计划 - 一个
Github
账户
创建一个 Xcode 项目
创建一个新的Bundle ID
每一个苹果应用都有一个ID, 苹果称之为Bundle ID
. 这个ID可以在苹果开发者网站中申请, 具体的网址为 Apple Developer > Account > Certificates, IDs & Profiles > Identifiers.
创建一个App Store Connect App
想要在App Store
发布应用, 就必须用到 App Store Connect 网站. 之后像是什么更新应用, 添加TestFlight
测试者, 查看Xcode Cloud
日志, 全部都是在这个网站上操作的.
- 在 Apps 页面点击 New App.
- 填写 New App 表格. 你可能会问 SKU 怎么写, 其实可以随便写, 我一般直接填入
Bundle ID
.
创建Xcode
项目
- 打开
Xcode
, 创建一个新的项目. - 然后将此项目的
Bundle ID
修改为我们新创建的Bundle ID
. 修改位置在左侧导航栏
>app.xcodeproj
>Signing & Capabilities
>Signing
>Bundle Identifier
. - 修改
Bundle ID
后,Xcode
会自动获取Profile
. 点击Provisioning Profile
右边的i图标, 如果全部是打勾, 则说明获取成功. - 在 iOS 模拟器上运行该项目, 保证该项目可以正常运行.
手动构建并上传
首次上传
- 设置
Run Destination
为Any iOS Device
- 点击
顶部菜单栏
>Product
>Archive
以开始构建 - 在构建完成后一个名为
Archives
的窗口会自动弹出(该窗口亦可通过顶部菜单栏
>Window
>Organizer
打开). 选择刚刚生成的Archive
, 然后点击Distribute App
. 如此, 应用便开始上传到TestFlight
. - 当
Archive
上传完成后, 你就能在 App Store Connect > TestFlight 网页中找到你刚刚上传的项目了. - 在
TestFlight
中设置该版本的加密方式, 我一般选未加密. 如果不想每次都手动设置加密方式的话, 我们可以在Info.plist
中设置应用加密方法.xml<dict> ... <key>ITSAppUsesNonExemptEncryption</key> <false/> </dict>
- 在
TestFlight
中创建一个内部测试组
(INTERNAL TESTING). 把你的iPhone
上使用的Apple ID
加到组里, 然后你就能在你的注册Apple ID
所使用的邮箱中找到一封来自TestFlight
的邀请. 点击View in TestFlight
这将打开TestFlight
,TestFlight
会询问你是否接受邀请, 点击Accept
. 如此以后你就能在你的iPhone
的TestFlight
应用中找到测试应用, 测试应用更新时你的iPhone
会收到通知.
加大版本号
如果你想上传一个船新版本新的版本到TestFlight
, 那么你必须先到 左侧导航栏
> app.xcodeproj
> General
> Identity
加大应用的版本号. 因为 TestFlight
只接受比当前版本大的 Archive
.
Xcode Cloud 自动构建并上传
在设置完 Xcode Cloud workflow
后, 每次 Github仓库
的 main
分支变化时, Xcode Cloud
就会自动开始构建 Archive
, 完成后自动将其上传到 TestFlight
.
创建 Github 仓库
创建一个main
分支, 然后将这个分支上传Github仓库
.
创建 Xcode Cloud workflow
- 创建
workflow
的按钮在Xcode
>左侧导航栏
>最右边那个标签
>Cloud
>Get Started
- 创建过程中需要为
workflow
增加一个Archive
类型的Action
. 其中Distribution Preparation
需要选为App Store Connect
, 因为这个构建最终可能会被发布到App Store
- 此时如果我们向
Github仓库
的main
分支提交一个commit
, 应该就会触发Xcode Cloud
的自动构建了. 你可以在 App Store Connect > Xcode Cloud 观察构建过程.
构建完成后自动发布到 TestFlight
上述 workflow
配置了自动构建, 但是没有配置自动发布. 我们现在跳转到 App Store Connect > Xcode Cloud.
打开要编辑的 workflow
, 然后增加一个 Post-Action
, 类型为 TestFlight Internal Testing
. 选择你之前创建的测试组, 然后每次 Xcode Cloud
构建完成后就会自动发布到 TestFlight
了.
自动加大版本号
根据苹果的文档 Writing custom build scripts | Apple, Xcode Cloud
在构建 Archive
之前, 会运行项目文件夹下的 ci_scripts/ci_post_clone.sh
文件. 所以我们可以在该文件中运行一个js
脚本来加大版本号.
#!/bin/sh
# Note: Xcode will run ci_post_clone.sh at ci_scripts directory
export HOMEBREW_NO_INSTALL_CLEANUP=TRUE
# Install node start
brew install node
brew link node
# Install node end
# Change App info
node changeAppInfo.mjs
#!/usr/bin/env node
import fs from "node:fs/promises";
import path from "node:path";
import process from "node:process";
async function main() {
try {
// TODO: Replace config path. Note: Xcode will run ci_post_clone.sh at ci_scripts directory
const configPath = path.resolve('../app.xcodeproj/project.pbxproj')
await increaseVersion(configPath)
}
catch (error) {
console.error(error)
process.exit(1)
}
}
/**
* Increase version automatically in project.pbxproj file
* Build #26 1.2 -> 1.2.26
* Build #27 1.2 -> 1.2.27
*/
async function increaseVersion(configPath) {
const configText = await fs.readFile(configPath, { encoding: 'utf-8' })
print(`Changing ${configPath}`)
const regex = /MARKETING_VERSION = (.+?);/g
const versionMatch = configText.match(regex)
if (versionMatch === null) {
throw new Error(`Cant get iOS bundle version in ${configPath}, terminate build`)
}
const bundleVersion = versionMatch[0].replace('MARKETING_VERSION = ', '').replace(';', '')
const finalBundleVersion = `${bundleVersion}.${process.env.CI_BUILD_NUMBER}`
print(`Overwrite version: ${bundleVersion} -> ${finalBundleVersion}`)
const updatedConfigText = configText
.replace(regex, `MARKETING_VERSION = ${finalBundleVersion};`)
await fs.writeFile(configPath, updatedConfigText, { encoding: 'utf-8' })
}
function print(message) {
console.log(`[changeAppInfo] ${message}`)
}
main()
应用图标
使用Xcode Cloud
构建时, 应用图标不得有alpha通道
, 其实就是不能有透明的像素. 你可以通过将应用图标转换为jpeg
格式来解决这个问题, 因为jpeg
格式的图片没有alpha通道
.