Sat Nov 15 Estimated time: PT55M

How to Sign and Notarize iOS and macOS Apps

A step-by-step guide to code signing and notarization for iOS and macOS apps. Learn how to configure certificates, provisioning profiles, entitlements, and automated notarization for App Store and direct distribution.

App signing and notarization for iOS and macOS

Why Signing and Notarization Matter

Every iOS and macOS app must be code signed before it can run on a user’s device. Code signing is Apple’s mechanism for verifying that an app comes from a known developer and has not been tampered with since it was built. Without a valid signature, iOS refuses to launch the app and macOS shows a security warning that most users will not click through.

Notarization is an additional layer for macOS apps distributed outside the App Store. Apple’s notarization service scans your app for malicious content and issues a ticket that tells macOS Gatekeeper the app is safe. Since macOS Catalina, notarization is effectively required for any macOS app that users download from the web.

For your App Store Optimization strategy, signing and notarization are not optional steps you can rush through at the end. A misconfigured signing setup causes build failures, rejected submissions, and delayed releases. Developers who automate and understand this process ship updates faster, which means more frequent metadata optimization cycles, quicker responses to competitor changes, and faster iteration on conversion rate experiments.

Step 1: Understand Distribution Methods and Their Requirements

Each distribution method requires a different combination of certificates, profiles, and notarization.

Distribution Methods Overview

MethodPlatformCertificate TypeProvisioning ProfileNotarization
App StoreiOS, macOSApple DistributionApp StoreNot required (Apple handles it)
TestFlightiOS, macOSApple DistributionApp StoreNot required
Ad HociOSApple DistributionAd Hoc (with device UDIDs)Not required
EnterpriseiOSIn-HouseIn-HouseNot required
Developer IDmacOSDeveloper ID ApplicationNone (uses Dev ID cert)Required
DevelopmentiOS, macOSApple DevelopmentDevelopmentNot required

App Store Distribution

The most common path. Your app is signed with an Apple Distribution certificate and an App Store provisioning profile, uploaded to App Store Connect, reviewed by Apple, and distributed through the App Store. Apple re-signs the binary with a distribution certificate during the install process.

Developer ID Distribution (macOS Only)

For macOS apps distributed outside the App Store (from your website, GitHub, or other channels). You sign with a Developer ID Application certificate and must notarize the app with Apple before distributing. This is the path that requires the most manual configuration.

TestFlight and Ad Hoc

TestFlight uses the same App Store certificate and profile. Ad Hoc distribution requires listing specific device UDIDs in the provisioning profile, limiting distribution to 100 devices per year. Both are useful for testing but not for production distribution.

When Signing Goes Wrong

Common symptoms of signing problems:

  • “App is damaged and can’t be opened” on macOS
  • “Unable to install” errors on iOS devices
  • App Store Connect rejects your upload with signing errors
  • TestFlight builds crash immediately on launch
  • Entitlement mismatches cause specific features (push notifications, iCloud, App Clips) to fail silently

Understanding which certificate and profile combination you need for your distribution method prevents all of these issues.

Step 2: Create Signing Certificates

Certificates are cryptographic identities that prove you are who you claim to be. They consist of a public key (stored at Apple) and a private key (stored in your Keychain).

Generate a Certificate Signing Request

On your Mac:

  1. Open Keychain Access (Applications > Utilities)
  2. From the menu: Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority
  3. Enter your email address and a common name (your name or your company name)
  4. Select “Saved to disk”
  5. Save the .certSigningRequest file

This generates a private key in your Keychain and a CSR file that you upload to Apple.

Create Certificates in the Developer Portal

Go to developer.apple.com > Account > Certificates, Identifiers & Profiles > Certificates:

  1. Click the ”+” button to create a new certificate
  2. Select the certificate type:
    • Apple Development - for running on test devices during development
    • Apple Distribution - for App Store and TestFlight distribution
    • Developer ID Application - for macOS apps distributed outside the App Store
    • Developer ID Installer - for macOS installer packages distributed outside the App Store
  3. Upload your CSR file
  4. Download the generated .cer file
  5. Double-click to install it in your Keychain

Verify Certificate Installation

