App distribution gets your app to users. Learn about TestFlight beta testing and App Store submission.
App Signing
Certificates and Provisioning
# Create development certificate
openssl req -new -newkey rsa:2048 -nodes -keyout Development.key -out Development.csr
# Create distribution certificate
openssl req -new -newkey rsa:2048 -nodes -keyout Distribution.key -out Distribution.csr
# Export as .p12
openssl pkcs12 -export -clcerts -inkey Development.key -in Development.csr -out Development.p12TestFlight Distribution
Internal Testing
- 1Archive App
- Product > Archive in Xcode - Validate archive - Distribute App
- 1Upload to App Store Connect
- Select "TestFlight & App Store" - Choose signing certificate - Upload build
- 1Add Internal Testers
- Go to App Store Connect - TestFlight > Internal Testing - Add testers by email
External Testing
// Add TestFlight beta feedback
struct TestFlightFeedbackView: View {
var body: some View {
VStack {
Text("Beta Testing")
.font(.title)
Text("Thanks for testing our app!")
.foregroundColor(.secondary)
Button("Send Feedback") {
sendFeedback()
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
private func sendFeedback() {
let email = "feedback@yourapp.com"
let subject = "Beta Feedback"
let body = "Please describe your feedback here"
if let url = URL(string: "mailto:\(email)?subject=\(subject)&body=\(body)") {
UIApplication.shared.open(url)
}
}
}App Store Submission
Prepare Metadata
- 1App Information
- Name (30 chars max) - Subtitle (30 chars max) - Description (4000 chars max) - Keywords (100 chars max) - Support URL - Marketing URL
- 1Screenshots
- Required for all device sizes - iPhone 6.7", 6.5", 5.5" - iPad Pro 12.9", iPad 11"
- 1App Preview Videos (Optional)
- 15-30 seconds - Device-specific
App Store Connect
// Add version check in app
struct VersionChecker: View {
@State private var updateAvailable = false
@State private var appStoreURL = URL(string: "https://apps.apple.com/app/yourapp")!
var body: some View {
VStack {
if updateAvailable {
Button("Update Available") {
UIApplication.shared.open(appStoreURL)
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
.onAppear {
checkForUpdates()
}
}
private func checkForUpdates() {
// Check app store version
let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? ""
Task {
do {
let version = try await getAppStoreVersion()
if version != currentVersion {
await MainActor.run {
updateAvailable = true
}
}
} catch {
print("Version check failed: \(error)")
}
}
}
private func getAppStoreVersion() async throws -> String {
// Implement App Store version lookup
return "1.0.0"
}
}In-App Purchase Setup
Configure Products
- 1Create Products
- App Store Connect > Features > In-App Purchases - Create product IDs - Set pricing
- 1Submit for Review
- Add screenshot - Provide review information
App Review Guidelines
Common Issues to Avoid
- 1Crashes: App must not crash
- 2Broken Features: All features must work
- 3Web Apps: Must have native functionality
- 4Apple Hardware: Use Apple features appropriately
- 5Privacy: Include privacy policy
- 6Permissions: Explain why you need permissions
Prepare for Review
// Add demo mode for reviewers
struct DemoModeView: View {
@State private var isDemoMode = false
var body: some View {
VStack {
Toggle("Demo Mode", isOn: $isDemoMode)
if isDemoMode {
Text("Demo Mode Active")
.foregroundColor(.orange)
}
}
.padding()
}
}
// Add review notes
/*
Review Notes:
- Account provided for testing: test@example.com / password123
- In-app purchases are test products
- Requires location services for core functionality
*/Release Strategy
Phased Release
- 1Phased Release
- 1% for first 2 days - Increase gradually - Monitor crash reports
- 1Release Notes
- Highlight new features - Mention bug fixes - Keep it concise
Version Management
struct AppVersion {
let major: Int
let minor: Int
let patch: Int
var description: String {
"\(major).\(minor).\(patch)"
}
static let current = AppVersion(major: 1, minor: 0, patch: 0)
func shouldUpdate(to version: AppVersion) -> Bool {
return version.major > major ||
(version.major == major && version.minor > minor) ||
(version.major == major && version.minor == minor && version.patch > patch)
}
}Analytics Integration
App Analytics
import AppTrackingTransparency
class AnalyticsManager: ObservableObject {
func requestTrackingAuthorization() async {
if #available(iOS 14, *) {
let status = await ATTrackingManager.requestTrackingAuthorization()
switch status {
case .authorized:
print("Tracking authorized")
case .denied:
print("Tracking denied")
case .notDetermined:
print("Tracking not determined")
@unknown default:
break
}
}
}
}Beta Testing
Crash Reporting
// Add crash reporting
import Crashlytics
class CrashReportManager {
func setupCrashReporting() {
#if DEBUG
// Disable crash reporting in debug
#else
// Initialize crash reporting
#endif
}
func logError(_ error: Error) {
// Log non-fatal errors
print("Error logged: \(error)")
}
func testCrash() {
// Test crash reporting
fatalError("Test crash")
}
}Best Practices
- 1Testing: Test thoroughly on real devices
- 2Beta: Use TestFlight for beta testing
- 3Screenshots: Provide high-quality screenshots
- 4Description: Write clear, concise descriptions
- 5Update: Regular updates keep app relevant
- 6Reviews: Monitor and respond to reviews
- 7Analytics: Track usage and crashes
- 8Guidelines: Follow App Store guidelines
Proper distribution ensures your app reaches users successfully!