단계 템플릿
CI/CD 파이프라인과 다단계 워크플로를 위해 설계된 단계 기반 템플릿입니다. 병렬 작업을 매트릭스 표시기로 시각화합니다.

Fields
| Field | Type | Description | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
template | string | Required. Must be "steps" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
progress | float | Required. Value between 0.0 and 1.0 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
current_step | integer | Required. The step currently running, counting from 1 (so 1 is the first entry of step_labels); 0 means nothing has started yet. Shown in the header as current_step/total_steps. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
total_steps | integer | Required. Total number of jobs in the workflow | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
step_rows | integer[] | Parallel jobs per step (e.g. [1,1,3,1] for a 3-job matrix at step 3). Length must equal total_steps. Each value must be between 1 and 10. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
step_labels | string[] | Optional labels for each step (e.g. ["Build","Test","Deploy"]). When provided and total_steps is 6 or fewer, labels appear below each segment bar. Length must equal total_steps. Each label max 32 characters. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
state | string | Current job name (e.g. "Build Container Image") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
icon | string | SF Symbol name or MDI icon with mdi: prefix (e.g. "mdi:source-branch") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
subtitle | string | Context subtitle (e.g. "repo / CI/CD") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
accent_color | string | Named color or hex | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
url | string | Primary URL -- tappable button on the Live Activity (e.g. link to workflow run) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
secondary_url | string | Secondary URL -- second tappable button (e.g. link to commit) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
background_color | string | Background color override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
text_color | string | Text color override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 앱 버전 1.3.3에서 제공 예정 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
step_weights | number[] | Gives each step a relative width. On its own it renders a single row of proportional segments (e.g. [1, 2, 1] makes the middle step twice as wide as its neighbours). Combined with step_rows it widths the matrix columns while each column keeps its fan-out. Length must equal total_steps. See Segmented layout. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
step_colors | string[] | Per-step colors for the segmented layout (named color or hex). Length must equal total_steps. An empty entry ("") falls back to accent_color for that segment, so you can tint just a few steps. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 앱 버전 1.3.5에서 제공 예정 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
live_progress | boolean | When true, iOS animates the current step toward completion and counts down an ETA, natively, between your pushes. Requires end_date (or duration); rejected with 422 without one. See Live progress. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
duration | integer | string | How long the current step takes: seconds (5400) or a duration string ("90m", "1h30m"). Sets start_date to now and end_date to now + duration, which is how you re-anchor the animation on each step change. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
end_date | integer | Unix timestamp when the current step is expected to finish -- not the end of the whole run. Wins over duration when both are sent. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
start_date | integer | Unix timestamp when the current step began. Set for you by duration; the bar fills across start_date to end_date. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 앱 버전 1.7.0에서 제공 예정 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
image_url | string | https URL of an image shown in place of the icon. Max 2048 characters, no username or password in the URL. The device downloads it, not the server, so the host has to be public. See Artwork. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
image_shape | string | How the image is framed: poster (2:3), square, or circle. Defaults to square. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
image_thumbhash | string | A ThumbHash of the image, as padded standard-alphabet base64 (about 28 characters). Rides on the push itself and renders as a blurred stand-in until the download lands. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 앱 버전 1.14.0에서 제공 예정 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
compact_label | string | Optional, max 4 characters. Replaces the ring in the Dynamic Island compact leading ear. The minimal presentation ignores it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
이 섹션은 App Store 심사를 기다리고 있는 앱 업데이트의 기능을 설명합니다. 업데이트가 출시되면 여기에서 자동으로 잠금 해제됩니다.
The subtitle renders under the activity name on the Lock Screen card and in the expanded Dynamic Island; the Apple Watch card shows it in place of the name. Builds before 1.9.2 show it only on the Apple Watch.
Renders url_action and secondary_url_action as buttons under the segment bar and background taps as tap_action; the legacy url / secondary_url strings still work. See Tap actions.
Changing total_steps on an existing activity
Updates are merge-patches -- an array you leave out of a PATCH keeps its stored value
(update semantics). When you reuse one slug across
runs and the new run changes total_steps, any carried-over array that no longer matches it
is dropped for you rather than failing the update; re-send the ones you want, and the rest come back
as the plain equal-width matrix. An array you do send still has to match: a length that
disagrees with total_steps in the same payload is rejected with 422.
Example Payload
{
"state": "ongoing",
"content": {
"template": "steps",
"progress": 0.375,
"state": "Build Container Image",
"icon": "arrow.triangle.branch",
"subtitle": "pushward-server / CI/CD",
"current_step": 2,
"total_steps": 8,
"step_rows": [1, 1, 3, 1, 1, 2, 1, 1],
"step_labels": ["Lint", "Build", "Test", "Scan", "Publish", "Deploy", "Verify", "Notify"],
"accent_color": "green"
}
}Step Rows
The step_rows array defines how many parallel jobs exist at each step, creating a matrix visualization -- the GitHub Actions bridge maps workflow jobs to steps this way:
// [1, 1, 3, 1, 1, 2, 1, 1]
// Step 1: ● (1 job)
// Step 2: ● (1 job)
// Step 3: ● ● ● (3 parallel jobs)
// Step 4: ● (1 job)
// Step 5: ● (1 job)
// Step 6: ● ● (2 parallel jobs)
// Step 7: ● (1 job)
// Step 8: ● (1 job)If step_rows is omitted, every step is treated as a single job. The counter still shows current_step/total_steps.
Segmented layout
이 섹션은 App Store 심사를 기다리고 있는 앱 업데이트의 기능을 설명합니다. 업데이트가 출시되면 여기에서 자동으로 잠금 해제됩니다.
Set step_weights to render the steps as a single row of proportional segments instead of the equal-width matrix. Each weight is that step's relative share of the bar width, so [1, 2, 1] gives a narrow-wide-narrow layout. This suits a linear process whose stages take different amounts of time (a wash cycle, a multi-part checkout) more than a CI matrix.
In this layout the shared progress field fills the current segment fractionally: completed steps are filled solid, the current_step segment fills to progress (0.0-1.0), and later steps stay empty. Pair it with step_colors to give each stage its own tint.
{
"state": "ongoing",
"content": {
"template": "steps",
"state": "Drying",
"icon": "dishwasher",
"subtitle": "Eco cycle",
"current_step": 3,
"total_steps": 3,
"progress": 0.5,
"step_labels": ["Wash", "Rinse", "Dry"],
"step_weights": [1, 2, 1],
"step_colors": ["blue", "teal", "orange"],
"accent_color": "teal"
}
}Weighted matrix
Send step_weights alongside step_rows to keep the parallel-jobs matrix but size each column by how long its step runs. Below, Build and Test dominate the width, Test fans out to three jobs, and each column is tinted by step_colors.
{
"state": "ongoing",
"content": {
"template": "steps",
"state": "Test",
"icon": "arrow.triangle.branch",
"subtitle": "my-app / CI/CD",
"current_step": 3,
"total_steps": 6,
"progress": 0.5,
"step_labels": ["Lint", "Build", "Test", "Scan", "Publish", "Deploy"],
"step_rows": [1, 1, 3, 1, 1, 1],
"step_weights": [15, 180, 90, 45, 20, 60],
"step_colors": ["purple", "blue", "yellow", "orange", "green", "green"],
"accent_color": "green"
}
}Live progress
이 섹션은 App Store 심사를 기다리고 있는 앱 업데이트의 기능을 설명합니다. 업데이트가 출시되면 여기에서 자동으로 잠금 해제됩니다.
If you know how long the current step takes -- a 90-minute wash cycle, a 4-minute test suite -- set live_progress: true and tell the server when that step ends: iOS fills the current step and counts an ETA down natively between your pushes. The shared mechanics -- how the animation anchors, how the anchors carry forward across updates, and how to clear them -- are on the generic template. What is specific to steps:
- The ETA describes the current step, not the whole run. Steps before it stay filled solid; steps after it stay empty.
- While it is counting, the ETA takes the header and the
current_step/total_stepscounter moves down beside the step name. Leavelive_progressoff and the counter keeps the header. - It works in every layout: the matrix column, the weighted column, and the segmented bar. In a fan-out column all the parallel jobs fill together, since they share one step deadline.
- If a step overruns its estimate without an update, its bar stops at full rather than racing ahead -- a stuck step never advances the counter on its own.
- Send
durationonly whencurrent_stepchanges -- that is what re-anchors the animation to the new step. Restamping the window on every update makes each one a high-priority push that skips update coalescing, and snaps the bar back to empty.
{
"state": "ongoing",
"content": {
"template": "steps",
"state": "Heating water",
"icon": "dishwasher",
"subtitle": "Eco 50",
"current_step": 2,
"total_steps": 4,
"step_labels": ["Pre-wash", "Wash", "Rinse", "Dry"],
"step_weights": [1, 6, 1, 2],
"live_progress": true,
"duration": "90m",
"accent_color": "teal"
}
}이 섹션은 App Store 심사를 기다리고 있는 앱 업데이트의 기능을 설명합니다. 업데이트가 출시되면 여기에서 자동으로 잠금 해제됩니다.
Artwork
Steps has the same image slot as the generic template: send image_url and the leading icon becomes artwork. On a step sequence that usually means the thing being processed rather than the tool doing the processing -- the film poster for a download that is grabbing, importing and renaming, the album sleeve for an import pipeline. The full rules -- reachability, the 2 MB limit, ThumbHash, shapes -- live on the generic page.
{
"state": "ongoing",
"content": {
"template": "steps",
"state": "Importing",
"subtitle": "Radarr",
"current_step": 3,
"total_steps": 4,
"step_labels": ["Grab", "Download", "Import", "Rename"],
"progress": 0.6,
"image_url": "https://image.example.com/posters/arrival.jpg",
"image_shape": "poster",
"image_thumbhash": "GYpSpSh4eHt4eHh4eHh4eHh4iIeH",
"accent_color": "purple"
}
}Radarr, Sonarr, Jellyfin and Overseerr do this automatically when you drive them through the Relay.