1import { IOSConfig } from '@expo/config-plugins';
2import plist from '@expo/plist';
3import fs from 'fs';
4
5import { AppIdResolver } from '../AppIdResolver';
6
7const debug = require('debug')('expo:start:platforms:ios:AppleAppIdResolver') as typeof console.log;
8
9/** Resolves the iOS bundle identifier from the Expo config or native files. */
10export class AppleAppIdResolver extends AppIdResolver {
11  constructor(projectRoot: string) {
12    super(projectRoot, 'ios', 'ios.bundleIdentifier');
13  }
14
15  /** @return `true` if the app has valid `*.pbxproj` file */
16  async hasNativeProjectAsync(): Promise<boolean> {
17    try {
18      // Never returns nullish values.
19      return !!IOSConfig.Paths.getAllPBXProjectPaths(this.projectRoot).length;
20    } catch (error: any) {
21      debug('Expected error checking for native project:', error);
22      return false;
23    }
24  }
25
26  async resolveAppIdFromNativeAsync(): Promise<string | null> {
27    // Check xcode project
28    try {
29      const bundleId = IOSConfig.BundleIdentifier.getBundleIdentifierFromPbxproj(this.projectRoot);
30      if (bundleId) {
31        return bundleId;
32      }
33    } catch (error: any) {
34      debug('Expected error resolving the bundle identifier from the pbxproj:', error);
35    }
36
37    // Check Info.plist
38    try {
39      const infoPlistPath = IOSConfig.Paths.getInfoPlistPath(this.projectRoot);
40      const data = await plist.parse(fs.readFileSync(infoPlistPath, 'utf8'));
41      if (data.CFBundleIdentifier && !data.CFBundleIdentifier.startsWith('$(')) {
42        return data.CFBundleIdentifier;
43      }
44    } catch (error) {
45      debug('Expected error resolving the bundle identifier from the project Info.plist:', error);
46    }
47
48    return null;
49  }
50}
51