Skip to content

Google provider setup errors reach JS as Swift enum case names on iOS #135

Description

@jkasprzyk17

Problem

MapProviderConfigurationError (package/ios/GoogleMapsAPIKey.swift:28-43) is a
LocalizedError whose errorDescription strings tell the consumer exactly what to
configure:

case .missingGoogleMapsIosApiKey:
  return "react-native-better-maps: provider=\"google\" on iOS requires GoogleMapsIosApiKey in the host app Info.plist."

That text reaches the placeholder view correctly, because UnavailableMapProviderAdapter.init
reads error.localizedDescription (package/ios/MapProviderAdapter.swift:101).

It never reaches JS. The same error object goes back through Promise.rejected(withError:)
and throw (package/ios/MapProviderAdapter.swift:117-139), and Nitro converts every Swift
Error with String(describing:) (react-native-nitro-modules/ios/core/RuntimeError.swift:41-44):

extension Error {
  public func toCpp() -> std.exception_ptr {
    let message = String(describing: self)          // <- not localizedDescription
    return margelo.nitro.makeException(std.string(message))
  }
}

String(describing:) consults CustomStringConvertible first and otherwise falls back to
reflection. LocalizedError provides neither, so the enum reflects to its case name.
Verified by running the enum standalone under swift:

String(describing:)   -> missingGoogleMapsIosApiKey
localizedDescription  -> react-native-better-maps: provider="google" on iOS requires GoogleMapsIosApiKey in the host app Info.plist.
String(describing:)   -> unsupportedIOSProvider("mapbox")

So a consumer whose Info.plist key is missing gets Error: missingGoogleMapsIosApiKey
instead of the sentence that says what to add and where — while the placeholder view on
screen shows the correct sentence at the same moment.

This is specific to MapProviderConfigurationError. The rest of the Swift layer throws
RuntimeError (package/ios/HybridMapView.swift:359), which does conform to
CustomStringConvertible, so "MapView is not mounted" survives intact. Android has no
equivalent problem — it throws IllegalStateException carrying the full sentence
(package/android/src/main/java/com/margelo/nitro/nitromaps/HybridMapView.kt:383).

Affected surfaces

Every MapViewRef method, whenever the iOS provider could not be created:

Path Message JS sees today
GoogleMapsIosApiKey missing from Info.plist missingGoogleMapsIosApiKey
Google Maps SDK not linked (#if canImport(GoogleMaps) false) googleMapsSdkNotLinked
provider="openstreetmap" / "mapbox" on iOS unsupportedIOSProvider(...)

Reached through getCamera, setCamera, animateCamera, getVisibleRegion and
fitToCoordinates — all five route through UnavailableMapProviderAdapter
(package/ios/HybridMapView.swift:375-396).

Reproduction

  1. iOS app with the Google Maps SDK linked but no GoogleMapsIosApiKey in Info.plist.
  2. Render <MapView provider="google" ref={mapRef} />.
  3. mapRef.current.getCamera().catch((e) => console.log(e.message)).
  • Now: missingGoogleMapsIosApiKey
  • Expected: react-native-better-maps: provider="google" on iOS requires GoogleMapsIosApiKey in the host app Info.plist.

The on-screen placeholder renders the correct sentence at the same time, which makes the
mismatch easy to confirm side by side.

Fix direction

One conformance, no call-site changes:

enum MapProviderConfigurationError: LocalizedError, CustomStringConvertible {
  // ...
  var description: String { errorDescription ?? "Map provider configuration failed." }
}

String(describing:) then picks up description, so the placeholder label and the JS error
carry the same text.

The alternative — throwing RuntimeError(...) with the message baked in — also works, but
loses the typed cases and the errorDescription the placeholder view relies on.

Acceptance criteria

  • A rejected MapViewRef call on an unavailable iOS provider carries the same sentence
    the placeholder view renders
  • All three cases covered, including the interpolated unsupportedIOSProvider
  • The placeholder label text is unchanged

Regression tests

XCTest in package/iosTests/ (already wired through the podspec test spec,
package/react-native-better-maps.podspec:70): assert
String(describing: error) == error.localizedDescription for every case. That is exactly
the property Nitro relies on, and it needs no map instance.

Notes

Whether Nitro should prefer localizedDescription for LocalizedError is worth raising
upstream, but that is a margelo/nitro change and this repo should not wait on it.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinggood first issueGood for newcomershelp wantedExtra attention is neededplatform: iosAffects iOSprovider: googleAffects the Google Maps providerswiftThe Swift / iOS native layer (package/ios)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions