Figment.so
Mac hostingWebsite hostingGuidesHow to usePricing

Send Slack Alerts for iOS CI Build Status

The Slack step runs after an iOS build, so it needs a Mac runner that can actually build and sign your app. For a typical Xcode 26 iOS pipeline, pick a Figment M-series Mac from $95/month (exact chip generation and RAM confirmed in the quote) at /mac-hosting/start, or Managed Build Mac at $249/month if you want Figment to set up CI, run backups and monitoring, and include 30 minutes of monthly support. The $29 Figment virtual Intel macOS runs Intel-compatible Xcode toolchains (Xcode 26 or earlier, per Apple's porting guide) and is a fine place to send notifications from Intel builds; choose M-series for Apple-silicon-only targets, GPU work, or higher RAM.

A useful notification carries four fields: status, branch, commit author, and artifact link. This guide delivers a reusable Fastlane lane built on the primary Fastlane slack action docs and a GitHub Actions–native alternative using Slack's documented incoming webhooks flow.

Price Check Beside the Recommendation

Before you wire this up, confirm the runner cost against your real term. Headline monthlies for M-class offers on 2026-11-10:

OfferMonthlyAnnual termNotes
Figment M-series (entry)$95not publishedexact chip/RAM in quote; physical supply request-based
Figment Managed Build Mac$249not publishedone M-series Mac, CI setup, backups, monitoring, 30 min support
CloudMac M4 / 16GB / 256GB$99.99$999/year ≈ $83.25/month256GB row shows "Reserve now"; 512GB/1TB show "Fully reserved"
MacStadium M4.S 16GB/256GB$149multi-year via sales99.99% uptime claim and included support per that page

CloudMac's $999/year is $141 less than twelve months of Figment's $95 entry. That is a real difference on hardware CloudMac specifies as M4/16GB; Figment's $95 is an unspecified M-series entry, not confirmed as M4 or 16GB. If a specific chip and RAM matter, request a Figment quote and compare the quoted configuration. Figment's differentiator is the managed CI work in the $249 tier, not a lower entry sticker.

Why Slack CI Notifications Reduce Context-Switching

Dashboard-first feedback forces developers to notice a badge, click to the CI URL, then read the log. For a team shipping twenty or more builds a day, that adds up. A pushed notification reverses the flow: the result arrives in the channel the team already watches. Signal quality matters more than volume: Build failed · feature/payment-refactor · alex · IPA: not produced is actionable; a bare red X is not.

Generate a Slack Incoming Webhook

Slack incoming webhooks are per-app and post to a fixed channel chosen at install time. Per Slack's docs, you cannot override the destination channel, username, or icon at post time—create one webhook per channel you want to reach.

  1. Open api.slack.com/apps, create (or reuse) an app.
  2. Under Features → Incoming Webhooks, toggle on.
  3. Click Add New Webhook to Workspace and pick the channel.
  4. Copy the URL (https://hooks.slack.com/services/T.../B.../XXX).

Store it in your CI secret manager (GitHub Actions: Settings → Secrets and variables → Actions, name SLACK_WEBHOOK_URL). Never paste the raw URL into a committed workflow or Fastfile—Slack revokes leaked webhooks.

Compose the Payload

Map five fields to GitHub Actions context:

FieldValueSource
Statussuccess / failurejob.status
Branchmain, feature/...github.ref_name
Commit authorGitHub usernamegithub.actor
Commit SHAshort hashgithub.sha (first 7)
Artifact linkworkflow run URL${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}

For TestFlight uploads, prefer App Store Connect's TestFlight page URL for the app and include the workflow run URL as a fallback so the message is still useful if the upload step failed.

The Reusable notify_slack Fastlane Lane

Add this lane to fastlane/Fastfile. It reads inputs from env vars so the same lane works in GitHub Actions, Bitrise, Buildkite, or Jenkins:

# fastlane/Fastfile (inside platform :ios do ... end)

lane :notify_slack do |options|
  build_ok = options.key?(:success) ? options[:success] :
             (ENV.fetch("FASTLANE_LANE_SUCCESS", "false") == "true")
  branch    = ENV.fetch("GITHUB_REF_NAME",  "unknown-branch")
  actor     = ENV.fetch("GITHUB_ACTOR",     "unknown")
  run_url   = ENV.fetch("WORKFLOW_RUN_URL", "")
  sha_short = ENV.fetch("GITHUB_SHA", "")[0, 7]

  status_emoji = build_ok ? ":white_check_mark:" : ":x:"
  status_text  = build_ok ? "Build succeeded"    : "Build FAILED"

  # Incoming webhooks post to the channel configured at install time;
  # do not pass :channel — it is ignored per Slack docs.
  slack(
    slack_url:        ENV.fetch("SLACK_WEBHOOK_URL"),
    message:          "#{status_emoji} *#{status_text}*",
    success:          build_ok,
    payload: {
      "Branch" => branch,
      "Author" => actor,
      "Commit" => sha_short,
      "Run"    => run_url
    },
    default_payloads: [],
    fail_on_error:    false
  )
end

Call it from your main lane with an ensure block so the alert fires even when a prior step raises:

lane :beta do
  build_ok = false
  begin
    match(type: "appstore", readonly: true)
    build_app(workspace: "ios/YourApp.xcworkspace", scheme: "YourApp")
    upload_to_testflight(skip_waiting_for_build_processing: true)
    build_ok = true
  ensure
    notify_slack(success: build_ok)
  end
end

Replace YourApp.xcworkspace and the scheme with your own project's values; do not run this against a scheme name you have not confirmed in xcodebuild -list.

Call the Lane from GitHub Actions

- name: Notify Slack
  if: always()
  run: bundle exec fastlane notify_slack
  env:
    SLACK_WEBHOOK_URL:     ${{ secrets.SLACK_WEBHOOK_URL }}
    GITHUB_REF_NAME:       ${{ github.ref_name }}
    GITHUB_ACTOR:          ${{ github.actor }}
    GITHUB_SHA:            ${{ github.sha }}
    WORKFLOW_RUN_URL:      ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
    FASTLANE_LANE_SUCCESS: ${{ job.status == 'success' && 'true' || 'false' }}

if: always() runs the step on both success and failure. The lane reads FASTLANE_LANE_SUCCESS when not called directly from Ruby.

Throttle: Ping @channel Only on Main Failures

A red build on main is urgent; a red build on a feature branch usually is not. Gate the broadcast mention on both conditions:

lane :notify_slack do |options|
  build_ok = options[:success]
  branch   = ENV.fetch("GITHUB_REF_NAME", "")
  mention  = (!build_ok && branch == "main") ? "<!channel> " : ""

  slack(
    slack_url: ENV.fetch("SLACK_WEBHOOK_URL"),
    message:   "#{mention}#{build_ok ? ':white_check_mark: Build succeeded' : ':x: Build FAILED'}",
    success:   build_ok,
    default_payloads: [],
    fail_on_error: false
  )
end

<!channel> is Slack's documented webhook syntax for a channel-wide mention; a literal @channel in a webhook payload does not notify.

GitHub Actions slack-send Alternative

Teams without Fastlane can post directly with slackapi/slack-github-action:

- name: Notify Slack (native)
  if: always()
  uses: slackapi/slack-github-action@v2
  with:
    webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
    webhook-type: incoming-webhook
    payload: |
      {
        "text": "${{ job.status == 'success' && ':white_check_mark: Build succeeded' || ':x: Build FAILED' }}",
        "attachments": [{
          "color": "${{ job.status == 'success' && 'good' || 'danger' }}",
          "fields": [
            { "title": "Branch", "value": "${{ github.ref_name }}", "short": true },
            { "title": "Author", "value": "${{ github.actor }}", "short": true },
            { "title": "Run",    "value": "<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View>", "short": false }
          ]
        }]
      }

No Ruby on the runner. Use Fastlane when you already have a Fastfile, because ensure fires on exceptions in one place.

Runner and Firewall Notes

The Slack HTTPS call is an outbound request to hooks.slack.com:443. On a Figment M-series runner nothing extra is needed. If your runner sits behind a corporate egress proxy, allow that host in your own firewall. Update any allowlists you actually control—your artifact store, your CI provider's IP list, your proxy—rather than reaching for a third-party IP range that does not apply here.

Set up a Mac runner at Figment Mac hosting and start the order at /mac-hosting/start. Tax and any setup fees apply as stated in your signed offer.

Related guides: GitHub-hosted vs self-hosted Mac runner costs · Speed up CI builds with a RAM disk on Mac · Remote Xcode setup on Windows


Subscribe for Figma design and website publishing guides.


Figment.so

Contact

Twitter

Website pricing

Compute index

About

Privacy

Terms