1import { ExpoConfig } from '@expo/config-types'; 2import { XcodeProject } from 'xcode'; 3 4import { InfoPlist } from './IosConfig.types'; 5import { findFirstNativeTarget } from './Target'; 6import { 7 ConfigurationSectionEntry, 8 getBuildConfigurationsForListId, 9 sanitizedName, 10} from './utils/Xcodeproj'; 11import { ConfigPlugin } from '../Plugin.types'; 12import { createInfoPlistPluginWithPropertyGuard, withXcodeProject } from '../plugins/ios-plugins'; 13 14export const withDisplayName = createInfoPlistPluginWithPropertyGuard( 15 setDisplayName, 16 { 17 infoPlistProperty: 'CFBundleDisplayName', 18 expoConfigProperty: 'name', 19 }, 20 'withDisplayName' 21); 22 23export const withName = createInfoPlistPluginWithPropertyGuard( 24 setName, 25 { 26 infoPlistProperty: 'CFBundleName', 27 expoConfigProperty: 'name', 28 }, 29 'withName' 30); 31 32/** Set the PRODUCT_NAME variable in the xcproj file based on the app.json name property. */ 33export const withProductName: ConfigPlugin = (config) => { 34 return withXcodeProject(config, (config) => { 35 config.modResults = setProductName(config, config.modResults); 36 return config; 37 }); 38}; 39 40export function getName(config: Pick<ExpoConfig, 'name'>) { 41 return typeof config.name === 'string' ? config.name : null; 42} 43 44/** 45 * CFBundleDisplayName is used for most things: the name on the home screen, in 46 * notifications, and others. 47 */ 48export function setDisplayName( 49 configOrName: Pick<ExpoConfig, 'name'> | string, 50 { CFBundleDisplayName, ...infoPlist }: InfoPlist 51): InfoPlist { 52 let name: string | null = null; 53 if (typeof configOrName === 'string') { 54 name = configOrName; 55 } else { 56 name = getName(configOrName); 57 } 58 59 if (!name) { 60 return infoPlist; 61 } 62 63 return { 64 ...infoPlist, 65 CFBundleDisplayName: name, 66 }; 67} 68 69/** 70 * CFBundleName is recommended to be 16 chars or less and is used in lists, eg: 71 * sometimes on the App Store 72 */ 73export function setName( 74 config: Pick<ExpoConfig, 'name'>, 75 { CFBundleName, ...infoPlist }: InfoPlist 76): InfoPlist { 77 const name = getName(config); 78 79 if (!name) { 80 return infoPlist; 81 } 82 83 return { 84 ...infoPlist, 85 CFBundleName: name, 86 }; 87} 88 89export function setProductName( 90 config: Pick<ExpoConfig, 'name'>, 91 project: XcodeProject 92): XcodeProject { 93 const name = sanitizedName(getName(config) ?? ''); 94 95 if (!name) { 96 return project; 97 } 98 const quotedName = ensureQuotes(name); 99 100 const [, nativeTarget] = findFirstNativeTarget(project); 101 102 getBuildConfigurationsForListId(project, nativeTarget.buildConfigurationList).forEach( 103 ([, item]: ConfigurationSectionEntry) => { 104 item.buildSettings.PRODUCT_NAME = quotedName; 105 } 106 ); 107 108 return project; 109} 110 111const ensureQuotes = (value: string) => { 112 if (!value.match(/^['"]/)) { 113 return `"${value}"`; 114 } 115 return value; 116}; 117