1082815dcSEvan Baconimport { ExpoConfig } from '@expo/config-types';
2082815dcSEvan Baconimport { JSONObject } from '@expo/json-file';
3082815dcSEvan Baconimport fs from 'fs';
4082815dcSEvan Baconimport path from 'path';
5082815dcSEvan Baconimport slash from 'slash';
6082815dcSEvan Baconimport { XCBuildConfiguration } from 'xcode';
7082815dcSEvan Bacon
8082815dcSEvan Baconimport { findFirstNativeTarget, getXCBuildConfigurationFromPbxproj } from './Target';
9082815dcSEvan Baconimport {
10082815dcSEvan Bacon  getBuildConfigurationsForListId,
11082815dcSEvan Bacon  getPbxproj,
12082815dcSEvan Bacon  getProductName,
13082815dcSEvan Bacon  getProjectName,
14082815dcSEvan Bacon} from './utils/Xcodeproj';
15082815dcSEvan Baconimport { trimQuotes } from './utils/string';
16*8a424bebSJames Ideimport { createEntitlementsPlugin } from '../plugins/ios-plugins';
17082815dcSEvan Bacon
18082815dcSEvan Baconexport const withAssociatedDomains = createEntitlementsPlugin(
19082815dcSEvan Bacon  setAssociatedDomains,
20082815dcSEvan Bacon  'withAssociatedDomains'
21082815dcSEvan Bacon);
22082815dcSEvan Bacon
23082815dcSEvan Baconexport function setAssociatedDomains(
24082815dcSEvan Bacon  config: ExpoConfig,
25082815dcSEvan Bacon  { 'com.apple.developer.associated-domains': _, ...entitlementsPlist }: JSONObject
26082815dcSEvan Bacon): JSONObject {
27082815dcSEvan Bacon  if (config.ios?.associatedDomains) {
28082815dcSEvan Bacon    return {
29082815dcSEvan Bacon      ...entitlementsPlist,
30082815dcSEvan Bacon      'com.apple.developer.associated-domains': config.ios.associatedDomains,
31082815dcSEvan Bacon    };
32082815dcSEvan Bacon  }
33082815dcSEvan Bacon
34082815dcSEvan Bacon  return entitlementsPlist;
35082815dcSEvan Bacon}
36082815dcSEvan Bacon
37082815dcSEvan Baconexport function getEntitlementsPath(
38082815dcSEvan Bacon  projectRoot: string,
39082815dcSEvan Bacon  {
40082815dcSEvan Bacon    targetName,
41082815dcSEvan Bacon    buildConfiguration = 'Release',
42082815dcSEvan Bacon  }: { targetName?: string; buildConfiguration?: string } = {}
43082815dcSEvan Bacon): string | null {
44082815dcSEvan Bacon  const project = getPbxproj(projectRoot);
45082815dcSEvan Bacon  const xcBuildConfiguration = getXCBuildConfigurationFromPbxproj(project, {
46082815dcSEvan Bacon    targetName,
47082815dcSEvan Bacon    buildConfiguration,
48082815dcSEvan Bacon  });
49082815dcSEvan Bacon  if (!xcBuildConfiguration) {
50082815dcSEvan Bacon    return null;
51082815dcSEvan Bacon  }
52082815dcSEvan Bacon  const entitlementsPath = getEntitlementsPathFromBuildConfiguration(
53082815dcSEvan Bacon    projectRoot,
54082815dcSEvan Bacon    xcBuildConfiguration
55082815dcSEvan Bacon  );
56082815dcSEvan Bacon  return entitlementsPath && fs.existsSync(entitlementsPath) ? entitlementsPath : null;
57082815dcSEvan Bacon}
58082815dcSEvan Bacon
59082815dcSEvan Baconfunction getEntitlementsPathFromBuildConfiguration(
60082815dcSEvan Bacon  projectRoot: string,
61082815dcSEvan Bacon  xcBuildConfiguration: XCBuildConfiguration
62082815dcSEvan Bacon): string | null {
63082815dcSEvan Bacon  const entitlementsPathRaw = xcBuildConfiguration?.buildSettings?.CODE_SIGN_ENTITLEMENTS as
64082815dcSEvan Bacon    | string
65082815dcSEvan Bacon    | undefined;
66082815dcSEvan Bacon  if (entitlementsPathRaw) {
67082815dcSEvan Bacon    return path.normalize(path.join(projectRoot, 'ios', trimQuotes(entitlementsPathRaw)));
68082815dcSEvan Bacon  } else {
69082815dcSEvan Bacon    return null;
70082815dcSEvan Bacon  }
71082815dcSEvan Bacon}
72082815dcSEvan Bacon
73082815dcSEvan Baconexport function ensureApplicationTargetEntitlementsFileConfigured(projectRoot: string): void {
74082815dcSEvan Bacon  const project = getPbxproj(projectRoot);
75082815dcSEvan Bacon  const projectName = getProjectName(projectRoot);
76082815dcSEvan Bacon  const productName = getProductName(project);
77082815dcSEvan Bacon
78082815dcSEvan Bacon  const [, applicationTarget] = findFirstNativeTarget(project);
79082815dcSEvan Bacon  const buildConfigurations = getBuildConfigurationsForListId(
80082815dcSEvan Bacon    project,
81082815dcSEvan Bacon    applicationTarget.buildConfigurationList
82082815dcSEvan Bacon  );
83082815dcSEvan Bacon  let hasChangesToWrite = false;
84082815dcSEvan Bacon  for (const [, xcBuildConfiguration] of buildConfigurations) {
85082815dcSEvan Bacon    const oldEntitlementPath = getEntitlementsPathFromBuildConfiguration(
86082815dcSEvan Bacon      projectRoot,
87082815dcSEvan Bacon      xcBuildConfiguration
88082815dcSEvan Bacon    );
89082815dcSEvan Bacon    if (oldEntitlementPath && fs.existsSync(oldEntitlementPath)) {
90082815dcSEvan Bacon      return;
91082815dcSEvan Bacon    }
92082815dcSEvan Bacon    hasChangesToWrite = true;
93082815dcSEvan Bacon    // Use posix formatted path, even on Windows
94082815dcSEvan Bacon    const entitlementsRelativePath = slash(path.join(projectName, `${productName}.entitlements`));
95082815dcSEvan Bacon    const entitlementsPath = path.normalize(
96082815dcSEvan Bacon      path.join(projectRoot, 'ios', entitlementsRelativePath)
97082815dcSEvan Bacon    );
98082815dcSEvan Bacon    fs.mkdirSync(path.dirname(entitlementsPath), { recursive: true });
99082815dcSEvan Bacon    if (!fs.existsSync(entitlementsPath)) {
100082815dcSEvan Bacon      fs.writeFileSync(entitlementsPath, ENTITLEMENTS_TEMPLATE);
101082815dcSEvan Bacon    }
102082815dcSEvan Bacon    xcBuildConfiguration.buildSettings.CODE_SIGN_ENTITLEMENTS = entitlementsRelativePath;
103082815dcSEvan Bacon  }
104082815dcSEvan Bacon  if (hasChangesToWrite) {
105082815dcSEvan Bacon    fs.writeFileSync(project.filepath, project.writeSync());
106082815dcSEvan Bacon  }
107082815dcSEvan Bacon}
108082815dcSEvan Bacon
109082815dcSEvan Baconconst ENTITLEMENTS_TEMPLATE = `
110082815dcSEvan Bacon<?xml version="1.0" encoding="UTF-8"?>
111082815dcSEvan Bacon<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
112082815dcSEvan Bacon<plist version="1.0">
113082815dcSEvan Bacon<dict>
114082815dcSEvan Bacon</dict>
115082815dcSEvan Bacon</plist>
116082815dcSEvan Bacon`;
117