Documentation structure for LLMs (llms.txt)

Set up code signing for iOS and macOS projects

Cloud

This document describes how to set up code signing for your iOS and macOS projects on CircleCI. The recommended method is the CircleCI code signing API. You upload your Apple signing certificates and provisioning profiles to CircleCI, and it installs them onto the macOS executor at build time. This method works for both iOS and macOS.

If you would rather store and manage your certificates yourself, or you already use it, Fastlane Match is available as an alternative.

CircleCI supports the code signing API (recommended) and Fastlane Match. Other methods may be used, but are not guaranteed to work and are unsupported.

Code signing with the CircleCI API

With the CircleCI code signing API you upload your Apple signing certificate (.p12) and any required provisioning profiles once, then reference the resulting signing bundle by name in your job. At build time, CircleCI installs the certificate and profiles into a temporary keychain on the macOS executor and removes them when the job finishes. The same endpoints and configuration are used for both iOS and macOS.

Prerequisites

  • Apple assets: Your .p12 certificate file and its password, and your provisioning profiles (if required for your certificate type, see Supported certificate types).

  • CircleCI API token: A personal API token to authenticate your requests. See Managing API Tokens.

  • Organization ID: Your CircleCI organization ID. See How to Find IDs.

1. Upload a certificate

Send a POST request to upload your certificate.

You will need to provide the following:

  • Your personal API token.

  • <org-id>: Your CircleCI organization ID. See How to Find IDs.

  • <.p12-base64-encoded-blob>: The raw binary content of your .p12 certificate file converted into a Base64-encoded string. On macOS, generate it with base64 -i <.p12-file> -o -.

  • <.p12-file>: This is the filename of your Apple signing certificate (iOS or macOS), exported from the Keychain Access app on macOS as a .p12 file. Max 40 characters.

  • <your-cert-password>: The password created when the .p12 file was exported from Keychain Access.

curl -X POST https://circleci.com/api/v2/certificates \
  -H "Circle-Token: <your-circleci-api-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "org_id": "<org-id>",
    "cert_file_name": "<.p12-file>",
    "cert_blob": "<.p12-base64-encoded-blob>",
    "cert_password": "<your-cert-password>"
  }'

The response returns the certificate ID. The certificate ID is the unique identifier for a certificate, returned in the API response after a successful certificate upload:

{"id":"<cert-id>"}

2. Create a signing bundle

A signing bundle maps a certificate to its provisioning profiles. Send a POST request, using the following:

  • Your personal API token.

  • <signing-bundle-name>: A descriptive name for this set of credentials (for example, production-signing) that you reference in your configuration. Unique per organization, maximum 50 characters, letters, numbers, and hyphens only.

  • <org-id>: Your CircleCI organization ID. See How to Find IDs.

  • cert-id-from-previous-request: The cert-id returned in the previous step.

  • Each provisioning profile’s filename and blob. The blob is the raw binary content of the profile file converted into a Base64-encoded string. On macOS, generate it with base64 -i <provisioning-profile-file> -o -. The filename is the filename of the Apple provisioning profile associated with the app’s bundle ID. Use a .mobileprovision file for iOS, or a .provisionprofile file for macOS, usually downloaded from the Apple Developer Portal. Maximum 40 characters.

  • A maximum of 100 profiles per bundle.

curl -X POST https://circleci.com/api/v2/signing-configs \
  -H "Circle-Token: <your-circleci-api-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "<signing-bundle-name>",
    "org_id": "<org-id>",
    "cert_id": "<cert-id-from-previous-request>",
    "provisioning_profiles": [
      {
        "file_name": "<provisioning-profile-file>",
        "blob": "<provisioning-profile-base64-blob>"
      }
    ]
  }'

The response returns the signing bundle ID. You will need the signing bundle ID when managing your assets, for example deleting a signing bundle. See View and delete certificates and signing bundles for more information:

{"id":"<signing-bundle-id>"}

3. Reference the signing bundle in your configuration

Add a code_signing key to your macos executor listing the signing bundles to install, and add the install_signing_bundle step before you build. CircleCI installs the referenced bundles onto the executor.

  • <signing-bundle-name>: A descriptive name for this set of credentials (for example, production-signing) that you reference in your configuration. Unique per organization, maximum 50 characters, letters, numbers, and hyphens only.

  • <other-signing-bundle-name>: An optional secondary signing configuration name (for example, staging-signing) if your job requires more than one certificate or set of profiles.

  • <your-xcode-scheme>: The name of the specific build scheme you want to archive (for example, MyApp or Runner). Found in Xcode under Product > Scheme, or by running xcodebuild -list.

