1import { ExpoConfig } from '@expo/config-types'; 2import plist from '@expo/plist'; 3import assert from 'assert'; 4import fs from 'fs'; 5import path from 'path'; 6import { XcodeProject } from 'xcode'; 7 8import { ConfigPlugin, ModProps } from '../Plugin.types'; 9import { withInfoPlist, withXcodeProject } from '../plugins/ios-plugins'; 10import { InfoPlist } from './IosConfig.types'; 11import { getSourceRoot } from './Paths'; 12import { appendScheme } from './Scheme'; 13import { addResourceFileToGroup, getProjectName } from './utils/Xcodeproj'; 14 15export const withGoogle: ConfigPlugin = (config) => { 16 return withInfoPlist(config, (config) => { 17 config.modResults = setGoogleConfig(config, config.modResults, config.modRequest); 18 return config; 19 }); 20}; 21 22export const withGoogleServicesFile: ConfigPlugin = (config) => { 23 return withXcodeProject(config, (config) => { 24 config.modResults = setGoogleServicesFile(config, { 25 projectRoot: config.modRequest.projectRoot, 26 project: config.modResults, 27 }); 28 return config; 29 }); 30}; 31 32function readGoogleServicesInfoPlist( 33 relativePath: string, 34 { projectRoot }: { projectRoot: string } 35) { 36 const googleServiceFilePath = path.resolve(projectRoot, relativePath); 37 const contents = fs.readFileSync(googleServiceFilePath, 'utf8'); 38 assert(contents, 'GoogleService-Info.plist is empty'); 39 return plist.parse(contents); 40} 41 42export function getGoogleSignInReservedClientId( 43 config: Pick<ExpoConfig, 'ios'>, 44 modRequest: Pick<ModProps<InfoPlist>, 'projectRoot'> 45): string | null { 46 const reservedClientId = config.ios?.config?.googleSignIn?.reservedClientId ?? null; 47 if (reservedClientId) { 48 return reservedClientId; 49 } 50 51 const googleServicesFileRelativePath = getGoogleServicesFile(config); 52 if (googleServicesFileRelativePath === null) { 53 return null; 54 } 55 56 const infoPlist = readGoogleServicesInfoPlist(googleServicesFileRelativePath, modRequest); 57 58 return infoPlist.REVERSED_CLIENT_ID ?? null; 59} 60 61export function getGoogleServicesFile(config: Pick<ExpoConfig, 'ios'>) { 62 return config.ios?.googleServicesFile ?? null; 63} 64 65export function setGoogleSignInReservedClientId( 66 config: Pick<ExpoConfig, 'ios'>, 67 infoPlist: InfoPlist, 68 modRequest: Pick<ModProps<InfoPlist>, 'projectRoot'> 69): InfoPlist { 70 const reservedClientId = getGoogleSignInReservedClientId(config, modRequest); 71 72 if (reservedClientId === null) { 73 return infoPlist; 74 } 75 76 return appendScheme(reservedClientId, infoPlist); 77} 78 79export function setGoogleConfig( 80 config: Pick<ExpoConfig, 'ios'>, 81 infoPlist: InfoPlist, 82 modRequest: ModProps<InfoPlist> 83): InfoPlist { 84 infoPlist = setGoogleSignInReservedClientId(config, infoPlist, modRequest); 85 return infoPlist; 86} 87 88export function setGoogleServicesFile( 89 config: Pick<ExpoConfig, 'ios'>, 90 { projectRoot, project }: { project: XcodeProject; projectRoot: string } 91): XcodeProject { 92 const googleServicesFileRelativePath = getGoogleServicesFile(config); 93 if (googleServicesFileRelativePath === null) { 94 return project; 95 } 96 97 const googleServiceFilePath = path.resolve(projectRoot, googleServicesFileRelativePath); 98 fs.copyFileSync( 99 googleServiceFilePath, 100 path.join(getSourceRoot(projectRoot), 'GoogleService-Info.plist') 101 ); 102 103 const projectName = getProjectName(projectRoot); 104 const plistFilePath = `${projectName}/GoogleService-Info.plist`; 105 if (!project.hasFile(plistFilePath)) { 106 project = addResourceFileToGroup({ 107 filepath: plistFilePath, 108 groupName: projectName, 109 project, 110 isBuildFile: true, 111 verbose: true, 112 }); 113 } 114 return project; 115} 116