Skip to main content

iOS App Store Pre-Submission Linter

Built your app with AI?
Catch issues before you submit.

ShipLint scans your Xcode project for the rejection risks that AI-generated code misses — privacy manifests, entitlements, Info.plist issues — before you upload to App Store Connect.

npx shiplint scan .

Free and open source. No account required. Works with Xcode projects AND Swift Package Manager (Package.swift) — no .xcodeproj required.

According to Apple's 2024 App Store Transparency Report, approximately 25% of app submissions are rejected. Each rejection cycle costs developers 10-30 minutes of upload time alone, not counting the fixes. ShipLint checks 16 rules covering the most preventable rejection causes.

~25%

of App Store submissions are rejected
— Apple 2024 Transparency Report

10-30 min

wasted per upload/rejection cycle
before you even start fixing

16 rules

covering the most preventable
App Store rejection causes

AI writes great Swift. It doesn't know Apple's rules.

Cursor, Copilot, and Claude produce working iOS code — but App Store submission isn't just about code that compiles. Apple requires specific project configurations that AI tools don't generate. ShipLint catches these gaps.

AI generates code

Your AI writes the Swift, sets up the views, handles the logic. It works in the simulator.

Apple rejects it

Missing privacy manifest. Vague usage string. Entitlement mismatch. Days lost to a rejection email.

ShipLint helps catch it

Run one command before you submit. Fix issues in minutes, not days. Ship with confidence.

What ShipLint Catches

16 rules targeting the most common App Store rejection causes — the "surprise" errors that AI-generated code doesn't know to avoid.

Each rule maps to a specific Apple guideline or ITMS error code. ShipLint tells you exactly what's wrong and how to fix it.

01

Privacy Manifests

Checks for PrivacyInfo.xcprivacy and required API declarations (UserDefaults, file timestamps, disk space, etc.).

ITMS-91053 ITMS-90683
02

Usage Description Strings

Detects missing or overly generic NS*UsageDescription strings. Vague strings like "This app needs camera access" get rejected.

Guideline 5.1.1
03

Entitlements Validation

Verifies entitlements match your provisioning profile. Catches capabilities declared in code but missing from signing.

ITMS-90078
04

Info.plist Validation

Required keys, valid bundle identifiers, version string format (CFBundleShortVersionString), and required device capabilities.

Guideline 2.1
05

App Transport Security

Flags ATS exceptions that need written justification. NSAllowsArbitraryLoads is almost always rejected without one.

Guideline 2.1
06

Sign in with Apple

If your app uses third-party login (Google, Facebook), Apple requires Sign in with Apple as an option. ShipLint checks for SIWA entitlement.

Guideline 4.8
07

URL Schemes

Validates custom URL scheme registration and deep link configuration. Catches mismatches between code and plist.

08

Build Settings

Deployment target compatibility, required architectures, and build configuration issues in .pbxproj.

09

Third-Party SDK Declarations

Detects SDKs that Apple requires privacy declarations for (e.g., Firebase, Facebook SDK, AdMob) and checks for matching manifest entries.

ITMS-91053
10

Asset Validation

Checks for required app icon sizes, launch screen configuration, and asset catalog completeness.

11

Required Reason API Detection

New

Scans your Swift source for Required Reason API usage (UserDefaults, file timestamps, disk space, boot time, etc.) and verifies matching privacy manifest declarations. The #1 privacy-related rejection reason in 2024.

ITMS-91053 privacy-010-required-reason-api

Install and Scan in 30 Seconds

No account. No config file. One command.

1

Install globally (or use npx)

$ npm install -g shiplint
2

Point it at your project

$ shiplint scan ./MyApp.xcodeproj
$ shiplint scan . # also works with Package.swift — no .xcodeproj needed
3

Fix what it flags, then ship

Every issue includes a clear explanation and fix suggestion. Copy the fix into your project, re-scan, submit with confidence.

Output Formats

--format text --format json --format sarif

Frequently Asked Questions

Common questions about App Store rejections and how ShipLint helps.

Why did Apple reject my app?

According to Apple's 2024 App Store Transparency Report, approximately 25% of app submissions are rejected. The most common preventable causes include:

  • Missing privacy manifest (ITMS-91053) — required since iOS 17 for apps using UserDefaults, file timestamps, and other system APIs
  • Missing required reason API declaration (ITMS-90683) — you must declare why you use certain APIs
  • Vague usage description strings — generic strings like "This app needs access" are rejected under Guideline 5.1.1
  • Entitlement mismatches (ITMS-90078) — capabilities in code that don't match your provisioning profile
  • App Transport Security exceptions without written justification

ShipLint checks for all 16 of the most preventable rejection causes before you submit.

How do I fix ITMS-90683?

ITMS-90683 means your app uses a "required reason API" but hasn't declared the reason in a privacy manifest. Since Spring 2024, Apple requires apps to include a PrivacyInfo.xcprivacy file declaring why they use specific system APIs.