version: 2.1

jobs:
  build-and-sign:
    macos:
      xcode: 26.6.0
      # List the signing bundles to install from your organization
      code_signing:
        - <signing-bundle-name>
        # Add more bundles as needed
        - <other-signing-bundle-name>
    steps:
      # Installs the certificates and profiles from the bundles above
      - install_signing_bundle
      - checkout
      - run:
          name: Build and archive
          command: xcodebuild archive -scheme <your-xcode-scheme> -configuration Release

What happens during the build

When the install_signing_bundle step runs, CircleCI:

  • Creates a temporary keychain on the executor.

  • Imports the certificate and installs the provisioning profiles into the correct locations.

  • Configures the keychain so Xcode can use the signing key without a password prompt.

  • Removes the keychain and profiles when the job completes.

Supported certificate types

The certificate’s Subject Common Name must begin with one of the following prefixes, or the upload is rejected. Whether a signing bundle requires provisioning profiles depends on the certificate type.

Common Name prefix Platform Provisioning profiles

Apple Distribution:, Apple Development:

iOS and macOS

At least one required

iPhone Distribution:, iPhone Developer:

iOS

At least one required

3rd Party Mac Developer Application: (Mac App Store)

macOS

At least one required

Mac Developer:

macOS

At least one required

Developer ID Application:, Developer ID Installer:

macOS

None (profiles are rejected)

3rd Party Mac Developer Installer:

macOS

None (profiles are rejected)

View and delete certificates and signing bundles

You can list the certificates stored for your organization, and the signing bundles created for your organization. You can also delete a certificate or signing bundle.

List the certificates stored for your organization
curl -H "Circle-Token: <your-circleci-api-token>" \
  "https://circleci.com/api/v2/certificates?org-id=<org-id>"
List the signing bundles created for your organization
curl -H "Circle-Token: <your-circleci-api-token>" \
  "https://circleci.com/api/v2/signing-configs?org-id=<org-id>"
Delete a certificate. Deletion fails with an HTTP 409 response if any signing bundle still references the certificate, so delete or update those bundles first.
curl -X DELETE -H "Circle-Token: <your-circleci-api-token>" \
  https://circleci.com/api/v2/certificates/<cert-id>
Delete a signing bundle
curl -X DELETE -H "Circle-Token: <your-circleci-api-token>" \
  https://circleci.com/api/v2/signing-configs/<signing-bundle-id>

Alternative: code signing with Fastlane Match

If you prefer to store and manage your own certificates and provisioning profiles, you can use Fastlane Match instead of the CircleCI code signing API. The following sections describe the Fastlane Match setup for iOS projects.

Basic configuration of iOS projects

This document assumes that you already have an iOS project building correctly on CircleCI using our recommended best practices. It also assumes that you use Bundler and Fastlane, and have a Gemfile, Appfile, and Fastfile checked into your repository.

If you have not yet configured your iOS project on CircleCI, you can find the configuration instructions in the Testing iOS Applications Document.

Set up Fastlane Match

Code signing must be configured to generate ad-hoc distributions of your app and App Store builds.

Fastlane Match is one of the Fastlane tools. It allows for seamless configuration for code signing in both your local development environment and on CircleCI. Fastlane Match stores all of your code signing certificates and provisioning profiles in a git repository/AWS S3 Bucket/Google Cloud Storage. It downloads and installs the necessary certificates and profiles when required.

In this example configuration, we will set up and use a git repository for storage.

To set up Fastlane Match:

  1. On your local machine, open Terminal and navigate to the root directory of your repository.

  2. Run bundle exec fastlane match init.

  3. Follow the instructions to configure the Match repository.

  4. After the above is complete, run bundle exec fastlane match development to generate and install the Development certificates and profiles.

  5. Then, run bundle exec fastlane match adhoc to generate and install the Ad-hoc distribution certificates and profiles.

Prepare your Xcode project for use with Fastlane Match

Before setting up Match you must ensure that the code signing settings in your Xcode project are configured as follows:

  • Signing & Capabilities -> Signing uncheck Automatically manage signing for both Debug and Release.

  • Signing & Capabilities -> Provisioning Profile choose the appropriate profile created by Fastlane Match (for example, match adhoc com.circleci.helloworld).

Add Match to the Fastlane lane

On CircleCI, Fastlane Match will need to be run every time you build and sign your app. The easiest way to do this is to add the match action to the lane which builds your app.

For the match action to work correctly, you must add setup_circle_ci to before_all in your Fastfile. This ensures that a temporary Fastlane keychain with full permissions is used. Without using this you may see build failures or inconsistent results.
# fastlane/Fastfile
default_platform :ios

