Mastering iOS Automation: Fastlane with Automatic and Manual Signing


Mastering iOS Automation: Fastlane with Automatic and Manual Signing

For iOS developers, app signing is a crucial, yet often tedious, process. Xcode's "Automatically manage signing" feature offers a convenient way to simplify this process. However, when combined with the automation tool Fastlane, it presents both opportunities and challenges. This article explores how to configure Fastlane in a project with automatic Xcode signing, the problems that can arise, and how to solve them. Someone also presents a recommended hybrid approach.

Xcode's Automatic Signing: A Double-Edged Sword

The automatic signing feature in Xcode is particularly appealing for solo developers and small teams. It independently handles the creation and management of provisioning profiles and certificates, significantly reducing the initial setup effort.[1] For local development and testing on registered devices, this approach is often sufficient.

However, the limitations of automatic signing become apparent in larger teams and especially in Continuous Integration/Continuous Deployment (CI/CD) environments. Since each developer could potentially generate their own signing identities, this can lead to inconsistencies and hard-to-trace errors. This is where Fastlane comes in to standardize and automate the process.

Configuring Fastlane with Automatic Signing

Fastlane can indeed work with Xcode's automatic signing. The key is in the configuration of the build_app action (also known as gym) in the Fastfile. Using the xcargs parameter, you can instruct Xcode to use automatic signing and update provisioning profiles if necessary.

Ruby
# Fastfile
lane :build_with_auto_signing do
  build_app(
    workspace: "YourApp.xcworkspace",
    scheme: "YourApp",
    xcargs: "-allowProvisioningUpdates"
  )
end

This configuration is useful for development builds that, for example, are to be distributed to a small internal team of testers.

Potential Issues and Their Solutions

When combining Fastlane and automatic Xcode signing, various problems can occur:

  • "No profile for team" Error: This error frequently occurs in CI/CD environments, as there is often no Xcode GUI available to manage signing automatically.[2][3][4]

    • Solution: For CI/CD builds, it is strongly recommended to switch to a manual signing method like fastlane match.

  • "Code signing is required" Error: This message appears when no valid signing identity has been found for a specific build target.[5][6][7] This can happen if automatic signing is disabled for a particular target or if Fastlane does not have the correct permissions.

    • Solution: Ensure that you have consistent signing settings across all targets in your project.[8] In many cases, disabling automatic signing and explicitly specifying the provisioning profiles is the more reliable method.[6]

  • Inconsistent Builds: If different developers on a team use automatic signing, it can lead to builds that are signed with different certificates and profiles. This makes traceability difficult and can lead to problems when publishing to the App Store.

  • "errSecInternalComponent Command CodeSign failed with a nonzero exit code 65" Error: This error frequently occurs in CI/CD environments, as it need to read your keychain but found out that it has no permissions to do that.

    • Solution: You should execute this "security unlock-keychain -p YOUR_LOGIN_PASSWORD_HERE login.keychain" to grant the permission to terminal.[9]


The Recommended Approach: A Hybrid Strategy

The best practice for most teams is a hybrid approach that combines the advantages of both worlds:

  1. Automatic Signing for Local Development: Developers can keep automatic signing enabled in their local Xcode environment to quickly and easily test on their devices.

  2. Manual Signing with  For all builds intended for beta testing (e.g., via TestFlight) or for App Store publication, a manual signing process using fastlane match should be used. match stores all certificates and provisioning profiles in a central, encrypted Git repository, giving everyone on the team and the CI/CD environment access to the same, consistent signing data.

Example of a Fastfile for the Hybrid Approach

Fastfile for this approach could look like this:

Ruby
# Fastfile
default_platform(:ios)

platform :ios do
  # Lane for local development with automatic signing
  desc "Builds the app for local testing using automatic signing"
  lane :dev_build do
    build_app(
      workspace: "YourApp.xcworkspace",
      scheme: "YourApp-Dev",
      xcargs: "-allowProvisioningUpdates"
    )
  end

  # Lane for App Store release with fastlane match
  desc "Builds and uploads the app to App Store Connect using manual signing"
  lane :release do
    # Disables automatic signing in the Xcode project
    disable_automatic_code_signing(path: "YourApp.xcodeproj")

    # Fetches the certificates and profiles from the match repository
    match(type: "appstore", readonly: true)

    # Builds the app with the profiles provided by match
    build_app(
      workspace: "YourApp.xcworkspace",
      scheme: "YourApp-Release",
      export_method: "app-store",
      export_options: {
        provisioningProfiles: {
          "com.yourcompany.yourapp" => "match AppStore com.yourcompany.yourapp"
        }
      }
    )

    # Uploads the app to App Store Connect
    upload_to_app_store

    # Re-enables automatic signing for local development
    enable_automatic_code_signing(path: "YourApp.xcodeproj")
  end
end

In this configuration, the dev_build lane will use Xcode's automatic signing. The release lane, on the other hand, temporarily disables automatic signing, uses match to synchronize signing data, and builds the app with the explicitly specified provisioning profiles. After the build process, automatic signing is re-enabled so as not to disrupt developers in their local environment.

While the Hybrid Approach is not Necessary

It may resolve your problem, but the match method is not what I want. I really do not need to maintain another repository to store the certificates and profiles. After I tried to add provisioningProfiles to the export_options, it worked as a charm. In this way, what you need to do is creating the profiles it need and download those profiles to local machine by Xcode Account Setting.

If you only need one profile, the export_options is not even necessary at all. I need this just because there are 2 profiles in this project.

Ruby
# Fastfile
default_platform(:ios)

platform :ios do

  # Lane for App Store release with fastlane
  desc "Builds and uploads the app to App Store Connect using manual signing"
  lane :release do
    # Fetches the certificates and profiles from the match repository
    match(type: "appstore", readonly: true)

    # Builds the app with the profiles provided by match
    build_app(
      workspace: "YourApp.xcworkspace",
      scheme: "YourApp-Release",
      export_method: "app-store",
      xcargs: "-allowProvisioningUpdates",
      export_options: {
        provisioningProfiles: {
          "com.yourcompany.yourapp" => "match AppStore com.yourcompany.yourapp",
          "com.yourcompany.yourwidget" => "match AppStore com.yourcompany.yourwidget"
        }
      }
    )

    # Uploads the app to App Store Connect
    upload_to_app_store

  end
end

Conclusion

Automatic signing in Xcode is a useful tool for local development but hits its limits in team and CI/CD environments. A combination of automatic signing for development builds and a robust manual signing process like fastlane match for release builds offers the best mix of convenience and reliability. With the right configuration in the Fastfile, this approach can be seamlessly integrated into the development workflow, ensuring consistent and error-free builds.


Sources

  1. polpiella.dev
  2. signmycode.com
  3. stackoverflow.com
  4. circleci.com
  5. fastlane.tools
  6. stackoverflow.com
  7. stackoverflow.com
  8. fastlane.tools
  9. stackoverflow.com

Comments

Popular posts from this blog

How to SSH Directly Into a sudo Shell Without Getting Stuck