App icon rasterize
Skill wei18/apple-dev-skills/apple-dev-skills/skills/app-icon-rasterize
Reusable Claude Code skills for AI-agent-driven Swift / Apple-platform development — composable via git submodule; aggregates other specialist skill repos
npx -y skills add wei18/apple-dev-skills --skill app-icon-rasterizeAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 0 stars0 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Rasterize a designer-authored 1024×1024 SVG into the PNG that Apple's asset catalog wants — no Homebrew dependency, no Cloud service, just `qlmanage` already on the Mac.
SKILL.md
9.2 KB, as published. Nobody here has run it
App icon rasterize (SVG → 1024 PNG)
Use when a designer hands off the icon as SVG and you need the PNG that AppIcon.appiconset/Contents.json references.
This is the gap discovered during first-pass icon production on a real project — the SVG → 1024 PNG step was undocumented (only the downstream Pillow Lanczos downscale to the macOS ladder was written up). Each appearance — Light, Dark, Tinted — is a single 1024 universal PNG produced by exactly one rasterize step, documented here. (Tinted is now a standard third appearance alongside Light and Dark; all apps in the reference project ship AppIcon-Tinted.png.)
When to use
- Designer subagent (or human designer) produced a clean
light.svg+dark.svg+tinted.svgmatching the icon spec - You need the matching
AppIcon-Light.png+AppIcon-Dark.pngin<app>/Assets.xcassets/AppIcon.appiconset/ - No
rsvg-convert/inkscape/imagemagickand no Homebrew —qlmanage(QuickLook) ships with macOS and is the rasteriser of record here (this skill exists precisely for the no-Homebrew case)
When NOT to use
- The SVG uses
<filter>blurs,<text>, embedded fonts, or other features QuickLook's SVG generator may render unreliably. Re-author the SVG with flat shapes first. - The SVG already ships as PNG from the designer (e.g. Affinity / Figma export). Just commit the PNG.
macOS ladder reality check
As of Xcode 26 / macOS Sequoia, macOS app icons still require the explicit 16/32/128/256/512 ladder — "Single Size" is iOS-only. (Re-verify against your current Xcode; Apple could add single-size macOS support in a later release.) The Appearances Inspector for macOS targets does not expose a single-asset option. Without the ladder, Xcode emits "AppIcon has N unassigned children" on the macOS target because the universal idiom does not satisfy AppKit's expected slots.
After producing the 1024 master:
SRC=<App>/Assets.xcassets/AppIcon.appiconset/AppIcon-Light.png
DST=<App>/Assets.xcassets/AppIcon-macOS.appiconset
mkdir -p "$DST"
cp "$SRC" "$DST/[email protected]" # 1024
sips -Z 512 "$SRC" --out "$DST/[email protected]" # 512
sips -Z 512 "$SRC" --out "$DST/icon_512x512.png" # 512
sips -Z 256 "$SRC" --out "$DST/[email protected]" # 256
sips -Z 256 "$SRC" --out "$DST/icon_256x256.png" # 256
sips -Z 128 "$SRC" --out "$DST/icon_128x128.png" # 128
sips -Z 64 "$SRC" --out "$DST/[email protected]" # 64
sips -Z 32 "$SRC" --out "$DST/icon_32x32.png" # 32
sips -Z 32 "$SRC" --out "$DST/[email protected]" # 32
sips -Z 16 "$SRC" --out "$DST/icon_16x16.png" # 16
sips -Z <max> resizes the longest dimension to <max> (Lanczos-equivalent quality). Each downscale is from the 1024 master, not the previous step.
Project.swift needs the SDK-scoped override so iOS targets keep the universal AppIcon and macOS targets pick up the ladder:
let appTargetSettings: SettingsDictionary = swiftSettings.merging([
"ASSETCATALOG_COMPILER_APPICON_NAME": "AppIcon",
"ASSETCATALOG_COMPILER_APPICON_NAME[sdk=macosx*]": "AppIcon-macOS",
]) { _, new in new }
SVG authoring contract — read BEFORE writing the SVG
The designer's SVG must NOT bake rounded corners into the artwork. Apple's compositor applies the squircle mask at render time (iOS Springboard, macOS Dock, every preview surface). Baking corners produces a double-mask look — the icon shows up smaller than its peers with visible inner padding.
Common mistakes that cause this:
<!-- WRONG — corners baked into the artwork via clipPath -->
<defs>
<clipPath id="iconMask">
<rect width="1024" height="1024" rx="246" ry="246"/>
</clipPath>
</defs>
<g clip-path="url(#iconMask)">
<rect width="1024" height="1024" fill="#FAF8F3"/>
...
</g>
<!-- ALSO WRONG — rx/ry directly on the background rect -->
<rect width="1024" height="1024" rx="246" ry="246" fill="#FAF8F3"/>
<!-- RIGHT — background fills 1024×1024 to the edges, no clip, no rx/ry -->
<rect x="0" y="0" width="1024" height="1024" fill="#FAF8F3"/>
<!-- artwork on top, also no clip -->
After rasterize, sips -g hasAlpha …png will still report yes because QuickLook writes 8-bit RGBA — but every pixel including the corners must be opaque. Quick check: open the PNG in Preview at 100%, hover the corners — Digital Color Meter should report the background color (e.g. #FAF8F3), not transparency.
This rule applies to Light, Dark, and Tinted variants. There is no platform on which the artwork should pre-apply its own squircle.
Procedure
# 1. Designer writes SVG to ../tmp/ (project-scope scratch
# dir, NOT /tmp). Each SVG must declare
# width="1024" height="1024" viewBox="0 0 1024 1024".
ls -la ../tmp/<app>-icon-light.svg ../tmp/<app>-icon-dark.svg
# 2. Rasterize via QuickLook thumbnail generator.
# -t thumbnail mode
# -s 1024 thumbnail size (square)
# -o <dir> output directory (-o cannot rename the file, see step 3)
qlmanage -t -s 1024 -o ../tmp \
../tmp/<app>-icon-light.svg \
../tmp/<app>-icon-dark.svg \
../tmp/<app>-icon-tinted.svg
# 3. qlmanage's quirk: it APPENDS `.png` to the source filename instead of
# swapping the extension. So `light.svg` becomes `light.svg.png`. Rename
# directly into the asset catalog so the .svg.png artifact never lingers.
mv ../tmp/<app>-icon-light.svg.png \
<App>/Assets.xcassets/AppIcon.appiconset/AppIcon-Light.png
mv ../tmp/<app>-icon-dark.svg.png \
<App>/Assets.xcassets/AppIcon.appiconset/AppIcon-Dark.png
mv ../tmp/<app>-icon-tinted.svg.png \
<App>/Assets.xcassets/AppIcon.appiconset/AppIcon-Tinted.png
# 4. Verify dimensions + format.
file <App>/Assets.xcassets/AppIcon.appiconset/AppIcon-Light.png
# expected: PNG image data, 1024 x 1024, 8-bit/color RGBA, non-interlaced
sips -g pixelWidth -g pixelHeight <App>/Assets.xcassets/AppIcon.appiconset/AppIcon-Light.png
# expected: pixelWidth: 1024 / pixelHeight: 1024
# 5. Commit the SVG source-of-truth alongside, under docs/app-store/icons/.
mkdir -p docs/app-store/icons/<app>/
cp ../tmp/<app>-icon-light.svg docs/app-store/icons/<app>/light.svg
cp ../tmp/<app>-icon-dark.svg docs/app-store/icons/<app>/dark.svg
cp ../tmp/<app>-icon-tinted.svg docs/app-store/icons/<app>/tinted.svg
Verify visually
Open the produced PNGs in Preview.app and confirm:
- Background fill reaches all 4 edges (no transparency at canvas border — Apple's compositor adds the squircle mask)
- Light variant uses paper
#FAF8F3background - Dark variant uses a near-black ink background (e.g.
#15171A— replace with your project's dark background hex); pixel-meter the corners against it - Colors match the spec hex values byte-for-byte
- No anti-alias bleed from
<filter>effects (rare with QuickLook, but inspect the highlight + spark ring areas) - Both PNGs are 1024×1024 exactly (no off-by-one from QuickLook scaling)
If any check fails, the fix lives in the SVG, not in a post-process pass — re-edit, re-rasterize.
Asset-catalog metadata
AppIcon.appiconset/Contents.json must reference both PNGs as universal idiom (no "platform": "ios"), so Apple auto-adapts the 1024 master across iOS + macOS:
{
"images" : [
{
"filename" : "AppIcon-Light.png",
"idiom" : "universal",
"size" : "1024x1024"
},
{
"appearances" : [
{ "appearance" : "luminosity", "value" : "dark" }
],
"filename" : "AppIcon-Dark.png",
"idiom" : "universal",
"size" : "1024x1024"
}
],
"info" : { "author" : "xcode", "version" : 1 }
}
Tinted entry present — AppIcon-Tinted.png, appearance: luminosity / value: tinted, universal idiom. For iOS only — the universal single PNG does NOT satisfy AppKit's macOS slots. macOS targets must use a separate AppIcon-macOS.appiconset with the explicit size ladder (see "macOS ladder reality check" above).
Why qlmanage and not X
| Tool | Reason rejected |
|---|---|
rsvg-convert (librsvg) | Requires Homebrew, not installed on this Mac |
inkscape --export-png | Requires Homebrew or .app install |
imagemagick convert | Requires Homebrew |
sips | Does not read SVG input on macOS as of Sequoia |
cairosvg (Python) | Requires Homebrew (Cairo system lib) |
| Pillow | No SVG support natively |
| Swift + WebKit CLI | Works but ~80 LOC of bespoke code for a one-step thumbnail render that qlmanage already does |
| Browser screenshot | Manual, unreproducible across machines |
qlmanage ships with macOS, takes one command, outputs the exact PNG shape Apple wants. The trade-off is that QuickLook's SVG generator may diverge slightly from full-spec SVG 1.1 — keep the SVG simple (flat shapes, no filters, no text) and the output is faithful.
Companion skill
The macOS size-ladder downscale workflow is documented in the "macOS ladder reality check" section above; it is superseded by the universal single-PNG approach for new projects, but remains useful as a reference for older Xcode targets that still require the explicit size ladder.