[iOS][New Arch] Marker onPress/onSelect/onDeselect `position` is always {0,0}: Fabric wrapper reads x/y from the coordinate dict, and AIRMapMarker measures against a nil calloutView
Summary
On iOS with the New Architecture (Fabric), the position in a marker's onPress, onSelect and onDeselect payload is always { x: 0, y: 0 }. Two separate bugs produce this, one in the Fabric wrapper and one in AIRMapMarker (Apple Maps provider):
ios/AirMaps/RNMapsMarkerView.mmbuilds thepositionstruct from the coordinate dictionary:coordinateDict[@"x"]/coordinateDict[@"y"]. A coordinate dictionary only haslatitude/longitude, sox/yare nil and read as 0. This happens in all three handlers (onPress,onDeselect,onSelect; lines 115, 144 and 231 on master). ThepositionDictis fetched right above and never used.ios/AirMaps/AIRMapMarker.m_handleTap:computes the marker-press point as[recognizer locationInView:self.calloutView]. When no callout is openself.calloutViewis nil, andlocationInView:nilreturns the point in the window's coordinate space rather than the map's. In practice on our app it reported{0, 0}; even where it does not, it is not in map coordinates, which is what the map-levelonPresspayload uses.
Together they mean a marker tap on iOS carries no usable finger position, so an app cannot do its own hit-testing on a marker press (we needed this because MapKit resolves a tap against the annotation view's whole rectangular frame, so in a dense pack of custom pins the neighbour gets selected).
Reproducible sample code
<MapView style={{ flex: 1 }} initialRegion={{ latitude: -41.29, longitude: 174.78, latitudeDelta: 0.02, longitudeDelta: 0.0092 }}>
<Marker
coordinate={{ latitude: -41.29, longitude: 174.78 }}
onPress={(e) => console.log('marker', e.nativeEvent.position, e.nativeEvent.coordinate)}
/>
</MapView>Steps to reproduce
- New Architecture enabled, Apple Maps provider (
PROVIDER_DEFAULT). - Tap the marker.
Expected result
position is the tap point in the map view's coordinate space, e.g. { x: 201, y: 387 }, matching what MapView.onPress reports for a map tap.
Actual result
position is { x: 0, y: 0 } on every marker press.
Fix
Both hunks are from a patch we run in production (patch-package against 1.29.0; the code is unchanged on master as of v1.29.2).
--- a/ios/AirMaps/RNMapsMarkerView.mm
+++ b/ios/AirMaps/RNMapsMarkerView.mm
@@ -110,10 +110,10 @@
NSDictionary* coordinateDict = dictionary[@"coordinate"];
NSDictionary* positionDict = dictionary[@"position"];
facebook::react::RNMapsMarkerEventEmitter::OnPressPosition point = {
- .x = [coordinateDict[@"x"] doubleValue],
- .y = [coordinateDict[@"y"] doubleValue],
+ .x = [positionDict[@"x"] doubleValue],
+ .y = [positionDict[@"y"] doubleValue],
};The same two-line change applies to the onDeselect (line 144) and onSelect (line 231) handlers.
--- a/ios/AirMaps/AIRMapMarker.m
+++ b/ios/AirMaps/AIRMapMarker.m
@@ -289,7 +289,7 @@
// the actual marker got clicked
- CGPoint touchPointReal = [recognizer locationInView:self.calloutView];
+ CGPoint touchPointReal = [recognizer locationInView:marker.map];
id event = @{
@"action": @"marker-press",A related change we also carry, which you may or may not want: in AIRMapMarker -setupTapGestureRecognizer (or wherever the tap recogniser is attached), tapGestureRecognizer.cancelsTouchesInView = NO lets MKMapView independently hit-test the same touch and select a different overlapping annotation. Setting it to YES stops that; the handler already calls selectAnnotation: itself.
React Native Maps Version
1.29.0 (bug present on master, v1.29.2)
What platforms are you seeing the problem on?
iOS
React Native Version
0.86 (Expo SDK 57), New Architecture
What version of Expo are you using?
SDK 57
Device(s)
iPhone 17 Pro simulator (iOS 26.5), iPhone hardware
Additional information
Verified with an XCTest bundle hosted by the real app build that drives AIRMapMarker / RNMapsMarkerView through the runtime and reads the JS-side press payload back; reverting each hunk in node_modules fails exactly its test. A full write-up will be at https://zackdesign.biz/which-pin-did-you-tap/ shortly.
Source: react-native-maps/react-native-maps