Open Keychain Access and look for your certificate under “My Certificates”. You should see:

  • The certificate with an expand arrow
  • Your private key nested under it

If the private key is missing, the certificate was installed on a different Mac than where the CSR was generated. You need either the original Mac’s Keychain or to export and import the private key.

Certificate Limits and Management

Certificate TypeMaximum ActiveShared Across Team
Apple Development2 per memberNo (individual)
Apple Distribution3 per teamYes
Developer ID Application5 per teamYes

Distribution and Developer ID certificates are shared across your team. Anyone with the certificate and private key can sign builds. Store the private key securely and share it only with trusted team members or CI/CD systems.

Exporting Certificates for CI/CD

To use certificates on a CI/CD server, export them as .p12 files:

  1. In Keychain Access, select the certificate (with its private key)
  2. Right-click > Export Items
  3. Choose Personal Information Exchange (.p12) format
  4. Set a strong password
  5. Store the .p12 file and password in your CI/CD secrets

Step 3: Create Provisioning Profiles

Provisioning profiles link three things together: your signing certificate, your App ID, and (for development/Ad Hoc) specific device UDIDs. They tell iOS which apps are allowed to run on which devices, signed by which developer.

Register Your App ID

If you haven’t already, register your App ID in the Developer portal:

  1. Go to Identifiers > App IDs
  2. Click ”+”
  3. Select “App IDs” and “App”
  4. Enter a description and your bundle identifier (e.g., com.yourcompany.yourapp)
  5. Enable any capabilities your app uses (Push Notifications, App Groups, Sign in with Apple, etc.)
  6. Click “Register”

Create a Development Profile

For testing on physical devices during development:

  1. Go to Profiles > click ”+”
  2. Select “iOS App Development” (or “macOS App Development”)
  3. Select your App ID
  4. Select your Apple Development certificate
  5. Select the devices you want to test on
  6. Name the profile (e.g., “YourApp Dev”)
  7. Download and double-click to install in Xcode

Create an App Store Distribution Profile

For App Store and TestFlight distribution:

  1. Go to Profiles > click ”+”
  2. Select “App Store Connect” (under Distribution)
  3. Select your App ID
  4. Select your Apple Distribution certificate
  5. Name the profile (e.g., “YourApp AppStore”)
  6. Download and install

Create an Ad Hoc Profile

For distributing test builds to specific devices outside TestFlight:

  1. Go to Profiles > click ”+”
  2. Select “Ad Hoc”
  3. Select your App ID
  4. Select your Apple Distribution certificate
  5. Select the test devices (must be registered in the portal)
  6. Name and download

Profile Expiration

Provisioning profiles expire after 1 year. Set a calendar reminder to regenerate them before they expire. Expired profiles cause build failures and can block App Store submissions.

When a profile expires:

  1. Go to the Developer portal
  2. Find the expired profile
  3. Click “Edit”
  4. Make no changes and click “Generate”
  5. Download the new profile and install it

Step 4: Configure Code Signing in Xcode

Xcode needs to know which certificate and profile to use for each build configuration.

Automatic Signing

For most projects, automatic signing is the simplest approach:

  1. Select your project in the Navigator
  2. Select your app target
  3. Go to “Signing & Capabilities”
  4. Check “Automatically manage signing”
  5. Select your team from the dropdown

Xcode will create and manage certificates and profiles for you. This works well for solo developers and small teams but can cause issues when multiple developers or CI/CD systems need to build the same project.

Manual Signing

For teams and CI/CD pipelines, manual signing gives you explicit control:

  1. Uncheck “Automatically manage signing”
  2. For the Debug build configuration:
  3. For the Release build configuration:
    • Signing Certificate: Apple Distribution
    • Provisioning Profile: Your App Store or Ad Hoc profile

Build Settings Configuration

You can also set signing in the Build Settings tab:

CODE_SIGN_IDENTITY = "Apple Distribution"
CODE_SIGN_STYLE = Manual
PROVISIONING_PROFILE_SPECIFIER = "YourApp AppStore"
DEVELOPMENT_TEAM = YOUR_TEAM_ID

For CI/CD, these settings can be overridden from the command line:

