in lib/prepare.js [359:488]
function mapIconResources (icons, iconsDir, xcodeversion) {
// Ref: https://developer.apple.com/design/human-interface-guidelines/app-icons
// These are ordered according to how Xcode puts them in the Contents.json file
const platformIcons = [
// iOS fallback icon sizes
{ dest: 'icon-20@2x.png', width: 40, height: 40 },
{ dest: 'icon-20@3x.png', width: 60, height: 60 },
{ dest: 'icon-29@2x.png', width: 58, height: 58 },
{ dest: 'icon-29@3x.png', width: 87, height: 87 },
{ dest: 'icon-38@2x.png', width: 76, height: 76 },
{ dest: 'icon-38@3x.png', width: 114, height: 114 },
{ dest: 'icon-40@2x.png', width: 80, height: 80, target: 'spotlight' },
{ dest: 'icon-40@3x.png', width: 120, height: 120, target: 'spotlight' },
{ dest: 'icon-60@2x.png', width: 120, height: 120 },
{ dest: 'icon-60@3x.png', width: 180, height: 180 },
{ dest: 'icon-64@2x.png', width: 128, height: 128 },
{ dest: 'icon-64@3x.png', width: 192, height: 192 },
{ dest: 'icon-68@2x.png', width: 136, height: 136 },
{ dest: 'icon-76@2x.png', width: 152, height: 152 },
{ dest: 'icon-83.5@2x.png', width: 167, height: 167 },
// Default iOS icon
{ dest: 'icon.png', width: 1024, height: 1024, useDefault: true },
// macOS icon sizes
{ dest: 'mac-16.png', width: 16, height: 16, target: 'mac' },
{ dest: 'mac-16@2x.png', width: 32, height: 32, target: 'mac' },
{ dest: 'mac-32.png', width: 32, height: 32, target: 'mac' },
{ dest: 'mac-32@2x.png', width: 64, height: 64, target: 'mac' },
{ dest: 'mac-128.png', width: 128, height: 128, target: 'mac' },
{ dest: 'mac-128@2x.png', width: 256, height: 256, target: 'mac' },
{ dest: 'mac-256.png', width: 256, height: 256, target: 'mac' },
{ dest: 'mac-256@2x.png', width: 512, height: 512, target: 'mac' },
{ dest: 'mac-512.png', width: 512, height: 512, target: 'mac' },
{ dest: 'mac-512@2x.png', width: 1024, height: 1024, target: 'mac' },
// WatchOS fallback icon sizes
{ dest: 'watchos-22@2x.png', width: 44, height: 44, target: 'watchos' },
{ dest: 'watchos-24@2x.png', width: 48, height: 48, target: 'watchos' },
{ dest: 'watchos-27.5@2x.png', width: 55, height: 55, target: 'watchos' },
{ dest: 'watchos-29@2x.png', width: 58, height: 58, target: 'watchos' },
{ dest: 'watchos-30@2x.png', width: 60, height: 60, target: 'watchos' },
{ dest: 'watchos-32@2x.png', width: 64, height: 64, target: 'watchos' },
{ dest: 'watchos-33@2x.png', width: 66, height: 66, target: 'watchos' },
{ dest: 'watchos-40@2x.png', width: 80, height: 80, target: 'watchos' },
{ dest: 'watchos-43.5@2x.png', width: 87, height: 87, target: 'watchos' },
{ dest: 'watchos-44@2x.png', width: 88, height: 88, target: 'watchos' },
{ dest: 'watchos-46@2x.png', width: 92, height: 92, target: 'watchos' },
{ dest: 'watchos-50@2x.png', width: 100, height: 100, target: 'watchos' },
{ dest: 'watchos-51@2x.png', width: 102, height: 102, target: 'watchos' },
{ dest: 'watchos-54@2x.png', width: 108, height: 108, target: 'watchos' },
{ dest: 'watchos-86@2x.png', width: 172, height: 172, target: 'watchos' },
{ dest: 'watchos-98@2x.png', width: 196, height: 196, target: 'watchos' },
{ dest: 'watchos-108@2x.png', width: 216, height: 216, target: 'watchos' },
{ dest: 'watchos-117@2x.png', width: 234, height: 234, target: 'watchos' },
{ dest: 'watchos-129@2x.png', width: 258, height: 258, target: 'watchos' },
// Allow customizing the watchOS icon with target="watchos"
// This falls back to using the iOS app icon by default
{ dest: 'watchos.png', width: 1024, height: 1024, target: 'watchos', useDefault: true }
];
const pathMap = {};
// We can only support dark mode and tinted icons with Xcode 16
const isAtLeastXcode16 = versions.compareVersions(xcodeversion, '16.0.0') >= 0;
function getDefaultIconForTarget (target) {
const def = icons.filter(res => !res.width && !res.height && !res.target).pop();
if (target) {
return icons
.filter(res => res.target === target)
.filter(res => !res.width && !res.height)
.pop() || def;
}
return def;
}
function getIconBySizeAndTarget (width, height, target) {
return icons
.filter(res => res.target === target)
.find(res =>
(res.width || res.height) &&
(!res.width || (width === res.width)) &&
(!res.height || (height === res.height))
) || null;
}
platformIcons.forEach(item => {
const dest = path.join(iconsDir, item.dest);
let icon = getIconBySizeAndTarget(item.width, item.height, item.target);
if (!icon && item.target === 'spotlight') {
// Fall back to a non-targeted icon
icon = getIconBySizeAndTarget(item.width, item.height);
}
if (!icon && item.useDefault) {
if (item.target) {
icon = getIconBySizeAndTarget(item.width, item.height);
}
const defaultIcon = getDefaultIconForTarget(item.target);
if (!icon && defaultIcon) {
icon = defaultIcon;
}
}
if (icon) {
if (icon.src) {
pathMap[dest] = icon.src;
}
// Only iOS has dark/tinted icon variants
if (isAtLeastXcode16 && (!item.target || item.target === 'spotlight')) {
if (icon.foreground) {
pathMap[dest.replace('.png', '-dark.png')] = icon.foreground;
}
if (icon.monochrome) {
pathMap[dest.replace('.png', '-tinted.png')] = icon.monochrome;
}
}
}
});
return pathMap;
}