Terminal window
base64 -i BUILD_CERTIFICATE.p12 | pbcopy
\n\n\n\n
  • \n

    The password for your Apple signing certificate.

    \n\n
  • \n
  • \n

    Your Apple provisioning profile.

    \n\n
  • \n\n

    2. Copy Fastline files

    \n

    Fastlane is a Ruby library created to automate common mobile development tasks. Using Fastlane, you can configure custom “lanes” which bundle a series of “actions” that perform tasks that you’d normally perform using Android studio. You can do a lot with Fastlane, but for the purposes of this tutorial, we’ll be using only a handful of core actions.

    \n

    Create a Fastlane folder at the root of your project and copy the following files:\nFastfile

    \n
    platform :ios do
    desc 'Export ipa and submit to TestFlight'
    lane :beta do
    keychain_info = { keychain_name: \"ios-build-#{Time.now.to_i}.keychain\", keychain_password: SecureRandom.uuid }
    \n
    begin
    setup_signing(keychain_info)
    bump_build_number
    build_app_with_signing(keychain_info)
    submit_to_testflight
    ensure
    cleanup_keychain(keychain_info)
    end
    end
    \n
    private_lane :setup_signing do |options|
    create_keychain(
    name: options[:keychain_name],
    password: options[:keychain_password],
    unlock: true,
    timeout: 0,
    lock_when_sleeps: false,
    add_to_search_list: true
    )
    import_cert(options)
    install_profile
    update_project_settings
    end
    \n
    lane :bump_build_number do
    file = File.read('../package.json')
    data_hash = JSON.parse(file)
    api_key = app_store_connect_api_key(
    key_id: ENV['APPLE_KEY_ID'],
    issuer_id: ENV['APPLE_ISSUER_ID'],
    key_content: Base64.decode64(ENV['APPLE_KEY_CONTENT']),
    duration: 1200,
    in_house: false
    )
    build_num = app_store_build_number(
    api_key: api_key,
    app_identifier: ENV['BUNDLE_IDENTIFIER'],
    live: false
    )
    build_num = build_num + 1
    UI.message(\"Bumped build number to #{build_num}\")
    increment_build_number(
    build_number: build_num,
    xcodeproj: \"./ios/App/App.xcodeproj\",
    skip_info_plist: true
    )
    end
    \n
    private_lane :import_cert do |options|
    cert_path = \"#{Dir.tmpdir}/build_certificate.p12\"
    File.write(cert_path, Base64.decode64(ENV['BUILD_CERTIFICATE_BASE64']))
    import_certificate(
    certificate_path: cert_path,
    certificate_password: ENV['P12_PASSWORD'] || \"\",
    keychain_name: options[:keychain_name],
    keychain_password: options[:keychain_password],
    log_output: true
    )
    File.delete(cert_path)
    end
    \n
    private_lane :cleanup_keychain do |options|
    delete_keychain(
    name: options[:keychain_name]
    )
    end
    \n
    private_lane :install_profile do
    profile_path = \"#{Dir.tmpdir}/build_pp.mobileprovision\"
    File.write(profile_path, Base64.decode64(ENV['BUILD_PROVISION_PROFILE_BASE64']))
    UI.user_error!(\"Failed to create provisioning profile at #{profile_path}\") unless File.exist?(profile_path)
    ENV['PROVISIONING_PROFILE_PATH'] = profile_path
    install_provisioning_profile(path: profile_path)
    File.delete(profile_path)
    end
    \n
    private_lane :update_project_settings do
    update_code_signing_settings(
    use_automatic_signing: false,
    path: \"./ios/App/App.xcodeproj\",
    code_sign_identity: \"iPhone Distribution\",
    profile_name: ENV['APPLE_PROFILE_NAME'],
    bundle_identifier: ENV['BUNDLE_IDENTIFIER'],
    team_id: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
    update_project_team(
    path: \"./ios/App/App.xcodeproj\",
    teamid: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
    end
    \n
    private_lane :build_app_with_signing do |options|
    unlock_keychain(
    path: options[:keychain_name],
    password: options[:keychain_password],
    set_default: false
    )
    build_app(
    workspace: \"./ios/App/App.xcworkspace\",
    scheme: \"App\",
    configuration: \"Release\",
    export_method: \"app-store\",
    output_name: \"App.ipa\",
    export_options: {
    provisioningProfiles: {
    ENV['BUNDLE_IDENTIFIER'] => ENV['APPLE_PROFILE_NAME']
    }
    },
    xcargs: \"-verbose\",
    buildlog_path: \"./build_logs\",
    export_xcargs: \"-allowProvisioningUpdates\",
    )
    end
    \n
    private_lane :submit_to_testflight do
    api_key = app_store_connect_api_key(
    key_id: ENV['APPLE_KEY_ID'],
    issuer_id: ENV['APPLE_ISSUER_ID'],
    key_content: Base64.decode64(ENV['APPLE_KEY_CONTENT']),
    duration: 1200,
    in_house: false
    )
    pilot(
    api_key: api_key,
    skip_waiting_for_build_processing: true,
    skip_submission: true,
    distribute_external: false,
    notify_external_testers: false,
    ipa: \"./App.ipa\"
    )
    end
    end
    \n

    Build Processing

    \n

    In GitHub Actions, you are billed based on the minutes you have used for running your CI/CD workflow. From experience, it takes about 10–15 minutes before a build can be processed in App Store Connect.

    \n

    For private projects, the estimated cost per build can go up to $0.08/min x 15 mins = $1.2, or more, depending on the configuration or dependencies of your project.

    \n

    If you share the same concerns for the pricing as I do for private projects, you can keep the skip_waiting_for_build_processing to true.

    \n

    What’s the catch? You have to manually update the compliance of your app in App Store Connect after the build has been processed, for you to distribute the build to your users.

    \n

    This is just an optional parameter to update if you want to save on the build minutes for private projects. For free projects, this shouldn’t be a problem at all. See pricing.

    \n

    3. Setup GitHub Actions

    \n

    Configure GitHub secrets

    \n

    Ever wonder where the values of the ENV are coming from? Well, it’s not a secret anymore – it’s from your project’s secret. 🤦

    \n

    \"Set

    \n

    1. APP_STORE_CONNECT_TEAM_ID - the ID of your App Store Connect team in you’re in multiple teams.

    \n

    2. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>, eg. match AppStore com.domain.blabla.demo.

    \n

    3. BUILD_CERTIFICATE_BASE64 - Base64 encoded certificate.

    \n

    4. BUILD_PROVISION_PROFILE_BASE64 - Base64 encoded provisioning profile.

    \n

    5. BUNDLE_IDENTIFIER - your app’s bundle identifier.

    \n

    6. APPLE_KEY_ID — App Store Connect API Key 🔺Key ID.

    \n

    7. APPLE_ISSUER_ID — App Store Connect API Key 🔺Issuer ID.

    \n

    8. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Key content of .p8, check it

    \n

    4. Configure GitHub workflow file

    \n

    Create a GitHub workflow directory.

    \n
    cd .github/workflows
    \n

    Inside the workflow folder, create a file named build-upload-ios.ymland add the following.

    \n
    name: Build source code on ios
    \n
    on:
    push:
    tags:
    - '*'
    \n
    jobs:
    build_ios:
    runs-on: macOS-latest
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js 20
    uses: actions/setup-node@v3
    with:
    node-version: 20
    cache: npm
    - name: Install dependencies
    id: install_code
    run: npm ci
    - name: Build
    id: build_code
    run: npm run build
    - name: Build
    id: build_code
    run: npm run mobile
    - uses: actions/cache@v3
    with:
    path: ios/App/Pods
    key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
    restore-keys: |
    ${{ runner.os }}-pods-
    - name: Sync
    id: sync_code
    run: npx cap sync
    - uses: ruby/setup-ruby@v1
    with:
    ruby-version: '3.0'
    bundler-cache: true
    - uses: maierj/fastlane-action@v3.1.0
    env:
    APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
    BUNDLE_IDENTIFIER: ${{ secrets.BUNDLE_IDENTIFIER }}
    BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
    BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
    APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
    APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
    APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
    APPLE_PROFILE_NAME: ${{ secrets.APPLE_PROFILE_NAME }}
    with:
    lane: ios beta
    - name: Upload release bundle
    uses: actions/upload-artifact@v4
    with:
    name: ios-release
    path: ./App.ipa
    retention-days: 10
    \n

    This workflow should be triggered after each GitHub tag, if you need to automatize tag please, refer to Automatic build and release with GitHub actions first.

    \n

    Then this workflow will pull your NodeJS deps, install them and build your JavaScript app.

    \n
    \n

    Each time you send a new commit, a release will be built in TestFlight.

    \n
    \n

    Your App doesn’t need to use Ionic, only Capacitor base is mandatory., it can have old Cordova module, but Capacitor JS plugin should be preferred.

    \n

    5. Trigger workflow

    \n

    Create a Commit

    \n

    Make a commit, you should see the active workflow in the repository.

    \n

    Trigger the workflow

    \n

    Push the new commits to the branch main or developement to trigger the workflow.

    \n

    \"Started

    \n

    After a few minutes, the build should be available in your App Store Connect dashboard.

    \n

    \"Testflight

    \n

    Can deploy from local machine?

    \n

    Yes, you can, and it is effortless.

    \n

    You can use Xcode to build and sign your app, as always.

    \n

    Thanks

    \n

    This blog is based on the following articles:

    \n"}
    article illustration Automatic Capacitor IOS build with GitHub actions with certificate
    CI/CD
    Last update: August 04, 2024

    Automatic Capacitor IOS build with GitHub actions with certificate

    How to set up a CI/CD pipeline for your IOS Ionic app using fastlane and GitHub Actions in 5 mins (2024)

    Continuous Delivery for iOS using Fastlane and GitHub Actions and certificate

    Prerequisites

    Before continuing with the tutorial…

    • Make sure you have Fastlane installed on your development machine.
    • iOS developer program membership.
    • Desire to read 😆…

    Important about the price

    Price GitHub Action

    https://github.com/features/actions

    The service is ‘free’ up to the limit, depending on the chosen machine.
    We are going to use a macOS machine, you can see in the screenshot its price and limits (prices as of the creation of the tutorial, they could undergo changes in the future)

    🔴 Once warned of requirements and prices, if you like, we continue…

    📣 In the post we assume that we have the app created in iTunes connect, we do have the certificates of the Apple ecosystem, everything will be copied by Fastlane!

    Let’s go to the mess 🧑🏽💻

    Steps to follow in the post

    1. Using App Store Connect API with Fastlane
    2. Requirements
    3. Creating an App Store Connect API Key
    4. Using an App Store Connect API Key
    5. Copy Fastline files
    6. Configure GitHub Actions

    1. Using App Store Connect API with Fastlane

    Starting February 2021, two-factor authentication or two-step verification is required for all users to sign in to App Store Connect. This extra layer of security for your Apple ID helps ensure that you’re the only person who can access your account.
    From Apple Support

    Requirements

    To be able to use App Store Connect API, Fastlane needs three things:

    1. Issuer ID
    2. Key ID
    3. Key file or Key content

    Creating an App Store Connect API Key

    To generate keys, you must have Admin permission in App Store Connect. If you don’t have that permission, you can direct the relevant person to this article and follow the following instructions.

    1 — Log in to App Store Connect.

    2 — Select Users and Access.

    App Store Connect user access

    3 — Select the API Keys tab.

    App Store Connect API Keys

    4 — Click Generate API Key or the Add (+) button.

    App Store Connect API keys create

    5 — Enter a name for the key. The name is for your reference only and is not part of the key itself.

    App Store Connect API keys create name

    6 — Under Access, select the role for the key. The roles that apply to keys are the same roles that apply to users on your team. See role permissions. We recommend to select App management.

    7 — Click Generate.

    An API key’s access cannot be limited to specific apps.

    The new key’s name, key ID, a download link, and other information appear on the page.

    App Store Connect download keys

    You can grab all three necessary information here.
    <1> Issue ID.
    <2> Key ID.
    <3> Click “Download API Key” to download your API private key. The download link appears only if the private key has not yet been downloaded. Apple does not keep a copy of the private key. So, you can download it only once.

    🔴 Store your private key in a safe place. You should never share your keys, store keys in a code repository, or include keys in client-side code.

    Using an App Store Connect API Key

    The API Key file (p8 file that you download), the key ID, and the issuer ID are needed to create the JWT token for authorization. There are multiple ways that these pieces of information can be input into Fastlane using Fastlane’s new action, app_store_connect_api_key. You can learn other ways in Fastlane documentation. I show this method because I think it is the easiest way to work with most CI out there, where you can set environment variables.

    Now we can manage Fastlane with the App Store Connect API key, great!

    Create certificates and provisioning profiles

    Certificates

    Open XCode and go to Settings > Accounts > Apple ID > Teams and select your team.

    Code signing identities

    Click on Manage certificates > + and select Apple Distribution.

    Apple Distribution

    Then you can create a new certificate.

    Then you need to go to keychain to download the certificate as a .p12 file.

    To do so, you need to go to keychain switch to the login keychain and then the tab My Certificates.

    My Certificates

    Then you can select the certificate you want to download. (Look by the date of the certificate)

    And then right-click on the certificate and select Export.

    Choose the file format Personal Information Exchange (.p12).

    That will download the certificate as a .p12 file.

    Provisioning profiles

    Open Apple Developer and select the right team.

    Then create a new profile, by clicking on +

    Create a new profile

    And select App Store Connect.

    Select App Store Connect

    Then you need to select the right app, be careful you cannot use wildcard otherwise signing will fail.

    Select the right app

    Select the right certificate you created before (look for the date of expiration it should same day and month as today) and click on Continue.

    Select the right certificate

    Finally enter the name of the profile and click on Generate.

    The name will be used to identify the profile in Fastlane, under the value of APPLE_PROFILE_NAME.

    Generate the profile

    You can download the profile as a .mobileprovision file.

    Download the profile

    Creating GitHub secrets for your certificate and provisioning profile

    The signing process involves storing certificates and provisioning profiles, transferring them to the runner, importing them to the runner’s keychain, and using them in your build.

    Create secrets in your repository or organization for the following items:

    • Your Apple signing certificate.

      • This is your p12 certificate file. For more information on exporting your signing certificate from Xcode, see the Xcode documentation.

      • You should convert your certificate to Base64 when saving it as a secret. In this example, the secret is named BUILD_CERTIFICATE_BASE64.

      • Use the following command to convert your certificate to Base64 and copy it to your clipboard:

        Terminal window
        base64 -i BUILD_CERTIFICATE.p12 | pbcopy
    • The password for your Apple signing certificate.

      • In this example, the secret is named P12_PASSWORD.
    • Your Apple provisioning profile.

      • For more information on exporting your provisioning profile from Xcode, see the Xcode documentation.

      • You should convert your provisioning profile to Base64 when saving it as a secret. In this example, the secret is named BUILD_PROVISION_PROFILE_BASE64.

      • Use the following command to convert your provisioning profile to Base64 and copy it to your clipboard:

        Terminal window
        base64 -i PROVISIONING_PROFILE.mobileprovision | pbcopy

    2. Copy Fastline files

    Fastlane is a Ruby library created to automate common mobile development tasks. Using Fastlane, you can configure custom “lanes” which bundle a series of “actions” that perform tasks that you’d normally perform using Android studio. You can do a lot with Fastlane, but for the purposes of this tutorial, we’ll be using only a handful of core actions.

    Create a Fastlane folder at the root of your project and copy the following files: Fastfile

    platform :ios do
    desc 'Export ipa and submit to TestFlight'
    lane :beta do
    keychain_info = { keychain_name: "ios-build-#{Time.now.to_i}.keychain", keychain_password: SecureRandom.uuid }
    begin
    setup_signing(keychain_info)
    bump_build_number
    build_app_with_signing(keychain_info)
    submit_to_testflight
    ensure
    cleanup_keychain(keychain_info)
    end
    end
    private_lane :setup_signing do |options|
    create_keychain(
    name: options[:keychain_name],
    password: options[:keychain_password],
    unlock: true,
    timeout: 0,
    lock_when_sleeps: false,
    add_to_search_list: true
    )
    import_cert(options)
    install_profile
    update_project_settings
    end
    lane :bump_build_number do
    file = File.read('../package.json')
    data_hash = JSON.parse(file)
    api_key = app_store_connect_api_key(
    key_id: ENV['APPLE_KEY_ID'],
    issuer_id: ENV['APPLE_ISSUER_ID'],
    key_content: Base64.decode64(ENV['APPLE_KEY_CONTENT']),
    duration: 1200,
    in_house: false
    )
    build_num = app_store_build_number(
    api_key: api_key,
    app_identifier: ENV['BUNDLE_IDENTIFIER'],
    live: false
    )
    build_num = build_num + 1
    UI.message("Bumped build number to #{build_num}")
    increment_build_number(
    build_number: build_num,
    xcodeproj: "./ios/App/App.xcodeproj",
    skip_info_plist: true
    )
    end
    private_lane :import_cert do |options|
    cert_path = "#{Dir.tmpdir}/build_certificate.p12"
    File.write(cert_path, Base64.decode64(ENV['BUILD_CERTIFICATE_BASE64']))
    import_certificate(
    certificate_path: cert_path,
    certificate_password: ENV['P12_PASSWORD'] || "",
    keychain_name: options[:keychain_name],
    keychain_password: options[:keychain_password],
    log_output: true
    )
    File.delete(cert_path)
    end
    private_lane :cleanup_keychain do |options|
    delete_keychain(
    name: options[:keychain_name]
    )
    end
    private_lane :install_profile do
    profile_path = "#{Dir.tmpdir}/build_pp.mobileprovision"
    File.write(profile_path, Base64.decode64(ENV['BUILD_PROVISION_PROFILE_BASE64']))
    UI.user_error!("Failed to create provisioning profile at #{profile_path}") unless File.exist?(profile_path)
    ENV['PROVISIONING_PROFILE_PATH'] = profile_path
    install_provisioning_profile(path: profile_path)
    File.delete(profile_path)
    end
    private_lane :update_project_settings do
    update_code_signing_settings(
    use_automatic_signing: false,
    path: "./ios/App/App.xcodeproj",
    code_sign_identity: "iPhone Distribution",
    profile_name: ENV['APPLE_PROFILE_NAME'],
    bundle_identifier: ENV['BUNDLE_IDENTIFIER'],
    team_id: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
    update_project_team(
    path: "./ios/App/App.xcodeproj",
    teamid: ENV['APP_STORE_CONNECT_TEAM_ID']
    )
    end
    private_lane :build_app_with_signing do |options|
    unlock_keychain(
    path: options[:keychain_name],
    password: options[:keychain_password],
    set_default: false
    )
    build_app(
    workspace: "./ios/App/App.xcworkspace",
    scheme: "App",
    configuration: "Release",
    export_method: "app-store",
    output_name: "App.ipa",
    export_options: {
    provisioningProfiles: {
    ENV['BUNDLE_IDENTIFIER'] => ENV['APPLE_PROFILE_NAME']
    }
    },
    xcargs: "-verbose",
    buildlog_path: "./build_logs",
    export_xcargs: "-allowProvisioningUpdates",
    )
    end
    private_lane :submit_to_testflight do
    api_key = app_store_connect_api_key(
    key_id: ENV['APPLE_KEY_ID'],
    issuer_id: ENV['APPLE_ISSUER_ID'],
    key_content: Base64.decode64(ENV['APPLE_KEY_CONTENT']),
    duration: 1200,
    in_house: false
    )
    pilot(
    api_key: api_key,
    skip_waiting_for_build_processing: true,
    skip_submission: true,
    distribute_external: false,
    notify_external_testers: false,
    ipa: "./App.ipa"
    )
    end
    end

    Build Processing

    In GitHub Actions, you are billed based on the minutes you have used for running your CI/CD workflow. From experience, it takes about 10–15 minutes before a build can be processed in App Store Connect.

    For private projects, the estimated cost per build can go up to $0.08/min x 15 mins = $1.2, or more, depending on the configuration or dependencies of your project.

    If you share the same concerns for the pricing as I do for private projects, you can keep the skip_waiting_for_build_processing to true.

    What’s the catch? You have to manually update the compliance of your app in App Store Connect after the build has been processed, for you to distribute the build to your users.

    This is just an optional parameter to update if you want to save on the build minutes for private projects. For free projects, this shouldn’t be a problem at all. See pricing.

    3. Setup GitHub Actions

    Configure GitHub secrets

    Ever wonder where the values of the ENV are coming from? Well, it’s not a secret anymore – it’s from your project’s secret. 🤦

    Set GitHub secrets

    1. APP_STORE_CONNECT_TEAM_ID - the ID of your App Store Connect team in you’re in multiple teams.

    2. PROVISIONING_PROFILE_SPECIFIER - match AppStore <YOUR_APP_BUNDLE_IDENTIFIER>, eg. match AppStore com.domain.blabla.demo.

    3. BUILD_CERTIFICATE_BASE64 - Base64 encoded certificate.

    4. BUILD_PROVISION_PROFILE_BASE64 - Base64 encoded provisioning profile.

    5. BUNDLE_IDENTIFIER - your app’s bundle identifier.

    6. APPLE_KEY_ID — App Store Connect API Key 🔺Key ID.

    7. APPLE_ISSUER_ID — App Store Connect API Key 🔺Issuer ID.

    8. APPLE_KEY_CONTENT — App Store Connect API Key 🔺 Key content of .p8, check it

    4. Configure GitHub workflow file

    Create a GitHub workflow directory.

    cd .github/workflows

    Inside the workflow folder, create a file named build-upload-ios.ymland add the following.

    name: Build source code on ios
    on:
    push:
    tags:
    - '*'
    jobs:
    build_ios:
    runs-on: macOS-latest
    steps:
    - uses: actions/checkout@v4
    - name: Use Node.js 20
    uses: actions/setup-node@v3
    with:
    node-version: 20
    cache: npm
    - name: Install dependencies
    id: install_code
    run: npm ci
    - name: Build
    id: build_code
    run: npm run build
    - name: Build
    id: build_code
    run: npm run mobile
    - uses: actions/cache@v3
    with:
    path: ios/App/Pods
    key: ${{ runner.os }}-pods-${{ hashFiles('**/Podfile.lock') }}
    restore-keys: |
    ${{ runner.os }}-pods-
    - name: Sync
    id: sync_code
    run: npx cap sync
    - uses: ruby/setup-ruby@v1
    with:
    ruby-version: '3.0'
    bundler-cache: true
    - uses: maierj/fastlane-action@v3.1.0
    env:
    APP_STORE_CONNECT_TEAM_ID: ${{ secrets.APP_STORE_CONNECT_TEAM_ID }}
    BUNDLE_IDENTIFIER: ${{ secrets.BUNDLE_IDENTIFIER }}
    BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
    BUILD_PROVISION_PROFILE_BASE64: ${{ secrets.BUILD_PROVISION_PROFILE_BASE64 }}
    APPLE_KEY_ID: ${{ secrets.APPLE_KEY_ID }}
    APPLE_ISSUER_ID: ${{ secrets.APPLE_ISSUER_ID }}
    APPLE_KEY_CONTENT: ${{ secrets.APPLE_KEY_CONTENT }}
    APPLE_PROFILE_NAME: ${{ secrets.APPLE_PROFILE_NAME }}
    with:
    lane: ios beta
    - name: Upload release bundle
    uses: actions/upload-artifact@v4
    with:
    name: ios-release
    path: ./App.ipa
    retention-days: 10

    This workflow should be triggered after each GitHub tag, if you need to automatize tag please, refer to Automatic build and release with GitHub actions first.

    Then this workflow will pull your NodeJS deps, install them and build your JavaScript app.

    Each time you send a new commit, a release will be built in TestFlight.

    Your App doesn’t need to use Ionic, only Capacitor base is mandatory., it can have old Cordova module, but Capacitor JS plugin should be preferred.

    5. Trigger workflow

    Create a Commit

    Make a commit, you should see the active workflow in the repository.

    Trigger the workflow

    Push the new commits to the branch main or developement to trigger the workflow.

    Started with commit

    After a few minutes, the build should be available in your App Store Connect dashboard.

    Testflight Dashboard

    Can deploy from local machine?

    Yes, you can, and it is effortless.

    You can use Xcode to build and sign your app, as always.

    Thanks

    This blog is based on the following articles:

    Authored By

    Latest from news

    Capgo gives you the best insights you need to create a truly professional mobile app.