platform :ios do
  before_all do
    setup_circle_ci
  end

  desc "Build and run tests"
  lane :test do
    scan
  end

  desc "Ad-hoc build"
  lane :adhoc do
    match(type: "adhoc")
    gym(export_method: "ad-hoc")
  end
  ...
end

Add a user key to the CircleCI project

To enable Fastlane Match to download the certificates and the keys from GitHub, add a user key to the CircleCI project. This key needs access to both the project repository and the certificates / keys repository.

To add a user key:

  1. In the CircleCI web app, select your org from the org cards on your user homepage.

  2. Select Projects from the sidebar and locate your project from the list. You can use the search to help.

  3. Select the ellipsis Ellipsis menu iconEllipsis menu icon next to your project and select Project Settings.

    You can also access project settings from each project overview page using the Settings button.
  1. In the sidebar menu, select SSH Keys.

  2. Under the User Key section, select Authorize With GitHub.

  3. After authorizing, navigate to the SSH keys page again, go to the User Key section, and select Add User Key, then Confirm User.

This action will give the CircleCI project the same GitHub permissions as the user who will be clicking the Authorize with GitHub button.

In your Matchfile, the git_url must be an SSH URL (in the git@github.com:... format), rather than an HTTPS URL. Otherwise you may see authentication errors when you attempt to use match. For example:

git_url("git@github.com:fastlane/certificates")
app_identifier("tools.fastlane.app")
username("user@fastlane.tools")

It is best practice to create a machine user with access to just the project repository and the keys repository. Use that machine user to create a user key to reduce the level of GitHub access granted to the CircleCI project.

After you have added a user key, CircleCI will be able to checkout both the project repository and the Fastlane Match repository from GitHub.

Add the Match passphrase to the project

To enable Fastlane Match to decrypt the certificates and profiles stored in the GitHub repository, add the encryption passphrase to the CircleCI project’s environment variables. Use the passphrase that you configured in the Match setup step.

In the project settings on CircleCI, click on Environment Variables and add the MATCH_PASSWORD variable. Set its value to your encryption passphrase. The passphrase will be stored encrypted at rest.

Build and code-sign the app on CircleCI

After you have configured Match and added its invocation into the appropriate lane, you can run that lane on CircleCI. The following config.yml will create an Ad-hoc build every time you push to the development branch:

# .circleci/config.yml
version: 2.1
jobs:
  build-and-test:
    macos:
      xcode: 26.6.0
    steps:
      # ...
      - run: bundle exec fastlane test

  adhoc:
    macos:
      xcode: 26.6.0
    steps:
      # ...
      - run: bundle exec fastlane adhoc

workflows:
  build-test-adhoc:
    jobs:
      - build-and-test
      - adhoc:
          filters: pipeline.git.branch == "development" # only run the adhoc job on the development branch
          requires:
            - build-and-test

Sample configuration files

The best practice configuration for setting up code signing for iOS projects is as follows:

# fastlane/Fastfile
default_platform :ios

platform :ios do
  before_all do
    setup_circle_ci
  end

  desc "Runs all the tests"
  lane :test do
    scan
  end

  desc "Ad-hoc build"
  lane :adhoc do
    match(type: "adhoc")
    gym(export_method: "ad-hoc")
  end
end
# .circleci/config.yml
version: 2.1
jobs:
  build-and-test:
    macos:
      xcode: 26.6.0
    environment:
      FL_OUTPUT_DIR: output
      FASTLANE_LANE: test
    steps:
      - checkout
      - run: bundle install
      - run:
          name: Fastlane
          command: bundle exec fastlane $FASTLANE_LANE
      - store_artifacts:
          path: output
      - store_test_results:
          path: output/scan

  adhoc:
    macos:
      xcode: 26.6.0
    environment:
      FL_OUTPUT_DIR: output
      FASTLANE_LANE: adhoc
    steps:
      - checkout
      - run: bundle install
      - run:
          name: Fastlane
          command: bundle exec fastlane $FASTLANE_LANE
      - store_artifacts:
          path: output

workflows:
  build-test-adhoc:
    jobs:
      - build-and-test
      - adhoc:
          filters: pipeline.git.branch == "development" # only run the adhoc job on the development branch
          requires:
            - build-and-test

By setting FL_OUTPUT_DIR:, that will tell Fastlane to output the Xcode and Fastlane logs to that directory, so they get uploaded as artifacts for ease in troubleshooting.

Example application on GitHub

See the circleci-demo-ios GitHub repository for an example of how to configure code signing for iOS apps using Fastlane Match.