1import { ExpoConfig } from '@expo/config-types'; 2 3import { InfoPlist, URLScheme } from './IosConfig.types'; 4import { createInfoPlistPluginWithPropertyGuard } from '../plugins/ios-plugins'; 5 6export const withScheme = createInfoPlistPluginWithPropertyGuard( 7 setScheme, 8 { 9 infoPlistProperty: 'CFBundleURLTypes', 10 expoConfigProperty: 'scheme', 11 }, 12 'withScheme' 13); 14 15export function getScheme(config: { scheme?: string | string[] }): string[] { 16 if (Array.isArray(config.scheme)) { 17 const validate = (value: any): value is string => { 18 return typeof value === 'string'; 19 }; 20 return config.scheme.filter<string>(validate); 21 } else if (typeof config.scheme === 'string') { 22 return [config.scheme]; 23 } 24 return []; 25} 26 27export function setScheme( 28 config: Partial<Pick<ExpoConfig, 'scheme' | 'ios'>>, 29 infoPlist: InfoPlist 30): InfoPlist { 31 const scheme = [ 32 ...getScheme(config), 33 // @ts-ignore: TODO: ios.scheme is an unreleased -- harder to add to turtle v1. 34 ...getScheme(config.ios ?? {}), 35 ]; 36 // Add the bundle identifier to the list of schemes for easier Google auth and parity with Turtle v1. 37 if (config.ios?.bundleIdentifier) { 38 scheme.push(config.ios.bundleIdentifier); 39 } 40 if (scheme.length === 0) { 41 return infoPlist; 42 } 43 44 return { 45 ...infoPlist, 46 CFBundleURLTypes: [{ CFBundleURLSchemes: scheme }], 47 }; 48} 49 50export function appendScheme(scheme: string | null, infoPlist: InfoPlist): InfoPlist { 51 if (!scheme) { 52 return infoPlist; 53 } 54 55 const existingSchemes = infoPlist.CFBundleURLTypes ?? []; 56 if (existingSchemes?.some(({ CFBundleURLSchemes }) => CFBundleURLSchemes.includes(scheme))) { 57 return infoPlist; 58 } 59 60 return { 61 ...infoPlist, 62 CFBundleURLTypes: [ 63 ...existingSchemes, 64 { 65 CFBundleURLSchemes: [scheme], 66 }, 67 ], 68 }; 69} 70 71export function removeScheme(scheme: string | null, infoPlist: InfoPlist): InfoPlist { 72 if (!scheme) { 73 return infoPlist; 74 } 75 76 // No need to remove if we don't have any 77 if (!infoPlist.CFBundleURLTypes) { 78 return infoPlist; 79 } 80 81 infoPlist.CFBundleURLTypes = infoPlist.CFBundleURLTypes.map((bundleUrlType) => { 82 const index = bundleUrlType.CFBundleURLSchemes.indexOf(scheme); 83 if (index > -1) { 84 bundleUrlType.CFBundleURLSchemes.splice(index, 1); 85 if (bundleUrlType.CFBundleURLSchemes.length === 0) { 86 return undefined; 87 } 88 } 89 return bundleUrlType; 90 }).filter(Boolean) as URLScheme[]; 91 92 return infoPlist; 93} 94 95export function hasScheme(scheme: string, infoPlist: InfoPlist): boolean { 96 const existingSchemes = infoPlist.CFBundleURLTypes; 97 98 if (!Array.isArray(existingSchemes)) return false; 99 100 return existingSchemes?.some(({ CFBundleURLSchemes: schemes }: any) => 101 Array.isArray(schemes) ? schemes.includes(scheme) : false 102 ); 103} 104 105export function getSchemesFromPlist(infoPlist: InfoPlist): string[] { 106 if (Array.isArray(infoPlist.CFBundleURLTypes)) { 107 return infoPlist.CFBundleURLTypes.reduce<string[]>((schemes, { CFBundleURLSchemes }) => { 108 if (Array.isArray(CFBundleURLSchemes)) { 109 return [...schemes, ...CFBundleURLSchemes]; 110 } 111 return schemes; 112 }, []); 113 } 114 return []; 115} 116