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 { InfoPlist } from './IosConfig.types';
9import { getSourceRoot } from './Paths';
10import { appendScheme } from './Scheme';
11import { addResourceFileToGroup, getProjectName } from './utils/Xcodeproj';
12import { ConfigPlugin, ModProps } from '../Plugin.types';
13import { withInfoPlist, withXcodeProject } from '../plugins/ios-plugins';
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 getGoogleSignInReversedClientId(
43  config: Pick<ExpoConfig, 'ios'>,
44  modRequest: Pick<ModProps<InfoPlist>, 'projectRoot'>
45): string | null {
46  const googleServicesFileRelativePath = getGoogleServicesFile(config);
47  if (googleServicesFileRelativePath === null) {
48    return null;
49  }
50
51  const infoPlist = readGoogleServicesInfoPlist(googleServicesFileRelativePath, modRequest);
52
53  return infoPlist.REVERSED_CLIENT_ID ?? null;
54}
55
56export function getGoogleServicesFile(config: Pick<ExpoConfig, 'ios'>) {
57  return config.ios?.googleServicesFile ?? null;
58}
59
60export function setGoogleSignInReversedClientId(
61  config: Pick<ExpoConfig, 'ios'>,
62  infoPlist: InfoPlist,
63  modRequest: Pick<ModProps<InfoPlist>, 'projectRoot'>
64): InfoPlist {
65  const reversedClientId = getGoogleSignInReversedClientId(config, modRequest);
66
67  if (reversedClientId === null) {
68    return infoPlist;
69  }
70
71  return appendScheme(reversedClientId, infoPlist);
72}
73
74export function setGoogleConfig(
75  config: Pick<ExpoConfig, 'ios'>,
76  infoPlist: InfoPlist,
77  modRequest: ModProps<InfoPlist>
78): InfoPlist {
79  infoPlist = setGoogleSignInReversedClientId(config, infoPlist, modRequest);
80  return infoPlist;
81}
82
83export function setGoogleServicesFile(
84  config: Pick<ExpoConfig, 'ios'>,
85  { projectRoot, project }: { project: XcodeProject; projectRoot: string }
86): XcodeProject {
87  const googleServicesFileRelativePath = getGoogleServicesFile(config);
88  if (googleServicesFileRelativePath === null) {
89    return project;
90  }
91
92  const googleServiceFilePath = path.resolve(projectRoot, googleServicesFileRelativePath);
93  fs.copyFileSync(
94    googleServiceFilePath,
95    path.join(getSourceRoot(projectRoot), 'GoogleService-Info.plist')
96  );
97
98  const projectName = getProjectName(projectRoot);
99  const plistFilePath = `${projectName}/GoogleService-Info.plist`;
100  if (!project.hasFile(plistFilePath)) {
101    project = addResourceFileToGroup({
102      filepath: plistFilePath,
103      groupName: projectName,
104      project,
105      isBuildFile: true,
106      verbose: true,
107    });
108  }
109  return project;
110}
111