xcodebuild archive \
  -scheme "YourApp" \
  -configuration Release \
  -archivePath build/YourApp.xcarchive \
  CODE_SIGN_IDENTITY="Apple Distribution" \
  PROVISIONING_PROFILE_SPECIFIER="YourApp AppStore" \
  DEVELOPMENT_TEAM="YOUR_TEAM_ID"

Multiple Targets

If your project has multiple targets (main app, App Clip, extensions, widgets), each target needs its own signing configuration:

TargetBundle IDProfile
Main Appcom.yourcompany.yourappYourApp AppStore
App Clipcom.yourcompany.yourapp.ClipYourApp Clip AppStore
Widgetcom.yourcompany.yourapp.WidgetYourApp Widget AppStore
Notification Extensioncom.yourcompany.yourapp.NotificationServiceYourApp Notification AppStore

Each target needs a separate App ID registered in the Developer portal and a separate provisioning profile. The bundle identifiers must follow Apple’s naming hierarchy (extensions use the parent app’s bundle ID as a prefix).

Step 5: Set Up Entitlements

Entitlements declare which system capabilities your app is allowed to use. They must match the capabilities enabled in your App ID and provisioning profile.

Common Entitlements

CapabilityEntitlement KeyPurpose
Push Notificationsaps-environmentSending and receiving push notifications
App Groupscom.apple.security.application-groupsSharing data between app and extensions
iCloudcom.apple.developer.icloud-container-identifiersCloud storage and sync
Sign in with Applecom.apple.developer.applesigninApple ID authentication
Associated Domainscom.apple.developer.associated-domainsUniversal Links and App Clips
HealthKitcom.apple.developer.healthkitReading/writing health data
App Attestcom.apple.developer.devicecheck.appattest-environmentDevice integrity verification
Keychain Sharingkeychain-access-groupsSharing keychain items between apps

Configuring Entitlements in Xcode

  1. Select your target
  2. Go to Signing & Capabilities
  3. Click ”+ Capability”
  4. Select the capability you need
  5. Xcode adds the entitlement to your .entitlements file and updates the App ID

Manual Entitlements File

Your entitlements file (YourApp.entitlements) is an XML plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>aps-environment</key>
    <string>production</string>
    <key>com.apple.security.application-groups</key>
    <array>
        <string>group.com.yourcompany.yourapp</string>
    </array>
    <key>com.apple.developer.associated-domains</key>
    <array>
        <string>applinks:yourdomain.com</string>
        <string>appclips:yourdomain.com</string>
    </array>
</dict>
</plist>

Entitlement Mismatch Errors

The most common signing failure: your entitlements file requests a capability that your provisioning profile does not include.

If you see errors like:

Provisioning profile "YourApp AppStore" doesn't include the aps-environment entitlement

Fix by:

  1. Go to the Developer portal > Identifiers > your App ID
  2. Enable the missing capability
  3. Regenerate your provisioning profile (it must be regenerated after changing App ID capabilities)
  4. Download and install the new profile
  5. Rebuild

Debug vs Release Entitlements

Some entitlements have different values for debug and release:

  • aps-environment: development for debug, production for release
  • com.apple.developer.associated-domains: Append ?mode=developer during development for AASA debugging

Xcode handles this automatically with automatic signing. With manual signing, verify the correct entitlements file is assigned to each build configuration.

Step 6: Sign and Export for Distribution

With certificates, profiles, and entitlements configured, you are ready to build a signed binary for distribution.

Archiving in Xcode

  1. Select a physical device or “Any iOS Device” as the build destination (not a simulator)
  2. Product > Archive
  3. Wait for the archive to complete
  4. The Organizer window opens showing your archive

Exporting for App Store

  1. In the Organizer, select your archive
  2. Click “Distribute App”
  3. Select “App Store Connect”
  4. Choose “Upload” (sends directly) or “Export” (saves an IPA file)
  5. Select your distribution certificate and profile
  6. Review the signing summary and click “Upload” or “Export”

Exporting for Ad Hoc or Development

  1. In the Organizer, select your archive
  2. Click “Distribute App”
  3. Select “Ad Hoc” or “Development”
  4. Select options (thinning, bitcode, symbols)
  5. Select your certificate and profile
  6. Export the IPA

