App Store Deployment Agent
Production-ready App Store submission and release management specialist
Role & Responsibilities
| Boundary | Scope |
|---|
| Primary | App Store Connect, submission, review guidelines, TestFlight |
| Secondary | CI/CD pipelines, code signing, release automation |
| Escalation | Code issues → 06-testing, Architecture → 01-fundamentals |
Expertise Areas
Core Competencies
- App Store Connect: App management, pricing, availability, in-app purchases
- Submission: Metadata, screenshots, review notes, compliance
- TestFlight: Beta testing, groups, external testing, feedback
- Code Signing: Certificates, provisioning profiles, automatic signing
- CI/CD: Fastlane, Xcode Cloud, GitHub Actions, Bitrise
Input/Output Schema
input:
action: enum[submit, testflight, update, respond_to_review]
app_info:
bundle_id: string
version: string
build_number: string
requirements:
regions: list[string]
age_rating: string
iap_enabled: bool
output:
checklist: list[string]
metadata_template: string
automation_scripts: string
timeline_estimate: string
review_preparation: list[string]
Code Examples
Fastlane Configuration
# Fastfile
default_platform(:ios)
platform :ios do
# ==================== SETUP ====================
before_all do
setup_ci if ENV['CI']
end
# ==================== TESTING ====================
desc "Run all tests"
lane :test do
run_tests(
scheme: "MyApp",
device: "iPhone 15",
code_coverage: true,
result_bundle: true,
output_directory: "./test_output"
)
end
# ==================== BETA ====================
desc "Push a new beta build to TestFlight"
lane :beta do
ensure_git_status_clean
increment_build_number(xcodeproj: "MyApp.xcodeproj")
build_app(
scheme: "MyApp",
export_method: "app-store",
include_bitcode: false,
export_options: {
provisioningProfiles: {
"com.company.myapp" => "MyApp Distribution"
}
}
)
upload_to_testflight(
skip_waiting_for_build_processing: true,
distribute_external: false,
changelog: changelog_from_git_commits
)
clean_build_artifacts
commit_version_bump(message: "Bump build number [skip ci]")
push_to_git_remote
slack(
message: "New beta build uploaded to TestFlight!",
success: true
)
end
# ==================== RELEASE ====================
desc "Deploy a new version to the App Store"
lane :release do
ensure_git_branch(branch: 'main')
ensure_git_status_clean
# Get version from user
version = prompt(text: "Enter version number (e.g., 1.2.0): ")
increment_version_number(
version_number: version,
xcodeproj: "MyApp.xcodeproj"
)
increment_build_number(xcodeproj: "MyApp.xcodeproj")
# Run tests before release
test
# Build
build_app(
scheme: "MyApp",
export_method: "app-store"
)
# Upload to App Store
upload_to_app_store(
submit_for_review: true,
automatic_release: false,
force: true,
precheck_include_in_app_purchases: false,
submission_information: {
add_id_info_uses_idfa: false,
export_compliance_uses_encryption: false
}
)
# Git tagging
add_git_tag(tag: "v#{version}")
commit_version_bump(message: "Release #{version} [skip ci]")
push_to_git_remote(tags: true)
slack(message: "Version #{version} submitted to App Store!")
end
# ==================== SCREENSHOTS ====================
desc "Capture screenshots"
lane :screenshots do
capture_screenshots(
scheme: "MyAppUITests",
devices: [
"iPhone 15 Pro Max",
"iPhone 15",
"iPhone SE (3rd generation)",
"iPad Pro (12.9-inch) (6th generation)"
],
languages: ["en-US", "de-DE", "ja"],
clear_previous_screenshots: true,
output_directory: "./screenshots"
)
frame_screenshots(
path: "./screenshots",
white: true
)
end
# ==================== CERTIFICATES ====================
desc "Sync certificates and profiles"
lane :sync_certs do
match(
type: "development",
readonly: is_ci
)
match(
type: "appstore",
readonly: is_ci
)
end
# ==================== ERROR HANDLING ====================
error do |lane, exception|
slack(
message: "Error in lane #{lane}: #{exception.message}",
success: false
)
end
end
GitHub Actions Workflow
# .github/workflows/ios.yml
name: iOS CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
DEVELOPER_DIR: /Applications/Xcode_15.0.app/Contents/Developer
jobs:
test:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Install dependencies
run: bundle exec pod install
- name: Run tests
run: |
bundle exec fastlane test
env:
CI: true
- name: Upload test results
uses: actions/upload-artifact@v4
if: failure()
with:
name: test-results
path: ./test_output
beta:
runs-on: macos-14
needs: test
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v4
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
bundler-cache: true
- name: Install certificates
uses: apple-actions/import-codesign-certs@v2
with:
p12-file-base64: ${{ secrets.CERTIFICATES_P12 }}
p12-password: ${{ secrets.CERTIFICATES_PASSWORD }}
- name: Install provisioning profile
uses: apple-actions/download-provisioning-profiles@v1
with:
bundle-id: com.company.myapp
issuer-id: ${{ secrets.APPSTORE_ISSUER_ID }}
api-key-id: ${{ secrets.APPSTORE_KEY_ID }}
api-private-key: ${{ secrets.APPSTORE_PRIVATE_KEY }}
- name: Deploy to TestFlight
run: bundle exec fastlane beta
env:
APP_STORE_CONNECT_API_KEY_KEY_ID: ${{ secrets.APPSTORE_KEY_ID }}
APP_STORE_CONNECT_API_KEY_ISSUER_ID: ${{ secrets.APPSTORE_ISSUER_ID }}
APP_STORE_CONNECT_API_KEY_KEY: ${{ secrets.APPSTORE_PRIVATE_KEY }}
release:
runs-on: macos-14
needs: test
if: github.ref == 'refs/heads/main' && startsWith(github.event.head_commit.message, 'Release')
steps:
- uses: actions/checkout@v4
# Similar steps as beta, but with release lane
- name: Deploy to App Store
run: bundle exec fastlane release
Xcode Cloud Configuration
# ci_scripts/ci_post_clone.sh
#!/bin/sh
# Install dependencies
brew install cocoapods
pod install
# Set up environment
echo "Setting up build environment..."
# ci_scripts/ci_pre_xcodebuild.sh
#!/bin/sh
# Run SwiftLint
if which swiftlint >/dev/null; then
swiftlint
fi
# Increment build number for CI
if [ "$CI" = "TRUE" ]; then
BUILD_NUMBER=$(date +%Y%m%d%H%M)
/usr/libexec/PlistBuddy -c "Set :CFBundleVersion $BUILD_NUMBER" "$CI_PRIMARY_REPOSITORY_PATH/MyApp/Info.plist"
fi
# ci_scripts/ci_post_xcodebuild.sh
#!/bin/sh
if [ "$CI_XCODEBUILD_EXIT_CODE" != 0 ]; then
echo "Build failed"
exit 1
fi
# Upload dSYMs to crash reporting service
if [ -d "$CI_ARCHIVE_PATH/dSYMs" ]; then
echo "Uploading dSYMs..."
# Upload to Crashlytics, Sentry, etc.
fi
App Store Metadata Template
# fastlane/metadata/en-US/description.txt
Your app's engaging description here.
Key Features:
• Feature 1 - Brief description
• Feature 2 - Brief description
• Feature 3 - Brief description
What's New:
- New functionality added
- Bug fixes and improvements
- Performance enhancements
# fastlane/metadata/en-US/keywords.txt
keyword1, keyword2, keyword3, keyword4, keyword5
# fastlane/metadata/en-US/release_notes.txt
Version X.Y.Z
NEW:
• New feature description
IMPROVED:
• Enhancement description
FIXED:
• Bug fix description
# fastlane/metadata/review_information/notes.txt
Test Account:
Email: demo@example.com
Password: TestPassword123
Notes for reviewer:
- The app requires [specific permission] to [functionality]
- Feature X can be tested by [steps]
- In-app purchase can be tested using sandbox account
Pre-Submission Checklist
// Build Script: validate_submission.swift
import Foundation
struct SubmissionValidator {
static func validate() -> [String] {
var issues: [String] = []
// Check Info.plist
if !validateInfoPlist() {
issues.append("❌ Info.plist: Missing required keys")
}
// Check privacy manifest
if !validatePrivacyManifest() {
issues.append("❌ Privacy Manifest: Required for iOS 17+")
}
// Check icons
if !validateAppIcons() {
issues.append("❌ App Icons: Missing required sizes")
}
// Check launch screen
if !validateLaunchScreen() {
issues.append("❌ Launch Screen: Not configured")
}
// Check code signing
if !validateCodeSigning() {
issues.append("❌ Code Signing: Invalid configuration")
}
if issues.isEmpty {
print("✅ All submission checks passed!")
} else {
print("⚠️ Submission issues found:")
issues.forEach { print($0) }
}
return issues
}
private static func validateInfoPlist() -> Bool {
// Check for required keys
let requiredKeys = [
"CFBundleDisplayName",
"CFBundleIdentifier",
"CFBundleShortVersionString",
"CFBundleVersion",
"UILaunchStoryboardName"
]
// Implementation
return true
}
private static func validatePrivacyManifest() -> Bool {
// Check PrivacyInfo.xcprivacy exists
return FileManager.default.fileExists(atPath: "PrivacyInfo.xcprivacy")
}
private static func validateAppIcons() -> Bool {
// Check all required icon sizes exist
return true
}
private static func validateLaunchScreen() -> Bool {
return true
}
private static func validateCodeSigning() -> Bool {
return true
}
}
Review Guidelines Quick Reference
Common Rejection Reasons
| Reason | Guideline | Resolution |
|---|
| Crashes/Bugs | 2.1 | Fix all crashes, test thoroughly |
| Incomplete info | 2.1 | Provide demo account, clear instructions |
| Placeholder content | 2.3.3 | Replace with actual content |
| Privacy violations | 5.1.1 | Add privacy labels, request permissions properly |
| Misleading metadata | 2.3 | Accurate screenshots, descriptions |
| Login required | 4.2.3 | Provide guest mode or test account |
Required Privacy Labels
# App Privacy - Data Collection
Data Types:
- Contact Info (Name, Email)
- Identifiers (Device ID, User ID)
- Usage Data (Analytics)
- Diagnostics (Crash logs)
Purposes:
- App Functionality
- Analytics
- Developer's Advertising
Linked to Identity: Yes/No
Tracking: Yes/No
Fallback Strategies
| Failure Mode | Fallback | Recovery |
|---|
| Build fails on CI | Local build, manual upload | Fix CI configuration |
| Certificate expired | Use automatic signing | Renew in Developer Portal |
| Review rejection | Address feedback, resubmit | Expedited review if critical |
| TestFlight issues | Direct IPA distribution | Ad-hoc provisioning |
Token/Cost Optimization
| Optimization | Impact | Implementation |
|---|
| Build caching | -50% CI time | Cache derived data |
| Parallel testing | -40% test time | Enable parallel execution |
| Incremental builds | -60% build time | Use Xcode build system |
| Asset optimization | -30% app size | Compress images, on-demand resources |
Troubleshooting
Common Issues
ISSUE: Code signing fails
├── Check: Certificate in Keychain
├── Check: Provisioning profile installed
├── Check: Team ID in project settings
└── Solution: Use automatic signing or sync with match
ISSUE: Upload fails
├── Check: Bundle ID matches App Store Connect
├── Check: Version/build number unique
├── Check: Valid API key for upload
└── Solution: Validate archive before upload
ISSUE: TestFlight processing stuck
├── Check: App Store Connect status
├── Check: Export compliance settings
├── Check: Missing compliance info
└── Solution: Wait or contact Apple support
ISSUE: Review rejection
├── Check: Rejection reason in Resolution Center
├── Check: Review guidelines section cited
├── Check: Screenshots match functionality
└── Solution: Address issues, provide demo video
Debug Checklist
Related Components
| Component | Relationship | Reference |
|---|
app-store skill | PRIMARY_BOND | Teaching content |
01-ios-fundamentals agent | ARCHITECTURE | App structure |
06-testing-debugging agent | TESTING | Pre-submission testing |
| CI/CD systems | INTEGRATION | Automation |
Quality Metrics
- Response accuracy: 95%+
- Submission success rate: 90%+
- First-review approval: 80%+
- CI/CD reliability: 99%+
- Documentation completeness: 100%