This repository was archived by the owner on Jan 18, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 476
Expand file tree
/
Copy pathMaps.ts
More file actions
215 lines (190 loc) · 6.74 KB
/
Maps.ts
File metadata and controls
215 lines (190 loc) · 6.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
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;
// Match against `UMModuleRegistryAdapter` (unimodules), and React Native without unimodules (Expo Modules).
export const MATCH_INIT = /(?:(self\.|_)(\w+)\s?=\s?\[\[UMModuleRegistryAdapter alloc\])|(?:RCTBridge\s?\*\s?(\w+)\s?=\s?\[\[RCTBridge alloc\])/g;
const withGoogleMapsKey = createInfoPlistPlugin(setGoogleMapsApiKey, 'withGoogleMapsKey');
export const withMaps: ConfigPlugin = config => {
config = withGoogleMapsKey(config);
const apiKey = getGoogleMapsApiKey(config);
// Technically adds react-native-maps (Apple maps) and google maps.
debug('Google Maps API Key:', apiKey);
config = withMapsCocoaPods(config, { useGoogleMaps: !!apiKey });
// Adds/Removes AppDelegate setup for Google Maps API on iOS
config = withGoogleMapsAppDelegate(config, { apiKey });
return config;
};
export function getGoogleMapsApiKey(config: Pick<ExpoConfig, 'ios'>) {
return config.ios?.config?.googleMapsApiKey ?? null;
}
export function setGoogleMapsApiKey(
config: Pick<ExpoConfig, 'ios'>,
{ GMSApiKey, ...infoPlist }: InfoPlist
): InfoPlist {
const apiKey = getGoogleMapsApiKey(config);
if (apiKey === null) {
return infoPlist;
}
return {
...infoPlist,
GMSApiKey: apiKey,
};
}
export function addGoogleMapsAppDelegateImport(src: string): MergeResults {
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: string): MergeResults {
return removeContents({
tag: 'react-native-maps-import',
src,
});
}
export function addGoogleMapsAppDelegateInit(src: string, apiKey: string): MergeResults {
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: string): MergeResults {
return removeContents({
tag: 'react-native-maps-init',
src,
});
}
/**
* @param src
* @param useGoogleMaps
* @param googleMapsPath '../node_modules/react-native-maps'
* @returns
*/
export function addMapsCocoaPods(src: string, googleMapsPath: string): MergeResults {
return mergeContents({
tag: 'react-native-maps',
src,
newSrc: ` pod 'react-native-google-maps', path: '${googleMapsPath}'`,
anchor: /use_native_modules/,
offset: 0,
comment: '#',
});
}
export function removeMapsCocoaPods(src: string): MergeResults {
return removeContents({
tag: 'react-native-maps',
src,
});
}
function isReactNativeMapsInstalled(projectRoot: string): string | null {
const resolved = resolveFrom.silent(projectRoot, 'react-native-maps/package.json');
return resolved ? path.dirname(resolved) : null;
}
function isReactNativeMapsAutolinked(config: Pick<ExpoConfig, '_internal'>): boolean {
// TODO: Detect autolinking
return true;
}
const withMapsCocoaPods: ConfigPlugin<{ useGoogleMaps: boolean }> = (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: MergeResults;
// Only add the block if react-native-maps is installed in the project (best effort).
// Generally prebuild runs after a yarn install so this should always work as expected.
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) {
// Make the pod path relative to the ios folder.
const googleMapsPodPath = path.relative(
config.modRequest.platformProjectRoot,
googleMapsPath
);
try {
results = addMapsCocoaPods(contents, googleMapsPodPath);
} catch (error: any) {
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 {
// If the package is no longer installed, then remove the block.
results = removeMapsCocoaPods(contents);
}
if (results.didMerge || results.didClear) {
await fs.writeFile(filePath, results.contents);
}
return config;
},
]);
};
const withGoogleMapsAppDelegate: ConfigPlugin<{ apiKey: string | null }> = (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;
});
};