Command-Line Export

For CI/CD and scripted workflows:

# Archive
xcodebuild archive \
  -scheme "YourApp" \
  -configuration Release \
  -archivePath build/YourApp.xcarchive \
  -destination "generic/platform=iOS"

# Export
xcodebuild -exportArchive \
  -archivePath build/YourApp.xcarchive \
  -exportPath build/export \
  -exportOptionsPlist ExportOptions.plist

The ExportOptions.plist specifies the distribution method:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>app-store-connect</string>
    <key>teamID</key>
    <string>YOUR_TEAM_ID</string>
    <key>signingStyle</key>
    <string>manual</string>
    <key>provisioningProfiles</key>
    <dict>
        <key>com.yourcompany.yourapp</key>
        <string>YourApp AppStore</string>
    </dict>
</dict>
</plist>

Verifying the Signature

After exporting, verify the code signature:

# Check the signature is valid
codesign --verify --deep --strict build/export/YourApp.ipa

# Display signature details
codesign -dv --verbose=4 build/export/YourApp.app

# Check entitlements embedded in the binary
codesign -d --entitlements - build/export/YourApp.app

# Verify provisioning profile
security cms -D -i build/export/YourApp.app/embedded.mobileprovision

If codesign --verify reports errors, the most common causes are:

  • Modified files after signing (never edit files inside the app bundle after signing)
  • Missing signature on a nested framework or dylib
  • Entitlement mismatch between the binary and the profile

Step 7: Notarize macOS Apps

Notarization is required for macOS apps distributed outside the App Store (Developer ID distribution). Apple’s notarization service scans your app for malicious content and issues a cryptographic ticket that macOS Gatekeeper trusts.

Prerequisites

  • macOS app signed with a Developer ID Application certificate
  • An app-specific password for your Apple ID (generated at appleid.apple.com)
  • notarytool (included with Xcode 13+)

Submitting for Notarization

# Submit the app for notarization
xcrun notarytool submit YourApp.dmg \
  --apple-id "your@email.com" \
  --team-id "YOUR_TEAM_ID" \
  --password "your-app-specific-password" \
  --wait

The --wait flag blocks until notarization completes (typically 5-15 minutes). Without it, the command returns immediately and you poll for status:

# Check status of a submission
xcrun notarytool info SUBMISSION_ID \
  --apple-id "your@email.com" \
  --team-id "YOUR_TEAM_ID" \
  --password "your-app-specific-password"

Notarization Formats

You can notarize these formats:

FormatExtensionUse case
Disk image.dmgStandard macOS app distribution
ZIP archive.zipAlternative distribution format
Installer package.pkgApps that need an installer

Handling Notarization Failures

If notarization fails, get the detailed log:

xcrun notarytool log SUBMISSION_ID \
  --apple-id "your@email.com" \
  --team-id "YOUR_TEAM_ID" \
  --password "your-app-specific-password"

Common failure reasons:

ErrorCauseFix
Invalid signatureNot signed with Developer ID certRe-sign with the correct certificate
Hardened Runtime not enabledMissing hardened runtime flagEnable “Hardened Runtime” in Xcode or add --options runtime to codesign
Unsigned nested codeA framework or helper tool inside the bundle is unsignedSign all nested binaries before signing the outer app
Timestamp missingCode signature lacks a secure timestampAdd --timestamp flag to codesign
Contains disallowed entitlementAn entitlement is not permitted for Developer IDRemove the entitlement or use a different distribution method

Enabling Hardened Runtime

Hardened runtime is required for notarization. In Xcode:

  1. Select your target
  2. Signing & Capabilities > ”+” > Hardened Runtime
  3. Enable specific exceptions only if your app requires them:
    • Allow Unsigned Executable Memory (for JIT compilation)
    • Allow DYLD Environment Variables (for plugin loading)
    • Disable Library Validation (for loading third-party frameworks)

Minimize exceptions. Each one reduces the security of your app and may trigger additional scrutiny during notarization.

Stapling the Notarization Ticket

After successful notarization, staple the ticket to your binary so users can verify it offline:

xcrun stapler staple YourApp.dmg

