Skip to content

Add native POI press events for Apple Maps and Google Maps #33

Description

@piotr-graczyk-dev

Problem

MapView currently exposes background map taps through onPress(coordinate) and app-owned overlay taps through marker/polyline/polygon/circle callbacks. Provider-owned base-map POIs are not exposed, so apps cannot react when the user taps a restaurant, hotel, park, school, government building, or other SDK-provided point of interest.

This issue is about provider-owned POIs rendered by the native map provider. It is not about app-owned <Marker /> or bulk markers; those continue to use Marker.onPress and MapView.onMarkerPress.

This is feasible on the currently supported native providers:

  • Apple MapKit: MKMapView.selectableMapFeatures can enable selectable points of interest on iOS 16+, and selected POIs are represented as MKMapFeatureAnnotation.
  • Google Maps SDK for iOS: GMSMapViewDelegate.mapView(_:didTapPOIWithPlaceID:name:location:) reports POI taps with place ID, name, and coordinate.
  • Google Maps SDK for Android: GoogleMap.setOnPoiClickListener(...) reports PointOfInterest with name, place ID, and coordinate.

Decisions

  • Public API name: onPoiPress.
  • v1 scope: type-safe press events only; no native detail UI, callout UI, sheet UI, or React Native POI UI components.
  • onPoiPress is enabled automatically when the callback is provided. Do not add poiPressEnabled in v1.
  • POI taps emit only onPoiPress; they must not also emit background-map onPress.
  • Apple Maps should enable only selectable points of interest, not all selectable map features.
  • Apple Maps should deselect the selected POI after emitting the event so native selection state does not linger.
  • Apple Maps should not block the event on MKMapItemRequest in v1.

Proposed public API

Use a provider-discriminated event payload instead of one flat payload with mostly-optional fields.

export type ApplePoiCategory =
  | 'animalService'
  | 'airport'
  | 'amusementPark'
  | 'aquarium'
  | 'atm'
  | 'automotiveRepair'
  | 'bakery'
  | 'bank'
  | 'baseball'
  | 'basketball'
  | 'beach'
  | 'beauty'
  | 'bowling'
  | 'brewery'
  | 'cafe'
  | 'campground'
  | 'carRental'
  | 'castle'
  | 'conventionCenter'
  | 'distillery'
  | 'evCharger'
  | 'fairground'
  | 'fireStation'
  | 'fishing'
  | 'fitnessCenter'
  | 'foodMarket'
  | 'fortress'
  | 'gasStation'
  | 'golf'
  | 'goKart'
  | 'hiking'
  | 'hospital'
  | 'hotel'
  | 'kayaking'
  | 'landmark'
  | 'laundry'
  | 'library'
  | 'mailbox'
  | 'marina'
  | 'miniGolf'
  | 'movieTheater'
  | 'museum'
  | 'musicVenue'
  | 'nationalMonument'
  | 'nationalPark'
  | 'nightlife'
  | 'park'
  | 'parking'
  | 'pharmacy'
  | 'planetarium'
  | 'police'
  | 'postOffice'
  | 'publicTransport'
  | 'restaurant'
  | 'restroom'
  | 'rockClimbing'
  | 'rvPark'
  | 'school'
  | 'skatePark'
  | 'skating'
  | 'skiing'
  | 'soccer'
  | 'spa'
  | 'stadium'
  | 'store'
  | 'surfing'
  | 'swimming'
  | 'tennis'
  | 'theater'
  | 'university'
  | 'volleyball'
  | 'winery'
  | 'zoo'
  | 'unknown';

export type ApplePoiPressEvent = {
  provider: 'apple';
  coordinate: Coordinate;
  name?: string;
  category: ApplePoiCategory;
  rawCategory?: string;
};

export type GooglePoiPressEvent = {
  provider: 'google';
  coordinate: Coordinate;
  name: string;
  placeId: string;
};

export type PoiPressEvent = ApplePoiPressEvent | GooglePoiPressEvent;

interface BaseMapViewProps {
  onPoiPress?: (event: PoiPressEvent) => void;
}

ApplePoiCategory should live in the Nitro/native spec so Nitrogen generates native enums and the JS/native contract stays type-safe. If MapKit returns a missing or unmapped category, emit category: 'unknown'; include rawCategory when an unmapped native category string exists.

Provider-aware TypeScript props should narrow the event type where possible:

  • provider="apple": onPoiPress receives ApplePoiPressEvent.
  • provider="google": onPoiPress receives GooglePoiPressEvent.
  • omitted provider: onPoiPress receives PoiPressEvent, because the runtime default provider depends on platform.

Implementation notes

  • Update package/src/types/map.ts and package/src/native/specs/MapView.nitro.ts.
  • Add the provider-specific POI event types and ApplePoiCategory to the native spec surface, then run Nitrogen/codegen.
  • iOS Apple provider:
    • When onPoiPress is installed, set MKMapView.selectableMapFeatures to include only .pointsOfInterest.
    • Extend the existing MKMapViewDelegate selection path to detect MKMapFeatureAnnotation.
    • Emit ApplePoiPressEvent immediately from the annotation data.
    • Map known MKPointOfInterestCategory values to ApplePoiCategory.
    • Emit category: 'unknown' and rawCategory when the native category is missing or not mapped.
    • Deselect the annotation after emitting the event.
    • Keep existing marker, cluster, overlay, and background-press behavior unchanged.
  • iOS Google provider:
    • Implement mapView(_:didTapPOIWithPlaceID:name:location:) in GoogleMapProviderAdapter.
    • Emit GooglePoiPressEvent.
    • Do not also emit background onPress for the same tap.
  • Android Google provider:
    • Register GoogleMap.setOnPoiClickListener.
    • Emit GooglePoiPressEvent from PointOfInterest.name, PointOfInterest.placeId, and PointOfInterest.latLng.
    • Do not also emit background onPress for the same tap.

Acceptance criteria

  • MapView has a typed onPoiPress?: (event: PoiPressEvent) => void prop in the public TypeScript API.
  • Provider-specific props narrow onPoiPress to the matching provider payload.
  • ApplePoiCategory is represented in the Nitro/native spec, with a full list of known MapKit POI categories plus unknown.
  • Nitro generated native bindings include the new event payload and category enum.
  • Apple Maps on iOS emits ApplePoiPressEvent when tapping a native POI.
  • Google Maps on iOS emits GooglePoiPressEvent using the SDK POI delegate callback.
  • Google Maps on Android emits GooglePoiPressEvent using OnPoiClickListener.
  • POI taps do not trigger background onPress.
  • Apple Maps POI selection is cleared after the event is emitted.
  • The example app includes a small scenario/log panel for native POI taps on Apple Maps and Google Maps.
  • Docs mention provider payload differences and explicitly distinguish provider-owned POIs from app-owned markers.
  • Focused validation covers TypeScript type tests plus iOS Apple, iOS Google, and Android Google manual smoke tests.

Non-goals

  • Native POI detail UI, native callouts, native sheets, or native "Open in Maps" affordances.
  • React Native POI detail UI components.
  • Waiting for MKMapItemRequest before emitting the Apple event.
  • Changing app-owned marker press behavior.

References

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions