Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,13 @@ module.exports = {
'@typescript-eslint/no-require-imports': 'off',
},
},
// Expo loads config plugins as CommonJS
{
files: ['app.plugin.js', 'plugin/**/*.js'],
rules: {
'@typescript-eslint/no-require-imports': 'off',
},
},
// Test files
{
files: ['**/*.test.js', '**/*.test.ts', '**/e2e/**/*.js'],
Expand Down
17 changes: 12 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,15 +156,22 @@ jobs:
run: |
find . -name "*.kt" -not -path "*/build/*" | xargs java -jar $HOME/ktfmt/ktfmt.jar --google-style --dry-run --set-exit-if-changed

# Required check on the googlemaps organization, so the job has to report a
# status. There is no test orchestration for Android and iOS yet; replace the
# placeholder step once Detox runs on both platforms.
# Required check on the googlemaps organization. Runs the Node unit tests;
# the Detox integration tests for Android and iOS run outside CI.
test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Placeholder test step
run: echo "No tests to run yet"
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false

- name: Setup
uses: ./.github/actions/setup

- name: Run tests
run: yarn test

build-library:
runs-on: ubuntu-latest
Expand Down
24 changes: 18 additions & 6 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ The repository contains one React Native library and its Yarn workspace example:
Objective-C++, including CarPlay support.
- `react-native-navigation-sdk.podspec`: iOS package integration and native SDK
dependency configuration.
- `app.plugin.js` and `plugin/`: Expo config plugin, in CommonJS JavaScript
because Expo loads it directly. Its unit tests live in `plugin/__tests__/`.
- `lib/`: Generated CommonJS, ES module, and TypeScript declaration outputs;
ignored by Git.
- `example/`: Sample app, native app projects, and test infrastructure.
Expand All @@ -66,8 +68,8 @@ The repository contains one React Native library and its Yarn workspace example:
- `example/src/screens/integration_tests/`: Integration-test support code.
- `scripts/`: Native formatting and license-header scripts.
- `.github/workflows/` and `.github/actions/setup/`: CI checks and tool setup.
- `ANDROIDAUTO.md`, `CARPLAY.md`, and `MIGRATING.md`: Specialized setup and
migration documentation.
- `ANDROIDAUTO.md`, `CARPLAY.md`, `EXPO.md`, and `MIGRATING.md`: Specialized
setup and migration documentation.

## Development setup

Expand Down Expand Up @@ -125,6 +127,9 @@ Run commands from the repository root unless noted otherwise.
yarn lint
yarn test:types

# Run the Node unit tests.
yarn test

# Build CommonJS, ES modules, and TypeScript declarations with Bob.
yarn build

Expand Down Expand Up @@ -201,8 +206,12 @@ A cross-platform API change commonly touches several layers. Check each layer:
Objective-C/Objective-C++ modules, views, and translation utilities.
5. Regenerate and compile native bindings through both platform builds.
6. Add integration coverage and update the example app.
7. Update `README.md` and, when relevant, `MIGRATING.md`, `ANDROIDAUTO.md`, or
`CARPLAY.md`.
7. Update `README.md` and, when relevant, `MIGRATING.md`, `ANDROIDAUTO.md`,
`CARPLAY.md`, or `EXPO.md`.

A change to the native setup the package requires, such as a new Gradle
setting or `AppDelegate` call, also updates the Expo config plugin in
`plugin/`, its tests, and `EXPO.md`.

For platform-specific limitations, preserve existing documented semantics and
explain the limitation in public API documentation. Do not introduce an
Expand All @@ -229,6 +238,9 @@ Run the narrowest relevant checks while iterating, then verify every affected
layer before finishing:

- TypeScript changes: run `yarn lint`, `yarn test:types`, and `yarn build`.
- Expo config plugin changes: run `yarn lint` and `yarn test`, then run
`npx expo prebuild --clean` in an Expo app that installs the packed package
(`npm pack`) and build the generated iOS and Android projects.
- Android changes: also run native formatting checks and the Android example
build.
- iOS changes: also run native formatting checks, install pods as needed, and
Expand All @@ -238,8 +250,8 @@ layer before finishing:
- Documentation-only changes: validate referenced paths, commands, and diff
whitespace; native builds are not necessary.

CI does not run tests: `.github/workflows/ci.yml` has no test job, and there is
no root `yarn test` script. The example has a Jest script, but it is not a
CI's `test` job runs `yarn test`, the Node unit tests for the Expo config
plugin. CI does not run Detox. The example has a Jest script, but it is not a
substitute for the Detox build-and-test flow.

### Detox integration tests
Expand Down
117 changes: 117 additions & 0 deletions EXPO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Using the Navigation SDK with Expo

This guide explains how to set up the React Native Navigation SDK in an Expo app.
The package ships an [Expo config plugin](https://docs.expo.dev/config-plugins/introduction/)
that applies the native setup during `npx expo prebuild`.

## Requirements

- Expo SDK 58 or later. Earlier SDKs ship a React Native version below this
package's minimum; see [React Native Compatibility](./README.md#react-native-compatibility).
- A [development build](https://docs.expo.dev/develop/development-builds/introduction/).
Expo Go cannot load this package's native code, so start the app with
`npx expo run:android` and `npx expo run:ios`, or build it with EAS Build.

## Setup

Install the package and `expo-build-properties`:

```shell
npx expo install @googlemaps/react-native-navigation-sdk expo-build-properties
```

Add the required settings to the `expo` object in `app.json`, keeping the
fields your app already has:

```json
{
"expo": {
"ios": {
"deploymentTarget": "17.0",
"infoPlist": {
"NSLocationWhenInUseUsageDescription": "Navigation needs your location.",
"NSLocationAlwaysAndWhenInUseUsageDescription": "Navigation needs your location.",
"NSMotionUsageDescription": "Used to improve navigation features.",
"UIBackgroundModes": ["location", "audio"]
}
},
"plugins": [
"@googlemaps/react-native-navigation-sdk",
["expo-build-properties", { "android": { "kotlinVersion": "2.3.21" } }]
]
}
}
```

`npx expo install` may already have added `expo-build-properties` to `plugins`
without options; keep a single entry with the Kotlin version. The override is
required because the Navigation SDK needs Kotlin Gradle Plugin 2.3.0 or later;
see the [Migration Guide](./MIGRATING.md).

Set the API keys in `ios.config.googleMapsApiKey` and
`android.config.googleMaps.apiKey`. Expo writes them to `Info.plist` as
`GMSApiKey` and to `AndroidManifest.xml`. To read them from environment
variables, add an `app.config.js` that extends `app.json`:

```js
module.exports = ({ config }) => ({
...config,
ios: {
...config.ios,
config: { googleMapsApiKey: process.env.GOOGLE_MAPS_IOS_API_KEY },
},
android: {
...config.android,
config: { googleMaps: { apiKey: process.env.GOOGLE_MAPS_ANDROID_API_KEY } },
},
});
```

Then generate the native projects:

```shell
npx expo prebuild --clean
```

## Location permission

The `infoPlist` usage descriptions only provide the text of the iOS permission
prompt. The app requests location permission at runtime before it initializes
navigation; see
[Requesting and handling permissions](./README.md#requesting-and-handling-permissions).
In an Expo app, `expo-location` can request it:

```shell
npx expo install expo-location
```

```tsx
import * as Location from 'expo-location';

const { granted } = await Location.requestForegroundPermissionsAsync();
```

## What the plugin changes

The plugin applies the native setup that Expo's app config does not cover:

- **iOS**: adds `import GoogleMaps` to `AppDelegate.swift` and passes the
`Info.plist` `GMSApiKey` value to `GMSServices.provideAPIKey` at launch.
- **Android**: enables Jetifier in `gradle.properties`, and enables core library
desugaring with `desugar_jdk_libs_nio` in `app/build.gradle`.

`npx expo prebuild` fails with an error from the plugin when an API key is not
set, or when it cannot find where to add its code in `AppDelegate.swift`. The
plugin supports the Swift `AppDelegate` and the Groovy `app/build.gradle` that
Expo SDK 58 generates.

Other packages that depend on the Google Maps SDK, such as `react-native-maps`,
may conflict with this package; see
[Compatibility with other libraries](./README.md#compatibility-with-other-libraries).

## Projects that keep native folders

If your project commits its `ios` and `android` folders instead of generating
them with `npx expo prebuild`, the plugin does not run. Follow the
[Android](./README.md#android) and [iOS](./README.md#ios) setup in the README
instead.
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ In your TSX or JSX file, import the components you need:
import { NavigationView } from '@googlemaps/react-native-navigation-sdk';
```

### Expo

Expo apps that generate their native projects with `npx expo prebuild` use the
package's config plugin instead of the Android and iOS steps below. See the
[Expo documentation](./EXPO.md).

### Android

This package uses Navigation SDK for Android **8.0.0**. See the
Expand Down Expand Up @@ -266,7 +272,7 @@ const initializeNavigation = useCallback(async () => {

// Initialize the navigation session and check the status
const status = await navigationController.init();

switch (status) {
case NavigationSessionStatus.OK:
console.log('Navigation initialized successfully');
Expand Down Expand Up @@ -360,8 +366,8 @@ await navigationController.startGuidance();
#### Adding navigation listeners

```tsx
const {
navigationController,
const {
navigationController,
removeAllListeners,
setOnArrival,
setOnRouteChanged,
Expand Down Expand Up @@ -616,8 +622,8 @@ The `useNavigation()` hook provides access to the `NavigationController` and lis
```tsx
import { useNavigation } from '@googlemaps/react-native-navigation-sdk';

const {
navigationController,
const {
navigationController,
removeAllListeners,
setOnArrival,
setOnRouteChanged,
Expand Down Expand Up @@ -703,7 +709,7 @@ useEffect(() => {
}
});
setOnRouteChanged(() => console.log('Route changed'));

// Use removeAllListeners() to clear all listeners at once on cleanup
// Alternatively, clear individual listeners: setOnArrival(null)
return () => removeAllListeners();
Expand Down Expand Up @@ -761,8 +767,8 @@ For Android Auto and CarPlay support, the `useNavigationAuto()` hook provides a
```tsx
import { useNavigationAuto } from '@googlemaps/react-native-navigation-sdk';

const {
mapViewAutoController,
const {
mapViewAutoController,
removeAllListeners,
setOnAutoScreenAvailabilityChanged,
setOnCustomNavigationAutoEvent,
Expand Down
17 changes: 17 additions & 0 deletions app.plugin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

module.exports = require('./plugin');
9 changes: 9 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
"android",
"ios",
"cpp",
"app.plugin.js",
"plugin",
"*.podspec",
"!ios/build",
"!android/build",
Expand Down Expand Up @@ -50,6 +52,7 @@
},
"scripts": {
"example": "yarn workspace react-native-navigation-sdk-sample",
"test": "node --test \"plugin/**/*.test.js\"",
"test:types": "tsc --noEmit",
"lint": "eslint \"**/*.{js,ts,tsx}\"",
"lint-fix": "eslint \"**/*.{js,ts,tsx}\" --fix",
Expand Down Expand Up @@ -87,9 +90,15 @@
"typescript": "~6.0.3"
},
"peerDependencies": {
"expo": "*",
"react": "*",
"react-native": ">=0.87.0"
},
"peerDependenciesMeta": {
"expo": {
"optional": true
}
},
"workspaces": [
"example"
],
Expand Down
Loading
Loading