import { ExpoConfig } from '@expo/config-types' ; import fs from 'fs-extra' ; import path from 'path' ; import resolveFrom from 'resolve-from' ; import { ConfigPlugin , InfoPlist } from '../Plugin.types' ; import { createInfoPlistPlugin , withAppDelegate } from '../plugins/ios-plugins' ; import { withDangerousMod } from '../plugins/withDangerousMod' ; import { mergeContents , MergeResults , removeContents } from '../utils/generateCode' ; const debug = require ( 'debug' ) ( 'expo:config-plugins:ios:maps' ) as typeof console . log ; export const MATCH_INIT = /(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])|(\[self\.reactDelegate createBridgeWithDelegate:self launchOptions:launchOptions\])/g ; const withGoogleMapsKey = createInfoPlistPlugin ( setGoogleMapsApiKey , 'withGoogleMapsKey' ) ; export const withMaps = config => { config = withGoogleMapsKey ( config ) ; const apiKey = getGoogleMapsApiKey ( config ) ; debug ( 'Google Maps API Key:' , apiKey ) ; config = withMapsCocoaPods ( config , { useGoogleMaps : ! ! apiKey } ) ; config = withGoogleMapsAppDelegate ( config , { apiKey } ) ; return config ; } ; export function getGoogleMapsApiKey ( config ) { return config . ios ?. config ?. googleMapsApiKey ?? null ; } export function setGoogleMapsApiKey ( config , { GMSApiKey , ... infoPlist } ) { const apiKey = getGoogleMapsApiKey ( config ) ; if ( apiKey === null ) { return infoPlist ; } return { ... infoPlist , GMSApiKey : apiKey } ; } export function addGoogleMapsAppDelegateImport ( src ) { const newSrc = [ ] ; newSrc . push ( '#if __has_include(<GoogleMaps/GoogleMaps.h>)' , '#import <GoogleMaps/GoogleMaps.h>' , '#endif' ) ; return mergeContents ( { tag : 'react-native-maps-import' , src , newSrc : newSrc . join ( '\n' ) , anchor : /#import "AppDelegate\.h"/ , offset : 1 , comment : '//' } ) ; } export function removeGoogleMapsAppDelegateImport ( src ) { return removeContents ( { tag : 'react-native-maps-import' , src } ) ; } export function addGoogleMapsAppDelegateInit ( src , apiKey ) { const newSrc = [ ] ; newSrc . push ( '#if __has_include(<GoogleMaps/GoogleMaps.h>)' , `  [GMSServices provideAPIKey:@"${ apiKey }"];` , '#endif' ) ; return mergeContents ( { tag : 'react-native-maps-init' , src , newSrc : newSrc . join ( '\n' ) , anchor : MATCH_INIT , offset : 0 , comment : '//' } ) ; } export function removeGoogleMapsAppDelegateInit ( src ) { return removeContents ( { tag : 'react-native-maps-init' , src } ) ; } param src returns export function addMapsCocoaPods ( src ) { return mergeContents ( { tag : 'react-native-maps' , src , newSrc : `  pod 'react-native-google-maps', path: File.dirname(\`node --print "require.resolve('react-native-maps/package.json')"\`)` , anchor : /use_native_modules/ , offset : 0 , comment : '#' } ) ; } export function removeMapsCocoaPods ( src ) { return removeContents ( { tag : 'react-native-maps' , src } ) ; } function isReactNativeMapsInstalled ( projectRoot ) { const resolved = resolveFrom . silent ( projectRoot , 'react-native-maps/package.json' ) ; return resolved ? path . dirname ( resolved ) : null ; } function isReactNativeMapsAutolinked ( config ) { return true ; } const withMapsCocoaPods = ( config , { useGoogleMaps } ) => { return withDangerousMod ( config , [ 'ios' , async ( config ) => { const filePath = path . join ( config . modRequest . platformProjectRoot , 'Podfile' ) ; const contents = await fs . readFile ( filePath , 'utf-8' ) ; let results ; const googleMapsPath = isReactNativeMapsInstalled ( config . modRequest . projectRoot ) ; const isLinked = isReactNativeMapsAutolinked ( config ) ; debug ( 'Is Expo Autolinked:' , isLinked ) ; debug ( 'react-native-maps path:' , googleMapsPath ) ; if ( isLinked && googleMapsPath && useGoogleMaps ) { try { results = addMapsCocoaPods ( contents ) ; } catch ( error ) { if ( error . code === 'ERR_NO_MATCH' ) { throw new Error ( `Cannot add react-native-maps to the project's ios/Podfile because it's malformed. Please report this with a copy of your project Podfile.` ) ; } throw error ; } } else { results = removeMapsCocoaPods ( contents ) ; } if ( results . didMerge || results . didClear ) { await fs . writeFile ( filePath , results . contents ) ; } return config ; } , ] ) ; } ; const withGoogleMapsAppDelegate = ( config , { apiKey } ) => { return withAppDelegate ( config , config => { if ( config . modResults . language === 'objc' ) { if ( apiKey && isReactNativeMapsAutolinked ( config ) && isReactNativeMapsInstalled ( config . modRequest . projectRoot ) ) { try { config . modResults . contents = addGoogleMapsAppDelegateImport ( config . modResults . contents ) . contents ; config . modResults . contents = addGoogleMapsAppDelegateInit ( config . modResults . contents , apiKey ) . contents ; } catch ( error ) { if ( error . code === 'ERR_NO_MATCH' ) { throw new Error ( `Cannot add Google Maps to the project's AppDelegate because it's malformed. Please report this with a copy of your project AppDelegate.` ) ; } throw error ; } } else { config . modResults . contents = removeGoogleMapsAppDelegateImport ( config . modResults . contents ) . contents ; config . modResults . contents = removeGoogleMapsAppDelegateInit ( config . modResults . contents ) . contents ; } } else { throw new Error ( 'Cannot setup Google Maps because the AppDelegate is not Objective C' ) ; } return config ; } ) ; } ;