Verify the staple:

xcrun stapler validate YourApp.dmg
spctl --assess --type execute --verbose YourApp.app

The output should show source=Notarized Developer ID.

Distribute the stapled .dmg or .pkg. When users download and open it, macOS Gatekeeper verifies the notarization ticket and allows the app to run without security warnings.

Step 8: Automate in CI/CD

Manual signing and notarization is manageable for occasional releases. For teams shipping weekly or more frequently, automation is essential.

Fastlane Match (Certificate Management)

Fastlane Match stores your certificates and profiles in an encrypted Git repository or cloud storage, ensuring every team member and CI/CD machine uses the same signing identity:

# Matchfile
git_url("https://github.com/yourteam/certificates.git")
type("appstore")
app_identifier("com.yourcompany.yourapp")
# Sync certificates on CI
fastlane match appstore --readonly

Fastlane Gym (Building)

# Fastfile
lane :release do
  match(type: "appstore", readonly: true)

  gym(
    scheme: "YourApp",
    configuration: "Release",
    export_method: "app-store",
    output_directory: "build"
  )
end

Fastlane Deliver (Uploading)

lane :release do
  match(type: "appstore", readonly: true)
  gym(scheme: "YourApp", export_method: "app-store")
  deliver(
    submit_for_review: false,
    force: true,
    skip_screenshots: true,
    skip_metadata: true
  )
end

Xcode Cloud

Apple’s built-in CI/CD handles signing automatically:

  1. Create a workflow in App Store Connect
  2. Xcode Cloud manages certificates and profiles for you
  3. Configure build actions, test actions, and post-build actions
  4. Xcode Cloud signs and uploads builds without manual certificate management

Custom CI/CD Scripts

For GitHub Actions, GitLab CI, or other systems:

#!/bin/bash
# install-certificates.sh

# Decode and install the certificate
echo "$SIGNING_CERTIFICATE_BASE64" | base64 --decode > certificate.p12
security create-keychain -p "" build.keychain
security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain

# Install provisioning profile
echo "$PROVISIONING_PROFILE_BASE64" | base64 --decode > profile.mobileprovision
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
cp profile.mobileprovision ~/Library/MobileDevice/Provisioning\ Profiles/

Automated Notarization (macOS)

Add notarization to your CI/CD pipeline for macOS Developer ID builds:

#!/bin/bash
# notarize.sh

# Store credentials in keychain profile (run once during setup)
xcrun notarytool store-credentials "notarization-profile" \
  --apple-id "$APPLE_ID" \
  --team-id "$TEAM_ID" \
  --password "$APP_SPECIFIC_PASSWORD"

# Submit, wait, and staple
xcrun notarytool submit YourApp.dmg \
  --keychain-profile "notarization-profile" \
  --wait

xcrun stapler staple YourApp.dmg

Signing Automation Checklist

  • Certificates and profiles stored securely (Fastlane Match, encrypted secrets, or Xcode Cloud)
  • CI/CD machine installs certificates on every build (ephemeral keychains)
  • Keychain is cleaned up after build completes (security concerns)
  • Provisioning profiles auto-renewed before expiration
  • Notarization integrated for macOS Developer ID builds
  • Build pipeline sends alerts on signing failures
  • Certificate expiration dates monitored with alerts 30 days before expiry

Signing and Your Release Velocity

The speed at which you can release updates directly affects your App Store Optimization outcomes. Every metadata update, every screenshots change, every keyword field adjustment, every bug fix that improves retention requires a signed, submitted build.

Teams with automated signing pipelines release 2-4x more frequently than teams with manual processes. More frequent releases mean:

  • Faster keyword experimentation cycles
  • Quicker response to competitor metadata changes
  • More frequent A/B test iterations on screenshots and descriptions
  • Faster bug fixes that improve ratings and reviews
  • Better App Store editorial consideration (Apple favors actively maintained apps)

Use ASODOG to track how your release frequency correlates with ranking improvements, conversion rate changes, and download velocity. The data consistently shows that apps with regular update cadences outperform apps that ship quarterly or less.

Investing in a reliable signing and notarization pipeline is not just an engineering concern. It is an ASO strategy accelerator.