To fix it:

  1. Create a PrivacyInfo.xcprivacy file in your Xcode project
  2. Add the required API type (e.g., NSPrivacyAccessedAPICategoryUserDefaults)
  3. Specify the reason code (e.g., CA92.1 for app functionality)
  4. Add the file to your target's "Copy Bundle Resources" build phase

ShipLint detects which APIs in your code trigger this requirement and tells you exactly which privacy manifest entries to add.

Does ShipLint replace Fastlane?

No — they're complementary tools that check different layers.

Fastlane Precheck validates your App Store Connect metadata: screenshots, app description, age rating, and pricing. It operates at the store listing level.

ShipLint validates your project files: Info.plist, entitlements, privacy manifests, build settings, and pbxproj configuration. It operates at the code and config level.

For maximum coverage, use both: ShipLint for project-level checks, Fastlane Precheck for store-level checks. They don't overlap.

Can ShipLint check apps built with Cursor, Copilot, or Claude?

Yes — in fact, that's exactly what ShipLint is designed for.

AI coding tools like Cursor, GitHub Copilot, and Claude are excellent at generating functional Swift code. But they often miss Apple's platform configuration requirements — privacy manifests, entitlement declarations, usage description strings — because these aren't about code correctness, they're about App Store compliance.

ShipLint bridges that gap. It checks the project-level files that AI tools don't generate or configure properly, catching issues before submission.

Just point ShipLint at your .xcodeproj, Package.swift, or project directory — it doesn't matter how the code was written. Swift PM projects work without any .xcodeproj at all.

What's the difference between ShipLint and Xcode validation?

Different timing, different scope.

Xcode's built-in validation (Archive → Validate) runs at archive time and catches binary-level issues: invalid architectures, missing symbols, code signing problems. It requires a full build.

ShipLint runs at development time — no build required. It catches configuration-level issues: missing privacy manifests, vague usage descriptions, entitlement mismatches. It runs in seconds from your terminal or CI pipeline.

Think of it as shift-left validation: catch config issues during development, not after a 10-minute archive process. ShipLint gives you feedback in seconds; Xcode validation requires a successful build first.

Is ShipLint free?

Yes. ShipLint is free and open source, licensed under MIT. Install it with npm install -g shiplint and start scanning immediately. No account, no API key, no usage limits.

View the source code at github.com/Signal26AI/ShipLint.

For AI Agents & CI Pipelines

ShipLint outputs structured JSON and SARIF for automated workflows. Integrate into GitHub Actions, Xcode Cloud, or your AI coding agent's toolchain.

Quick Reference — ShipLint CLI
# Install
npm install -g shiplint
# Scan (human-readable)
shiplint scan ./path/to/project
# Scan (JSON for parsing)
shiplint scan ./path/to/project --format json
# Scan (SARIF for GitHub code scanning)
shiplint scan ./path/to/project --format sarif
# Exit codes: 0 = all checks pass, 1 = errors found, 2 = scan failed
.github/workflows/shiplint.yml
name: ShipLint
on: [push, pull_request]

jobs:
  lint:
    runs-on: macos-latest
    steps:
      - uses: actions/checkout@v4
      - run: npm install -g shiplint
      - run: shiplint scan . --format sarif > results.sarif
      - uses: github/codeql-action/upload-sarif@v3
        if: always()
        with:
          sarif_file: results.sarif
JSON Output Schema
{
  "version": "1.0.0",
  "project": "VibeApp.xcodeproj",
  "summary": {
    "errors": 2,
    "warnings": 1,
    "passed": 7
  },
  "issues": [
    {
      "rule": "privacy-manifest",
      "severity": "error",
      "code": "ITMS-91053",
      "message": "Missing privacy manifest for UserDefaults API",
      "file": "Info.plist",
      "fix": "Add PrivacyInfo.xcprivacy with NSPrivacyAccessedAPICategoryUserDefaults"
    }
  ]
}
MCP

AI Agent Ready via Model Context Protocol

ShipLint works with Claude, Cursor, Windsurf, and any MCP-compatible AI coding agent. Exposes 3 tools: scan (run full project scan), rules (list all 16 rules), and explain (get detailed fix guidance for any issue) — all locally, no network required.

# Start MCP server
shiplint mcp
// IDE config (Cursor, Windsurf, etc.)
{
  "mcpServers": {
    "shiplint": {
      "command": "npx",
      "args": ["shiplint", "mcp"]
    }
  }
}

Integration Tips

Use --format json for machine-parseable output
Check exit code before parsing: 0 = pass, 1 = issues
Each issue includes a fix field with actionable remediation
SARIF format integrates with GitHub Advanced Security code scanning
Run on every PR to catch issues before merge
No network access required — all checks are local and offline

Your AI wrote the code.
Now catch issues before you submit.

One command. 30 seconds. Know before you submit.

npm install -g shiplint
View on GitHub