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  async hasNativeProjectAsync(): Promise<boolean> {
16    try {
17      // Never returns nullish values.
18      return !!IOSConfig.Paths.getAppDelegateFilePath(this.projectRoot);
19    } catch (error: any) {
20      debug('Expected error checking for native project:', error);
21      return false;
22    }
23  }
24
25  async resolveAppIdFromNativeAsync(): Promise<string | null> {
26    // Check xcode project
27    try {
28      const bundleId = IOSConfig.BundleIdentifier.getBundleIdentifierFromPbxproj(this.projectRoot);
29      if (bundleId) {
30        return bundleId;
31      }
32    } catch (error: any) {
33      debug('Expected error resolving the bundle identifier from the pbxproj:', error);
34    }
35
36    // Check Info.plist
37    try {
38      const infoPlistPath = IOSConfig.Paths.getInfoPlistPath(this.projectRoot);
39      const data = await plist.parse(fs.readFileSync(infoPlistPath, 'utf8'));
40      if (data.CFBundleIdentifier && !data.CFBundleIdentifier.startsWith('$(')) {
41        return data.CFBundleIdentifier;
42      }
43    } catch (error) {
44      debug('Expected error resolving the bundle identifier from the project Info.plist:', error);
45    }
46
47    return null;
48  }
49}
50