Skip to content

Commit f78c0ee

Browse files
authored
Support Google Maps on iOS (#548)
1 parent f4219a2 commit f78c0ee

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2098
-446
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,5 @@ local.properties
3333
#
3434
node_modules/
3535
npm-debug.log
36+
Pods/
37+
AirMapsExplorer.xcworkspace/

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ React Native Map components for iOS + Android
66

77
See [Installation Instructions](docs/installation.md).
88

9+
See [Setup Instructions for the Included Example Project](docs/examples-setup.md).
10+
911
## Compatibility
1012

1113
Due to the rapid changes being made in the React Native ecosystem, we are not officially going to

components/MapCallout.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
import React, { PropTypes } from 'react';
22
import {
33
View,
4-
requireNativeComponent,
54
StyleSheet,
65
} from 'react-native';
6+
import decorateMapComponent, {
7+
SUPPORTED,
8+
USES_DEFAULT_IMPLEMENTATION,
9+
} from './decorateMapComponent';
710

811
const propTypes = {
912
...View.propTypes,
@@ -17,6 +20,7 @@ const defaultProps = {
1720

1821
class MapCallout extends React.Component {
1922
render() {
23+
const AIRMapCallout = this.getAirComponent();
2024
return <AIRMapCallout {...this.props} style={[styles.callout, this.props.style]} />;
2125
}
2226
}
@@ -30,6 +34,12 @@ const styles = StyleSheet.create({
3034
},
3135
});
3236

33-
const AIRMapCallout = requireNativeComponent('AIRMapCallout', MapCallout);
34-
35-
module.exports = MapCallout;
37+
module.exports = decorateMapComponent(MapCallout, {
38+
componentType: 'Callout',
39+
providers: {
40+
google: {
41+
ios: SUPPORTED,
42+
android: USES_DEFAULT_IMPLEMENTATION,
43+
},
44+
},
45+
});

components/MapCircle.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React, { PropTypes } from 'react';
22
import {
33
View,
4-
requireNativeComponent,
54
} from 'react-native';
5+
import decorateMapComponent, {
6+
USES_DEFAULT_IMPLEMENTATION,
7+
NOT_SUPPORTED,
8+
} from './decorateMapComponent';
69

710
const propTypes = {
811
...View.propTypes,
@@ -123,6 +126,7 @@ const defaultProps = {
123126

124127
class MapCircle extends React.Component {
125128
render() {
129+
const AIRMapCircle = this.getAirComponent();
126130
return (
127131
<AIRMapCircle {...this.props} />
128132
);
@@ -132,6 +136,12 @@ class MapCircle extends React.Component {
132136
MapCircle.propTypes = propTypes;
133137
MapCircle.defaultProps = defaultProps;
134138

135-
const AIRMapCircle = requireNativeComponent('AIRMapCircle', MapCircle);
136-
137-
module.exports = MapCircle;
139+
module.exports = decorateMapComponent(MapCircle, {
140+
componentType: 'Circle',
141+
providers: {
142+
google: {
143+
ios: NOT_SUPPORTED,
144+
android: USES_DEFAULT_IMPLEMENTATION,
145+
},
146+
},
147+
});

components/MapMarker.js

+18-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import React, { PropTypes } from 'react';
22
import {
33
View,
4-
requireNativeComponent,
54
StyleSheet,
65
Platform,
76
NativeModules,
@@ -10,9 +9,13 @@ import {
109
} from 'react-native';
1110

1211
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource';
12+
import decorateMapComponent, {
13+
SUPPORTED,
14+
USES_DEFAULT_IMPLEMENTATION,
15+
} from './decorateMapComponent';
1316

1417
const viewConfig = {
15-
uiViewClassName: 'AIRMapMarker',
18+
uiViewClassName: 'AIR<provider>MapMarker',
1619
validAttributes: {
1720
coordinate: true,
1821
},
@@ -226,16 +229,13 @@ class MapMarker extends React.Component {
226229
case 'android':
227230
NativeModules.UIManager.dispatchViewManagerCommand(
228231
this._getHandle(),
229-
NativeModules.UIManager.AIRMapMarker.Commands[name],
232+
this.getUIManagerCommand(name),
230233
args
231234
);
232235
break;
233236

234237
case 'ios':
235-
NativeModules.AIRMapMarkerManager[name].apply(
236-
NativeModules.AIRMapMarkerManager[name],
237-
[this._getHandle(), ...args]
238-
);
238+
this.getMapManagerCommand(name)(this._getHandle(), ...args);
239239
break;
240240

241241
default:
@@ -250,6 +250,8 @@ class MapMarker extends React.Component {
250250
image = image.uri;
251251
}
252252

253+
const AIRMapMarker = this.getAirComponent();
254+
253255
return (
254256
<AIRMapMarker
255257
ref={ref => { this.marker = ref; }}
@@ -272,8 +274,14 @@ const styles = StyleSheet.create({
272274
},
273275
});
274276

275-
const AIRMapMarker = requireNativeComponent('AIRMapMarker', MapMarker);
276-
277277
MapMarker.Animated = Animated.createAnimatedComponent(MapMarker);
278278

279-
module.exports = MapMarker;
279+
module.exports = decorateMapComponent(MapMarker, {
280+
componentType: 'Marker',
281+
providers: {
282+
google: {
283+
ios: SUPPORTED,
284+
android: USES_DEFAULT_IMPLEMENTATION,
285+
},
286+
},
287+
});

components/MapPolygon.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React, { PropTypes } from 'react';
22
import {
33
View,
4-
requireNativeComponent,
54
} from 'react-native';
5+
import decorateMapComponent, {
6+
USES_DEFAULT_IMPLEMENTATION,
7+
NOT_SUPPORTED,
8+
} from './decorateMapComponent';
69

710
const propTypes = {
811
...View.propTypes,
@@ -128,6 +131,7 @@ const defaultProps = {
128131

129132
class MapPolygon extends React.Component {
130133
render() {
134+
const AIRMapPolygon = this.getAirComponent();
131135
return (
132136
<AIRMapPolygon {...this.props} />
133137
);
@@ -137,6 +141,12 @@ class MapPolygon extends React.Component {
137141
MapPolygon.propTypes = propTypes;
138142
MapPolygon.defaultProps = defaultProps;
139143

140-
const AIRMapPolygon = requireNativeComponent('AIRMapPolygon', MapPolygon);
141-
142-
module.exports = MapPolygon;
144+
module.exports = decorateMapComponent(MapPolygon, {
145+
componentType: 'Polygon',
146+
providers: {
147+
google: {
148+
ios: NOT_SUPPORTED,
149+
android: USES_DEFAULT_IMPLEMENTATION,
150+
},
151+
},
152+
});

components/MapPolyline.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
import React, { PropTypes } from 'react';
22
import {
33
View,
4-
requireNativeComponent,
54
} from 'react-native';
5+
import decorateMapComponent, {
6+
USES_DEFAULT_IMPLEMENTATION,
7+
NOT_SUPPORTED,
8+
} from './decorateMapComponent';
69

710
const propTypes = {
811
...View.propTypes,
@@ -123,6 +126,7 @@ const defaultProps = {
123126

124127
class MapPolyline extends React.Component {
125128
render() {
129+
const AIRMapPolyline = this.getAirComponent();
126130
return (
127131
<AIRMapPolyline {...this.props} />
128132
);
@@ -132,6 +136,12 @@ class MapPolyline extends React.Component {
132136
MapPolyline.propTypes = propTypes;
133137
MapPolyline.defaultProps = defaultProps;
134138

135-
const AIRMapPolyline = requireNativeComponent('AIRMapPolyline', MapPolyline);
136-
137-
module.exports = MapPolyline;
139+
module.exports = decorateMapComponent(MapPolyline, {
140+
componentType: 'Polyline',
141+
providers: {
142+
google: {
143+
ios: NOT_SUPPORTED,
144+
android: USES_DEFAULT_IMPLEMENTATION,
145+
},
146+
},
147+
});

components/MapView.js

+55-14
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@ import MapPolygon from './MapPolygon';
1515
import MapCircle from './MapCircle';
1616
import MapCallout from './MapCallout';
1717
import MapUrlTile from './MapUrlTile';
18+
import {
19+
contextTypes as childContextTypes,
20+
getAirMapName,
21+
googleMapIsInstalled,
22+
createNotSupportedComponent,
23+
} from './decorateMapComponent';
24+
import * as ProviderConstants from './ProviderConstants';
1825

1926
const MAP_TYPES = {
2027
STANDARD: 'standard',
@@ -30,14 +37,23 @@ const ANDROID_ONLY_MAP_TYPES = [
3037
];
3138

3239
const viewConfig = {
33-
uiViewClassName: 'AIRMap',
40+
uiViewClassName: 'AIR<provider>Map',
3441
validAttributes: {
3542
region: true,
3643
},
3744
};
3845

3946
const propTypes = {
4047
...View.propTypes,
48+
/**
49+
* When provider is "google", we will use GoogleMaps.
50+
* Any value other than "google" will default to using
51+
* MapKit in iOS or GoogleMaps in android as the map provider.
52+
*/
53+
provider: PropTypes.oneOf([
54+
'google',
55+
]),
56+
4157
/**
4258
* Used to style and layout the `MapView`. See `StyleSheet.js` and
4359
* `ViewStylePropTypes.js` for more info.
@@ -341,6 +357,10 @@ class MapView extends React.Component {
341357
this._onLayout = this._onLayout.bind(this);
342358
}
343359

360+
getChildContext() {
361+
return { provider: this.props.provider };
362+
}
363+
344364
componentDidMount() {
345365
const { region, initialRegion } = this.props;
346366
if (region && this.state.isReady) {
@@ -421,6 +441,14 @@ class MapView extends React.Component {
421441
this._runCommand('takeSnapshot', [width, height, finalRegion, callback]);
422442
}
423443

444+
_uiManagerCommand(name) {
445+
return NativeModules.UIManager[getAirMapName(this.props.provider)].Commands[name];
446+
}
447+
448+
_mapManagerCommand(name) {
449+
return NativeModules[`${getAirMapName(this.props.provider)}Manager`][name];
450+
}
451+
424452
_getHandle() {
425453
return findNodeHandle(this.map);
426454
}
@@ -430,16 +458,13 @@ class MapView extends React.Component {
430458
case 'android':
431459
NativeModules.UIManager.dispatchViewManagerCommand(
432460
this._getHandle(),
433-
NativeModules.UIManager.AIRMap.Commands[name],
461+
this._uiManagerCommand(name),
434462
args
435463
);
436464
break;
437465

438466
case 'ios':
439-
NativeModules.AIRMapManager[name].apply(
440-
NativeModules.AIRMapManager[name],
441-
[this._getHandle(), ...args]
442-
);
467+
this._mapManagerCommand(name)(this._getHandle(), ...args);
443468
break;
444469

445470
default:
@@ -483,6 +508,8 @@ class MapView extends React.Component {
483508
);
484509
}
485510

511+
const AIRMap = getAirMapComponent(this.props.provider);
512+
486513
return (
487514
<AIRMap
488515
ref={ref => { this.map = ref; }}
@@ -494,31 +521,45 @@ class MapView extends React.Component {
494521

495522
MapView.propTypes = propTypes;
496523
MapView.viewConfig = viewConfig;
524+
MapView.childContextTypes = childContextTypes;
497525

498526
MapView.MAP_TYPES = MAP_TYPES;
499527

500-
const AIRMap = requireNativeComponent('AIRMap', MapView, {
528+
const nativeComponent = Component => requireNativeComponent(Component, MapView, {
501529
nativeOnly: {
502530
onChange: true,
503531
onMapReady: true,
504532
handlePanDrag: true,
505533
},
506534
});
535+
const airMaps = {
536+
default: nativeComponent('AIRMap'),
537+
};
538+
if (Platform.OS === 'android') {
539+
airMaps.google = airMaps.default;
540+
} else {
541+
airMaps.google = googleMapIsInstalled ? nativeComponent('AIRGoogleMap') :
542+
createNotSupportedComponent('react-native-maps: AirGoogleMaps dir must be added to your xCode project to support GoogleMaps on iOS.'); // eslint-disable-line max-len
543+
}
544+
const getAirMapComponent = provider => airMaps[provider || 'default'];
507545

508-
const AIRMapLite = requireNativeComponent('AIRMapLite', MapView, {
509-
nativeOnly: {
510-
onChange: true,
511-
onMapReady: true,
512-
handlePanDrag: true,
513-
},
514-
});
546+
const AIRMapLite = NativeModules.UIManager.AIRMapLite &&
547+
requireNativeComponent('AIRMapLite', MapView, {
548+
nativeOnly: {
549+
onChange: true,
550+
onMapReady: true,
551+
handlePanDrag: true,
552+
},
553+
});
515554

516555
MapView.Marker = MapMarker;
517556
MapView.Polyline = MapPolyline;
518557
MapView.Polygon = MapPolygon;
519558
MapView.Circle = MapCircle;
520559
MapView.UrlTile = MapUrlTile;
521560
MapView.Callout = MapCallout;
561+
Object.assign(MapView, ProviderConstants);
562+
MapView.ProviderPropType = PropTypes.oneOf(Object.values(ProviderConstants));
522563

523564
MapView.Animated = Animated.createAnimatedComponent(MapView);
524565

components/ProviderConstants.js

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export const PROVIDER_DEFAULT = null;
2+
export const PROVIDER_GOOGLE = 'google';

0 commit comments

Comments
 (0)