1082815dcSEvan Baconimport { ExpoConfig } from '@expo/config-types';
2082815dcSEvan Bacon
3082815dcSEvan Baconimport { InfoPlist, URLScheme } from './IosConfig.types';
4*8a424bebSJames Ideimport { createInfoPlistPluginWithPropertyGuard } from '../plugins/ios-plugins';
5082815dcSEvan Bacon
6082815dcSEvan Baconexport const withScheme = createInfoPlistPluginWithPropertyGuard(
7082815dcSEvan Bacon  setScheme,
8082815dcSEvan Bacon  {
9082815dcSEvan Bacon    infoPlistProperty: 'CFBundleURLTypes',
10082815dcSEvan Bacon    expoConfigProperty: 'scheme',
11082815dcSEvan Bacon  },
12082815dcSEvan Bacon  'withScheme'
13082815dcSEvan Bacon);
14082815dcSEvan Bacon
15082815dcSEvan Baconexport function getScheme(config: { scheme?: string | string[] }): string[] {
16082815dcSEvan Bacon  if (Array.isArray(config.scheme)) {
17082815dcSEvan Bacon    const validate = (value: any): value is string => {
18082815dcSEvan Bacon      return typeof value === 'string';
19082815dcSEvan Bacon    };
20082815dcSEvan Bacon    return config.scheme.filter<string>(validate);
21082815dcSEvan Bacon  } else if (typeof config.scheme === 'string') {
22082815dcSEvan Bacon    return [config.scheme];
23082815dcSEvan Bacon  }
24082815dcSEvan Bacon  return [];
25082815dcSEvan Bacon}
26082815dcSEvan Bacon
27082815dcSEvan Baconexport function setScheme(
28082815dcSEvan Bacon  config: Partial<Pick<ExpoConfig, 'scheme' | 'ios'>>,
29082815dcSEvan Bacon  infoPlist: InfoPlist
30082815dcSEvan Bacon): InfoPlist {
31082815dcSEvan Bacon  const scheme = [
32082815dcSEvan Bacon    ...getScheme(config),
33082815dcSEvan Bacon    // @ts-ignore: TODO: ios.scheme is an unreleased -- harder to add to turtle v1.
34082815dcSEvan Bacon    ...getScheme(config.ios ?? {}),
35082815dcSEvan Bacon  ];
36082815dcSEvan Bacon  // Add the bundle identifier to the list of schemes for easier Google auth and parity with Turtle v1.
37082815dcSEvan Bacon  if (config.ios?.bundleIdentifier) {
38082815dcSEvan Bacon    scheme.push(config.ios.bundleIdentifier);
39082815dcSEvan Bacon  }
40082815dcSEvan Bacon  if (scheme.length === 0) {
41082815dcSEvan Bacon    return infoPlist;
42082815dcSEvan Bacon  }
43082815dcSEvan Bacon
44082815dcSEvan Bacon  return {
45082815dcSEvan Bacon    ...infoPlist,
46082815dcSEvan Bacon    CFBundleURLTypes: [{ CFBundleURLSchemes: scheme }],
47082815dcSEvan Bacon  };
48082815dcSEvan Bacon}
49082815dcSEvan Bacon
50082815dcSEvan Baconexport function appendScheme(scheme: string | null, infoPlist: InfoPlist): InfoPlist {
51082815dcSEvan Bacon  if (!scheme) {
52082815dcSEvan Bacon    return infoPlist;
53082815dcSEvan Bacon  }
54082815dcSEvan Bacon
55082815dcSEvan Bacon  const existingSchemes = infoPlist.CFBundleURLTypes ?? [];
56e9630418SEvan Bacon  if (existingSchemes?.some(({ CFBundleURLSchemes }) => CFBundleURLSchemes.includes(scheme))) {
57082815dcSEvan Bacon    return infoPlist;
58082815dcSEvan Bacon  }
59082815dcSEvan Bacon
60082815dcSEvan Bacon  return {
61082815dcSEvan Bacon    ...infoPlist,
62082815dcSEvan Bacon    CFBundleURLTypes: [
63082815dcSEvan Bacon      ...existingSchemes,
64082815dcSEvan Bacon      {
65082815dcSEvan Bacon        CFBundleURLSchemes: [scheme],
66082815dcSEvan Bacon      },
67082815dcSEvan Bacon    ],
68082815dcSEvan Bacon  };
69082815dcSEvan Bacon}
70082815dcSEvan Bacon
71082815dcSEvan Baconexport function removeScheme(scheme: string | null, infoPlist: InfoPlist): InfoPlist {
72082815dcSEvan Bacon  if (!scheme) {
73082815dcSEvan Bacon    return infoPlist;
74082815dcSEvan Bacon  }
75082815dcSEvan Bacon
76082815dcSEvan Bacon  // No need to remove if we don't have any
77082815dcSEvan Bacon  if (!infoPlist.CFBundleURLTypes) {
78082815dcSEvan Bacon    return infoPlist;
79082815dcSEvan Bacon  }
80082815dcSEvan Bacon
81082815dcSEvan Bacon  infoPlist.CFBundleURLTypes = infoPlist.CFBundleURLTypes.map((bundleUrlType) => {
82082815dcSEvan Bacon    const index = bundleUrlType.CFBundleURLSchemes.indexOf(scheme);
83082815dcSEvan Bacon    if (index > -1) {
84082815dcSEvan Bacon      bundleUrlType.CFBundleURLSchemes.splice(index, 1);
85082815dcSEvan Bacon      if (bundleUrlType.CFBundleURLSchemes.length === 0) {
86082815dcSEvan Bacon        return undefined;
87082815dcSEvan Bacon      }
88082815dcSEvan Bacon    }
89082815dcSEvan Bacon    return bundleUrlType;
90082815dcSEvan Bacon  }).filter(Boolean) as URLScheme[];
91082815dcSEvan Bacon
92082815dcSEvan Bacon  return infoPlist;
93082815dcSEvan Bacon}
94082815dcSEvan Bacon
95082815dcSEvan Baconexport function hasScheme(scheme: string, infoPlist: InfoPlist): boolean {
96082815dcSEvan Bacon  const existingSchemes = infoPlist.CFBundleURLTypes;
97082815dcSEvan Bacon
98082815dcSEvan Bacon  if (!Array.isArray(existingSchemes)) return false;
99082815dcSEvan Bacon
100e9630418SEvan Bacon  return existingSchemes?.some(({ CFBundleURLSchemes: schemes }: any) =>
101082815dcSEvan Bacon    Array.isArray(schemes) ? schemes.includes(scheme) : false
102082815dcSEvan Bacon  );
103082815dcSEvan Bacon}
104082815dcSEvan Bacon
105082815dcSEvan Baconexport function getSchemesFromPlist(infoPlist: InfoPlist): string[] {
106082815dcSEvan Bacon  if (Array.isArray(infoPlist.CFBundleURLTypes)) {
107082815dcSEvan Bacon    return infoPlist.CFBundleURLTypes.reduce<string[]>((schemes, { CFBundleURLSchemes }) => {
108082815dcSEvan Bacon      if (Array.isArray(CFBundleURLSchemes)) {
109082815dcSEvan Bacon        return [...schemes, ...CFBundleURLSchemes];
110082815dcSEvan Bacon      }
111082815dcSEvan Bacon      return schemes;
112082815dcSEvan Bacon    }, []);
113082815dcSEvan Bacon  }
114082815dcSEvan Bacon  return [];
115082815dcSEvan Bacon}
116