Skip to content

Repository files navigation

Apple App Store Pre-Submit Audit

Tests Python 3.10+ MIT License

A local Python CLI that surfaces potential App Store submission issues before review: inconsistent metadata, unused permission declarations, incomplete subscription disclosures, suspicious entitlement state, and project-specific release risks.

It is a preflight assistant, not an App Review guarantee. The checks combine Apple's published guidelines with conservative, submission-derived heuristics.

🔴 MyApp                [passed=24 skipped=0 total=27] blockers=1
   🔴 OFFICIAL 2.3.7 name-length          App Store name too long (34 chars, max 30)
   ⚠️  ADVISORY 3.1.2(c) renewal-disclosure-copy
                                              Heuristic: verify that price, duration,
                                              and renewal terms are clear
   ℹ️  ADVISORY 4.2 minimum-functionality-shape
                                              Heuristic: file counts do not determine
                                              compliance; review the experience

The output above is illustrative.

What it checks

The audit combines a baseline rule set with condition-aware checks across Apple's five review categories and additional engineering checks.

  • A baseline set runs for every project; metadata-only findings are treated as not_evaluated when --no-asc supplies no corresponding field.
  • Extra checks activate when the project declares permissions, subscriptions, accounts, third-party login, CloudKit, AI APIs, a paywall, or other relevant features.
  • A synthetic coverage fixture exercises the broad conditional catalog; a normal project runs only the checks relevant to the signals it exposes.
Area Examples
Safety misleading sensor or medical claims, Kids Category advertising
Performance metadata completeness, app-name length, implementation evidence for advertised features
Business subscription disclosure, restore flow, privacy and terms links, purchase-price prominence
Design minimum-functionality and duplicate-app heuristics, Sign in with Apple
Legal privacy policy, account deletion, permissions matched to framework use
Custom StoreKit state, CloudKit save patterns, SwiftData defaults, paywall claims matched to code

See audit.py for the implementation of every check.

Quick start

git clone https://github.com/XJM-free/apple-presubmit-audit.git
cd apple-presubmit-audit

python3 -m venv .venv
source .venv/bin/activate
python3 -m pip install -r requirements.txt

Audit one project against App Store Connect metadata:

python3 audit.py \
  --project ~/Code/MyApp \
  --bundle-id com.example.myapp \
  --key-id ABC123XYZ \
  --issuer-id 12345-67890-... \
  --key-file ~/AuthKey_ABC123XYZ.p8

The credentials can also come from environment variables:

export ASC_KEY_ID=ABC123XYZ
export ASC_ISSUER_ID=12345-67890-...
export ASC_KEY_FILE=~/AuthKey_ABC123XYZ.p8

python3 audit.py --project ~/Code/MyApp --bundle-id com.example.myapp

Code-only mode skips App Store Connect. It is useful for local iteration, but metadata-dependent checks will not have enough information:

python3 audit.py --project ~/Code/MyApp --no-asc

For several apps, copy apps.example.json to apps.json and run:

python3 audit.py --config apps.json
python3 audit.py --config apps.json --quiet
python3 audit.py --config apps.json --json
python3 audit.py --config apps.json --sarif > audit.sarif

Never commit App Store Connect credentials. This repository ignores common private-key, provisioning-profile, environment, and local app-config files by default.

GitHub Code Scanning in five minutes

Copy examples/github-actions/apple-presubmit-audit.yml to .github/workflows/apple-presubmit-audit.yml in an Apple project. The workflow pins every third-party action and the audit source to full commit SHAs, runs without App Store Connect credentials, uploads SARIF, and then preserves the CLI exit code.

The repository also exposes a composite action for shorter workflows. Pin it to the full commit SHA of a release rather than a mutable branch or tag:

permissions:
  contents: read
  security-events: write

steps:
  - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
  - uses: XJM-free/apple-presubmit-audit@FULL_RELEASE_COMMIT_SHA

Both integrations deliberately use --no-asc: no issuer ID, key ID, or private key is read. Metadata-only checks therefore report not_evaluated. For repeatable offline metadata checks, pass config_path to the composite action and keep non-secret field overrides in the config.

The audit always writes SARIF before returning its status. GitHub Code Scanning receives the report first; the job then succeeds for exit 0, fails for blockers with exit 1, or fails for invalid input with exit 2. Public repositories can use Code Scanning without an Advanced Security license; private repositories need Code Security enabled for SARIF uploads.

Exit codes

Code Meaning
0 No blocker was detected among the checks that ran
1 One or more blocker checks failed
2 Project/config input is invalid, or App Store Connect data could not be fetched safely

Do not treat exit code 0 as proof that Apple will approve an app.

Configuration failures are written to stderr. With --json, stdout remains valid JSON and includes an errors array, so CI callers can distinguish invalid input from review findings.

In JSON output, passed is true, false, or null; null is paired with "status": "not_evaluated" and is never counted as a blocker.

--sarif emits SARIF 2.1.0 while preserving the same exit codes. Failed findings become results (blockererror, highwarning, lownote); passed and not_evaluated rules are omitted. Configuration failures become tool execution notifications. Run the command from the repository root when uploading the report to GitHub Code Scanning. Because most checks combine project-wide code and App Store Connect metadata, SARIF locations point to a real project file as an explicitly labeled audit anchor, not an exact match line. If no safe repository-relative project file exists, the finding remains locationless instead of exposing an absolute local path.

How to read the findings

Every rule ID starts with its evidence basis:

  • OFFICIAL — directly validates a field or limit published by Apple, such as the 30-character app-name limit, a required Support URL, or a published age-rating value.
  • READINESS — directly observes a submission or StoreKit catalog state that can prevent the intended release or purchase flow.
  • ADVISORY — a regex, keyword, typography, file-count, or submission-derived heuristic. It is evidence to inspect, not proof of an Apple violation.

Apple does not publish a 200-character minimum for review notes, a 36-point price-font rule, a minimum number of SwiftUI views, or a mandatory literal auto-renewing CTA phrase. Those checks are therefore labeled ADVISORY.

Treat findings as prompts for review:

  1. Confirm the relevant guideline and current App Store Connect requirements.
  2. Inspect the matched code or metadata.
  3. Fix real issues and dismiss false positives with project context.

Severity describes release impact:

  • blocker — reserved for OFFICIAL or directly observed READINESS findings.
  • high — a significant manual-review prompt.
  • low — a lower-confidence or product-quality prompt.

ADVISORY findings are enforced in code and tests to never use blocker severity or hard-requirement wording.

Limitations

  • Swift and plist inspection is regex-based; generated code and dynamic framework loading can produce false negatives.
  • Some checks are deliberately conservative and can produce false positives.
  • The tool does not inspect the final signed binary or screenshots.
  • App Store Connect does not expose every review field through its API.
  • Apple can change its guidelines and reviewer behavior independently of this repository.

Always perform a manual final review.

Development

python3 -m unittest discover -s tests -v
python3 -m py_compile audit.py

Contributions are welcome. Read CONTRIBUTING.md before adding a rule, and include both a triggering fixture and a non-triggering fixture when possible.

License

MIT

About

70+ App Store Review pre-submit checks for indie iOS developers. Catches plist mismatches, missing 'auto-renewing' CTAs, declared-but-unimplemented permissions, and 60+ more before you hit Submit.

Topics

Resources

Contributing

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages