# App Icons Complete guide for generating, configuring, and managing iOS app icons from the CLI. ## Quick Start (Xcode 14+) The simplest approach—provide a single 1024×1024 PNG and let Xcode auto-generate all sizes: 1. Create `Assets.xcassets/AppIcon.appiconset/` 2. Add your 1024×1024 PNG 3. Create `Contents.json` with single-size configuration ```json { "images": [ { "filename": "icon-1024.png", "idiom": "universal", "platform": "ios", "size": "1024x1024" } ], "info": { "author": "xcode", "version": 1 } } ``` The system auto-generates all required device sizes from this single image. ## CLI Icon Generation ### Using sips (Built into macOS) Generate all required sizes from a 1024×1024 source: ```bash #!/bin/bash # generate-app-icons.sh # Usage: ./generate-app-icons.sh source.png output-dir SOURCE="$1" OUTPUT="${2:-AppIcon.appiconset}" mkdir -p "$OUTPUT" # Generate all required sizes sips -z 1024 1024 "$SOURCE" --out "$OUTPUT/icon-1024.png" sips -z 180 180 "$SOURCE" --out "$OUTPUT/icon-180.png" sips -z 167 167 "$SOURCE" --out "$OUTPUT/icon-167.png" sips -z 152 152 "$SOURCE" --out "$OUTPUT/icon-152.png" sips -z 120 120 "$SOURCE" --out "$OUTPUT/icon-120.png" sips -z 87 87 "$SOURCE" --out "$OUTPUT/icon-87.png" sips -z 80 80 "$SOURCE" --out "$OUTPUT/icon-80.png" sips -z 76 76 "$SOURCE" --out "$OUTPUT/icon-76.png" sips -z 60 60 "$SOURCE" --out "$OUTPUT/icon-60.png" sips -z 58 58 "$SOURCE" --out "$OUTPUT/icon-58.png" sips -z 40 40 "$SOURCE" --out "$OUTPUT/icon-40.png" sips -z 29 29 "$SOURCE" --out "$OUTPUT/icon-29.png" sips -z 20 20 "$SOURCE" --out "$OUTPUT/icon-20.png" echo "Generated icons in $OUTPUT" ``` ### Using ImageMagick ```bash #!/bin/bash # Requires: brew install imagemagick SOURCE="$1" OUTPUT="${2:-AppIcon.appiconset}" mkdir -p "$OUTPUT" for size in 1024 180 167 152 120 87 80 76 60 58 40 29 20; do convert "$SOURCE" -resize "${size}x${size}!" "$OUTPUT/icon-$size.png" done ``` ## Complete Contents.json (All Sizes) For manual size control or when not using single-size mode: ```json { "images": [ { "filename": "icon-1024.png", "idiom": "ios-marketing", "scale": "1x", "size": "1024x1024" }, { "filename": "icon-180.png", "idiom": "iphone", "scale": "3x", "size": "60x60" }, { "filename": "icon-120.png", "idiom": "iphone", "scale": "2x", "size": "60x60" }, { "filename": "icon-87.png", "idiom": "iphone", "scale": "3x", "size": "29x29" }, { "filename": "icon-58.png", "idiom": "iphone", "scale": "2x", "size": "29x29" }, { "filename": "icon-120.png", "idiom": "iphone", "scale": "3x", "size": "40x40" }, { "filename": "icon-80.png", "idiom": "iphone", "scale": "2x", "size": "40x40" }, { "filename": "icon-60.png", "idiom": "iphone", "scale": "3x", "size": "20x20" }, { "filename": "icon-40.png", "idiom": "iphone", "scale": "2x", "size": "20x20" }, { "filename": "icon-167.png", "idiom": "ipad", "scale": "2x", "size": "83.5x83.5" }, { "filename": "icon-152.png", "idiom": "ipad", "scale": "2x", "size": "76x76" }, { "filename": "icon-76.png", "idiom": "ipad", "scale": "1x", "size": "76x76" }, { "filename": "icon-80.png", "idiom": "ipad", "scale": "2x", "size": "40x40" }, { "filename": "icon-40.png", "idiom": "ipad", "scale": "1x", "size": "40x40" }, { "filename": "icon-58.png", "idiom": "ipad", "scale": "2x", "size": "29x29" }, { "filename": "icon-29.png", "idiom": "ipad", "scale": "1x", "size": "29x29" }, { "filename": "icon-40.png", "idiom": "ipad", "scale": "2x", "size": "20x20" }, { "filename": "icon-20.png", "idiom": "ipad", "scale": "1x", "size": "20x20" } ], "info": { "author": "xcode", "version": 1 } } ``` ## Required Sizes Reference | Purpose | Size (pt) | Scale | Pixels | Device | |---------|-----------|-------|--------|--------| | App Store | 1024×1024 | 1x | 1024 | Marketing | | Home Screen | 60×60 | 3x | 180 | iPhone | | Home Screen | 60×60 | 2x | 120 | iPhone | | Home Screen | 83.5×83.5 | 2x | 167 | iPad Pro | | Home Screen | 76×76 | 2x | 152 | iPad | | Spotlight | 40×40 | 3x | 120 | iPhone | | Spotlight | 40×40 | 2x | 80 | iPhone/iPad | | Settings | 29×29 | 3x | 87 | iPhone | | Settings | 29×29 | 2x | 58 | iPhone/iPad | | Notification | 20×20 | 3x | 60 | iPhone | | Notification | 20×20 | 2x | 40 | iPhone/iPad | ## iOS 18 Dark Mode & Tinted Icons iOS 18 adds appearance variants: Any (default), Dark, and Tinted. ### Asset Structure Create three versions of each icon: - `icon-1024.png` - Standard (Any appearance) - `icon-1024-dark.png` - Dark mode variant - `icon-1024-tinted.png` - Tinted variant ### Dark Mode Design - Use transparent background (system provides dark fill) - Keep foreground elements recognizable - Lighten foreground colors for contrast against dark background - Or provide full icon with dark-tinted background ### Tinted Design - Must be grayscale, fully opaque - System applies user's tint color over the grayscale - Use gradient background: #313131 (top) to #141414 (bottom) ### Contents.json with Appearances ```json { "images": [ { "filename": "icon-1024.png", "idiom": "universal", "platform": "ios", "size": "1024x1024" }, { "appearances": [ { "appearance": "luminosity", "value": "dark" } ], "filename": "icon-1024-dark.png", "idiom": "universal", "platform": "ios", "size": "1024x1024" }, { "appearances": [ { "appearance": "luminosity", "value": "tinted" } ], "filename": "icon-1024-tinted.png", "idiom": "universal", "platform": "ios", "size": "1024x1024" } ], "info": { "author": "xcode", "version": 1 } } ``` ## Alternate App Icons Allow users to choose between different app icons. ### Setup 1. Add alternate icon sets to asset catalog 2. Configure build setting in project.pbxproj: ``` ASSETCATALOG_COMPILER_ALTERNATE_APPICON_NAMES = "DarkIcon ColorfulIcon"; ``` Or add icons loose in project with @2x/@3x naming and configure Info.plist: ```xml CFBundleIcons CFBundleAlternateIcons DarkIcon CFBundleIconFiles DarkIcon ColorfulIcon CFBundleIconFiles ColorfulIcon CFBundlePrimaryIcon CFBundleIconFiles AppIcon ``` ### SwiftUI Implementation ```swift import SwiftUI enum AppIcon: String, CaseIterable, Identifiable { case primary = "AppIcon" case dark = "DarkIcon" case colorful = "ColorfulIcon" var id: String { rawValue } var displayName: String { switch self { case .primary: return "Default" case .dark: return "Dark" case .colorful: return "Colorful" } } var iconName: String? { self == .primary ? nil : rawValue } } @Observable class IconManager { var currentIcon: AppIcon = .primary init() { if let iconName = UIApplication.shared.alternateIconName, let icon = AppIcon(rawValue: iconName) { currentIcon = icon } } func setIcon(_ icon: AppIcon) async throws { guard UIApplication.shared.supportsAlternateIcons else { throw IconError.notSupported } try await UIApplication.shared.setAlternateIconName(icon.iconName) currentIcon = icon } enum IconError: LocalizedError { case notSupported var errorDescription: String? { "This device doesn't support alternate icons" } } } struct IconPickerView: View { @Environment(IconManager.self) private var iconManager @State private var error: Error? var body: some View { List(AppIcon.allCases) { icon in Button { Task { do { try await iconManager.setIcon(icon) } catch { self.error = error } } } label: { HStack { // Preview image (add to asset catalog) Image("\(icon.rawValue)-preview") .resizable() .frame(width: 60, height: 60) .clipShape(RoundedRectangle(cornerRadius: 12)) Text(icon.displayName) Spacer() if iconManager.currentIcon == icon { Image(systemName: "checkmark") .foregroundStyle(.blue) } } } .buttonStyle(.plain) } .navigationTitle("App Icon") .alert("Error", isPresented: .constant(error != nil)) { Button("OK") { error = nil } } message: { if let error { Text(error.localizedDescription) } } } } ``` ## Design Guidelines ### Technical Requirements - **Format**: PNG, non-interlaced - **Transparency**: Not allowed (fully opaque) - **Shape**: Square with 90° corners - **Color Space**: sRGB or Display P3 - **Minimum**: 1024×1024 for App Store ### Design Constraints 1. **No rounded corners** - System applies mask automatically 2. **No text** unless essential to brand identity 3. **No photos or screenshots** - Too detailed at small sizes 4. **No drop shadows or gloss** - System may add effects 5. **No Apple hardware** - Copyright protected 6. **No SF Symbols** - Prohibited in icons/logos ### Safe Zone The system mask cuts corners using a superellipse shape. Keep critical elements away from edges. Corner radius formula: `10/57 × icon_size` - 57px icon = 10px radius - 1024px icon ≈ 180px radius ### Test at Small Sizes Your icon must be recognizable at 29×29 pixels (Settings icon size). If details are lost, simplify the design. ## Troubleshooting ### "Missing Marketing Icon" Error Ensure you have a 1024×1024 icon with idiom `ios-marketing` in Contents.json. ### Icon Has Transparency App Store rejects icons with alpha channels. Check with: ```bash sips -g hasAlpha icon-1024.png ``` Remove alpha channel: ```bash sips -s format png -s formatOptions 0 icon-1024.png --out icon-1024-opaque.png ``` Or with ImageMagick: ```bash convert icon-1024.png -background white -alpha remove -alpha off icon-1024-opaque.png ``` ### Interlaced PNG Error Convert to non-interlaced: ```bash convert icon-1024.png -interlace none icon-1024.png ``` ### Rounded Corners Look Wrong Never pre-round your icon. Provide square corners and let iOS apply the mask. Pre-rounding causes visual artifacts where the mask doesn't align. ## Complete Generation Script One-command generation for a new project: ```bash #!/bin/bash # setup-app-icon.sh # Usage: ./setup-app-icon.sh source.png project-path SOURCE="$1" PROJECT="${2:-.}" ICONSET="$PROJECT/Assets.xcassets/AppIcon.appiconset" mkdir -p "$ICONSET" # Generate 1024x1024 (single-size mode) sips -z 1024 1024 "$SOURCE" --out "$ICONSET/icon-1024.png" # Remove alpha channel if present sips -s format png -s formatOptions 0 "$ICONSET/icon-1024.png" --out "$ICONSET/icon-1024.png" # Generate Contents.json for single-size mode cat > "$ICONSET/Contents.json" << 'EOF' { "images": [ { "filename": "icon-1024.png", "idiom": "universal", "platform": "ios", "size": "1024x1024" } ], "info": { "author": "xcode", "version": 1 } } EOF echo "App icon configured at $ICONSET" ```