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.
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
| Method | Platform | Certificate Type | Provisioning Profile | Notarization |
|---|---|---|---|---|
| App Store | iOS, macOS | Apple Distribution | App Store | Not required (Apple handles it) |
| TestFlight | iOS, macOS | Apple Distribution | App Store | Not required |
| Ad Hoc | iOS | Apple Distribution | Ad Hoc (with device UDIDs) | Not required |
| Enterprise | iOS | In-House | In-House | Not required |
| Developer ID | macOS | Developer ID Application | None (uses Dev ID cert) | Required |
| Development | iOS, macOS | Apple Development | Development | Not 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:
- Open Keychain Access (Applications > Utilities)
- From the menu: Keychain Access > Certificate Assistant > Request a Certificate From a Certificate Authority
- Enter your email address and a common name (your name or your company name)
- Select “Saved to disk”
- Save the
.certSigningRequestfile
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:
- Click the ”+” button to create a new certificate
- 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
- Upload your CSR file
- Download the generated
.cerfile - 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 Type | Maximum Active | Shared Across Team |
|---|---|---|
| Apple Development | 2 per member | No (individual) |
| Apple Distribution | 3 per team | Yes |
| Developer ID Application | 5 per team | Yes |
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:
- In Keychain Access, select the certificate (with its private key)
- Right-click > Export Items
- Choose Personal Information Exchange (.p12) format
- Set a strong password
- Store the
.p12file 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:
- Go to Identifiers > App IDs
- Click ”+”
- Select “App IDs” and “App”
- Enter a description and your bundle identifier (e.g.,
com.yourcompany.yourapp) - Enable any capabilities your app uses (Push Notifications, App Groups, Sign in with Apple, etc.)
- Click “Register”
Create a Development Profile
For testing on physical devices during development:
- Go to Profiles > click ”+”
- Select “iOS App Development” (or “macOS App Development”)
- Select your App ID
- Select your Apple Development certificate
- Select the devices you want to test on
- Name the profile (e.g., “YourApp Dev”)
- Download and double-click to install in Xcode
Create an App Store Distribution Profile
For App Store and TestFlight distribution:
- Go to Profiles > click ”+”
- Select “App Store Connect” (under Distribution)
- Select your App ID
- Select your Apple Distribution certificate
- Name the profile (e.g., “YourApp AppStore”)
- Download and install
Create an Ad Hoc Profile
For distributing test builds to specific devices outside TestFlight:
- Go to Profiles > click ”+”
- Select “Ad Hoc”
- Select your App ID
- Select your Apple Distribution certificate
- Select the test devices (must be registered in the portal)
- 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:
- Go to the Developer portal
- Find the expired profile
- Click “Edit”
- Make no changes and click “Generate”
- 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:
- Select your project in the Navigator
- Select your app target
- Go to “Signing & Capabilities”
- Check “Automatically manage signing”
- 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:
- Uncheck “Automatically manage signing”
- For the Debug build configuration:
- Signing Certificate: Apple Development
- Provisioning Profile: Your development profile
- 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:
| Target | Bundle ID | Profile |
|---|---|---|
| Main App | com.yourcompany.yourapp | YourApp AppStore |
| App Clip | com.yourcompany.yourapp.Clip | YourApp Clip AppStore |
| Widget | com.yourcompany.yourapp.Widget | YourApp Widget AppStore |
| Notification Extension | com.yourcompany.yourapp.NotificationService | YourApp 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
| Capability | Entitlement Key | Purpose |
|---|---|---|
| Push Notifications | aps-environment | Sending and receiving push notifications |
| App Groups | com.apple.security.application-groups | Sharing data between app and extensions |
| iCloud | com.apple.developer.icloud-container-identifiers | Cloud storage and sync |
| Sign in with Apple | com.apple.developer.applesignin | Apple ID authentication |
| Associated Domains | com.apple.developer.associated-domains | Universal Links and App Clips |
| HealthKit | com.apple.developer.healthkit | Reading/writing health data |
| App Attest | com.apple.developer.devicecheck.appattest-environment | Device integrity verification |
| Keychain Sharing | keychain-access-groups | Sharing keychain items between apps |
Configuring Entitlements in Xcode
- Select your target
- Go to Signing & Capabilities
- Click ”+ Capability”
- Select the capability you need
- Xcode adds the entitlement to your
.entitlementsfile 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:
- Go to the Developer portal > Identifiers > your App ID
- Enable the missing capability
- Regenerate your provisioning profile (it must be regenerated after changing App ID capabilities)
- Download and install the new profile
- Rebuild
Debug vs Release Entitlements
Some entitlements have different values for debug and release:
aps-environment:developmentfor debug,productionfor releasecom.apple.developer.associated-domains: Append?mode=developerduring 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
- Select a physical device or “Any iOS Device” as the build destination (not a simulator)
- Product > Archive
- Wait for the archive to complete
- The Organizer window opens showing your archive
Exporting for App Store
- In the Organizer, select your archive
- Click “Distribute App”
- Select “App Store Connect”
- Choose “Upload” (sends directly) or “Export” (saves an IPA file)
- Select your distribution certificate and profile
- Review the signing summary and click “Upload” or “Export”
Exporting for Ad Hoc or Development
- In the Organizer, select your archive
- Click “Distribute App”
- Select “Ad Hoc” or “Development”
- Select options (thinning, bitcode, symbols)
- Select your certificate and profile
- 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:
| Format | Extension | Use case |
|---|---|---|
| Disk image | .dmg | Standard macOS app distribution |
| ZIP archive | .zip | Alternative distribution format |
| Installer package | .pkg | Apps 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:
| Error | Cause | Fix |
|---|---|---|
| Invalid signature | Not signed with Developer ID cert | Re-sign with the correct certificate |
| Hardened Runtime not enabled | Missing hardened runtime flag | Enable “Hardened Runtime” in Xcode or add --options runtime to codesign |
| Unsigned nested code | A framework or helper tool inside the bundle is unsigned | Sign all nested binaries before signing the outer app |
| Timestamp missing | Code signature lacks a secure timestamp | Add --timestamp flag to codesign |
| Contains disallowed entitlement | An entitlement is not permitted for Developer ID | Remove the entitlement or use a different distribution method |
Enabling Hardened Runtime
Hardened runtime is required for notarization. In Xcode:
- Select your target
- Signing & Capabilities > ”+” > Hardened Runtime
- 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:
- Create a workflow in App Store Connect
- Xcode Cloud manages certificates and profiles for you
- Configure build actions, test actions, and post-build actions
- 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.