Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
b290740
ci: pin Xcode 26.2
pinpong Feb 1, 2026
2606efa
ci: pin Xcode 26.2
pinpong Feb 1, 2026
7810c49
Fix memory leak by nullifying lifecycleObserver
ottor-o Jan 31, 2026
5e23a39
fix(android): nullify lifecycleObserver to prevent memory leak
pinpong Feb 1, 2026
d36ca7f
ci: run commitlint
pinpong Feb 9, 2026
1d3a74c
feat: improve error reporting
pinpong Feb 11, 2026
680a671
refactor: remove redundant null param
pinpong Feb 11, 2026
00c7f10
feat: improve error reporting
pinpong Feb 11, 2026
2436bc3
chore(ios): update maps sdk
pinpong Feb 11, 2026
666a81a
chore: update to latest Nitro Modules version
pinpong Feb 12, 2026
4cc84ae
chore: update dependencies
pinpong Feb 12, 2026
0535c6f
chore: update nitro modules
pinpong Feb 12, 2026
41884eb
fix(android): error log
pinpong Feb 16, 2026
881dbfc
fix(android): invoke createFallbackDescriptor instead of passing func…
pinpong Feb 22, 2026
f8801d4
fix(ios): file extension
pinpong Feb 26, 2026
8e55606
fix(android): file extension
pinpong Feb 26, 2026
a5b2bd5
chore: updated deps
pinpong Feb 26, 2026
0f88a52
chore: upgrade dependencies and React Native to 0.84
pinpong Mar 1, 2026
839f414
chore(ios): update maps sdk
pinpong Mar 1, 2026
2aee14f
chore(ci): update next release workflow
pinpong Mar 2, 2026
8291f05
chore(example): update Podfile.lock
pinpong Mar 2, 2026
b6a0f67
chore: upgrade dependencies and React Native to 0.84 (#104)
pinpong Mar 2, 2026
8478bc4
fix: update to nitro v0.35.0
pinpong Mar 5, 2026
b9f046f
fix: update to nitro v0.35.1
pinpong Mar 15, 2026
4d2f39a
chore: bump node from 22.11.0 to 22.16.0
pinpong Mar 16, 2026
dee296f
chore: bump minimum React Native version to 0.82
pinpong Mar 16, 2026
9037f2e
chore: minor code quality improvements
pinpong Mar 18, 2026
bdc263b
chore: minor code quality improvements
pinpong Mar 18, 2026
e0106d0
chore: update dependencies and pin all versions
pinpong Apr 2, 2026
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
12 changes: 7 additions & 5 deletions .github/workflows/prepare_dev_for_next_release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ name: Prepare dev branch for next release

on:
push:
branches: [main]
tags:
- 'v*'

permissions:
contents: write
Expand All @@ -11,7 +12,7 @@ jobs:
update-pods:
name: Update Podfile.lock after release
runs-on: macos-latest
if: contains(join(github.event.commits.*.message, ' '), 'release')
if: ${{ !contains(github.ref_name, '-') }}
env:
XCODE_VERSION: latest-stable

Expand All @@ -21,6 +22,7 @@ jobs:
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
ref: main

- name: Setup
uses: ./.github/actions/setup
Expand All @@ -41,14 +43,14 @@ jobs:
exit 0
fi

git commit -m "chore(example): update Podfile.lock after release [skip ci]"
git push
git commit -m "chore(example): update Podfile.lock after release"
git push origin main

sync-dev:
name: Merge main into dev
runs-on: ubuntu-latest
needs: update-pods
if: contains(join(github.event.commits.*.message, ' '), 'release')
if: ${{ !contains(github.ref_name, '-') }}
steps:
- name: Checkout
uses: actions/checkout@v5.0.0
Expand Down
21 changes: 18 additions & 3 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,23 @@ concurrency:
cancel-in-progress: true

jobs:
commitlint:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5.0.0
with:
fetch-depth: 0

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

- name: Lint commit messages
run: |
yarn commitlint --from ${{ github.event.pull_request.base.sha }} --to ${{ github.event.pull_request.head.sha }} --verbose
lint:
runs-on: macos-latest
runs-on: macos-26
needs: [commitlint]
steps:
- name: Checkout
uses: actions/checkout@v5.0.0
Expand Down Expand Up @@ -110,10 +125,10 @@ jobs:
JAVA_OPTS: '-XX:MaxHeapSize=6g'
run: yarn build:android
build-ios:
runs-on: macos-latest
runs-on: macos-26
needs: [lint, test, docs]
env:
XCODE_VERSION: '26.0.1'
XCODE_VERSION: '26.2'
steps:
- name: Checkout
uses: actions/checkout@v5.0.0
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/stale.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
- uses: actions/stale@v10.2.0
with:
exempt-issue-labels: 'pinned,important'
days-before-issue-stale: 7
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
22
22.16.0
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![Dev Release](https://img.shields.io/npm/v/react-native-google-maps-plus/dev.svg?label=dev%20release&color=orange)](https://www.npmjs.com/package/react-native-google-maps-plus)
[![Build](https://github.com/pinpong/react-native-google-maps-plus/actions/workflows/release.yml/badge.svg)](https://github.com/pinpong/react-native-google-maps-plus/actions/workflows/release.yml)
[![API Docs](https://img.shields.io/static/v1?label=typedoc&message=docs&color=informational)](https://pinpong.github.io/react-native-google-maps-plus)
![React Native](https://img.shields.io/badge/react--native-%3E%3D0.81.0-61dafb.svg?logo=react)
![React Native](https://img.shields.io/badge/react--native-%3E%3D0.82.0-61dafb.svg?logo=react)

React Native wrapper for Android & iOS Google Maps SDK.

Expand Down
2 changes: 1 addition & 1 deletion RNGoogleMapsPlus.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Pod::Spec.new do |s|
s.dependency 'React-jsi'
s.dependency 'React-callinvoker'

s.dependency 'GoogleMaps', '10.7.0'
s.dependency 'GoogleMaps', '10.10.0'
s.dependency 'Google-Maps-iOS-Utils', '7.0.0'
s.dependency 'SVGKit', '3.0.0'

Expand Down
5 changes: 4 additions & 1 deletion android/src/main/cpp/cpp-adapter.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include <fbjni/fbjni.h>
#include <jni.h>
#include "RNGoogleMapsPlusOnLoad.hpp"

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*) {
return margelo::nitro::rngooglemapsplus::initialize(vm);
return facebook::jni::initialize(vm, []() {
margelo::nitro::rngooglemapsplus::registerAllNatives();
});
}
29 changes: 15 additions & 14 deletions android/src/main/java/com/rngooglemapsplus/GoogleMapsViewImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,10 +57,11 @@ import java.io.ByteArrayInputStream
import java.nio.charset.StandardCharsets

class GoogleMapsViewImpl(
val reactContext: ThemedReactContext,
val locationHandler: LocationHandler,
val playServiceHandler: PlayServicesHandler,
val markerBuilder: MapMarkerBuilder,
private val reactContext: ThemedReactContext,
private val locationHandler: LocationHandler,
private val playServiceHandler: PlayServicesHandler,
private val markerBuilder: MapMarkerBuilder,
private val mapErrorHandler: MapErrorHandler,
) : FrameLayout(reactContext),
GoogleMap.OnCameraMoveStartedListener,
GoogleMap.OnCameraMoveListener,
Expand Down Expand Up @@ -95,7 +96,7 @@ class GoogleMapsViewImpl(
private val pendingCircles = mutableListOf<Pair<String, CircleOptions>>()
private val pendingHeatmaps = mutableListOf<Pair<String, TileOverlayOptions>>()
private val pendingKmlLayers = mutableListOf<Pair<String, String>>()
private val pendingUrlTilesOverlays = mutableListOf<Pair<String, TileOverlayOptions>>()
private val pendingUrlTileOverlays = mutableListOf<Pair<String, TileOverlayOptions>>()

private val markersById = mutableMapOf<String, Marker>()
private val polylinesById = mutableMapOf<String, Polyline>()
Expand Down Expand Up @@ -135,7 +136,7 @@ class GoogleMapsViewImpl(
val result = playServiceHandler.playServicesAvailability()
val errorCode = result.toRNMapErrorCodeOrNull()
if (errorCode != null) {
onMapError?.invoke(errorCode)
mapErrorHandler.report(errorCode, "play services unavailable")
if (errorCode == RNMapErrorCode.PLAY_SERVICES_MISSING ||
errorCode == RNMapErrorCode.PLAY_SERVICES_INVALID
) {
Expand Down Expand Up @@ -267,9 +268,9 @@ class GoogleMapsViewImpl(
pendingKmlLayers.forEach { (id, str) -> addKmlLayerInternal(id, str) }
pendingKmlLayers.clear()
}
if (pendingUrlTilesOverlays.isNotEmpty()) {
pendingUrlTilesOverlays.forEach { (id, opts) -> addUrlTileOverlayInternal(id, opts) }
pendingUrlTilesOverlays.clear()
if (pendingUrlTileOverlays.isNotEmpty()) {
pendingUrlTileOverlays.forEach { (id, opts) -> addUrlTileOverlayInternal(id, opts) }
pendingUrlTileOverlays.clear()
}
}

Expand Down Expand Up @@ -396,7 +397,6 @@ class GoogleMapsViewImpl(
)
}

var onMapError: ((RNMapErrorCode) -> Unit)? = null
var onMapReady: ((Boolean) -> Unit)? = null
var onMapLoaded: ((RNRegion, RNCamera) -> Unit)? = null
var onLocationUpdate: ((RNLocation) -> Unit)? = null
Expand Down Expand Up @@ -505,7 +505,7 @@ class GoogleMapsViewImpl(
onUi {
googleMap?.snapshot { bitmap ->
bitmap
?.encode(context, size, format, compressFormat, quality, resultIsFile)
?.encode(context, size, format, compressFormat, quality, resultIsFile, mapErrorHandler)
?.let(promise::resolve) ?: promise.resolve(null)
}
}
Expand Down Expand Up @@ -754,7 +754,7 @@ class GoogleMapsViewImpl(
kmlLayersById[id] = layer
layer.addLayerToMap()
} catch (_: Exception) {
mapsLog("kml layer parse failed: id=$id")
mapErrorHandler.report(RNMapErrorCode.KML_LAYER_FAILED, "kml layer parse failed: id=$id")
}
}

Expand All @@ -775,7 +775,7 @@ class GoogleMapsViewImpl(
opts: TileOverlayOptions,
) = onUi {
if (googleMap == null) {
pendingUrlTilesOverlays.add(id to opts)
pendingUrlTileOverlays.add(id to opts)
return@onUi
}
urlTileOverlaysById.remove(id)?.remove()
Expand Down Expand Up @@ -805,14 +805,15 @@ class GoogleMapsViewImpl(
it.remove()
}
urlTileOverlaysById.clear()
pendingUrlTilesOverlays.clear()
pendingUrlTileOverlays.clear()
}

fun destroyInternal() =
onUi {
if (destroyed) return@onUi
destroyed = true
lifecycleObserver?.toDestroyedState()
lifecycleObserver = null
markerBuilder.cancelAllJobs()
clearMarkers()
clearPolylines()
Expand Down
4 changes: 2 additions & 2 deletions android/src/main/java/com/rngooglemapsplus/LocationHandler.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ private const val INTERVAL_DEFAULT = 600000L
private const val MIN_UPDATE_INTERVAL = 3600000L

class LocationHandler(
val context: ReactContext,
private val context: ReactContext,
) : LocationSource {
private val fusedLocationClientProviderClient: FusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(context)
Expand Down Expand Up @@ -68,7 +68,7 @@ class LocationHandler(
val activity = context.currentActivity ?: run { return@onUi }

val lr =
if (Build.VERSION.SDK_INT >= 31) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, 10_000L).build()
} else {
@Suppress("DEPRECATION")
Expand Down
24 changes: 24 additions & 0 deletions android/src/main/java/com/rngooglemapsplus/MapErrorHandler.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.rngooglemapsplus

private const val MAPS_LOG_TAG = "react-native-google-maps-plus"

class MapErrorHandler {
@Volatile
var callback: ((RNMapErrorCode, String) -> Unit)? = null

fun report(
code: RNMapErrorCode,
msg: String,
t: Throwable? = null,
) {
if (t != null) {
android.util.Log.w(MAPS_LOG_TAG, msg, t)
} else {
android.util.Log.w(MAPS_LOG_TAG, msg)
}

onUi {
callback?.invoke(code, msg)
}
}
}
13 changes: 0 additions & 13 deletions android/src/main/java/com/rngooglemapsplus/MapHelper.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,3 @@ inline fun <T> onUiSync(crossinline block: () -> T): T {
}
return runBlocking { result.await() }
}

private const val MAPS_LOG_TAG = "react-native-google-maps-plus"

fun mapsLog(msg: String) {
android.util.Log.w(MAPS_LOG_TAG, msg)
}

fun mapsLog(
msg: String,
t: Throwable,
) {
android.util.Log.w(MAPS_LOG_TAG, msg, t)
}
40 changes: 23 additions & 17 deletions android/src/main/java/com/rngooglemapsplus/MapMarkerBuilder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ import java.util.concurrent.ConcurrentHashMap
import kotlin.coroutines.cancellation.CancellationException

class MapMarkerBuilder(
val context: ThemedReactContext,
private val context: ThemedReactContext,
private val mapErrorHandler: MapErrorHandler,
private val scope: CoroutineScope = CoroutineScope(SupervisorJob() + Dispatchers.Default),
) {
private val iconCache =
Expand Down Expand Up @@ -120,8 +121,8 @@ class MapMarkerBuilder(
null
}
}
}.onFailure {
mapsLog("external svg resolve failed")
}.onFailure { t ->
mapErrorHandler.report(RNMapErrorCode.MARKER_ICON_BUILD_FAILED, "external svg resolve failed", t)
}.getOrNull()
}

Expand All @@ -144,8 +145,8 @@ class MapMarkerBuilder(
for (path in candidates) {
try {
return Typeface.createFromAsset(assetManager, path)
} catch (_: Throwable) {
mapsLog("font resolve failed: $path")
} catch (t: Throwable) {
mapErrorHandler.report(RNMapErrorCode.INVALID_ARGUMENT, "font resolve failed: $path", t)
}
}

Expand Down Expand Up @@ -291,15 +292,20 @@ class MapMarkerBuilder(
ensureActive()
onReady(desc)
}
} catch (_: OutOfMemoryError) {
mapsLog("markerId=${m.id} buildIconAsync out of memory")
} catch (e: OutOfMemoryError) {
mapErrorHandler.report(RNMapErrorCode.MARKER_ICON_BUILD_FAILED, "markerId=${m.id} buildIconAsync out of memory", e)
clearIconCache()
withContext(Dispatchers.Main) {
ensureActive()
onReady(createFallbackDescriptor())
}
} catch (_: Throwable) {
mapsLog("markerId=${m.id} buildIconAsync failed")
} catch (_: CancellationException) {
withContext(Dispatchers.Main) {
ensureActive()
onReady(createFallbackDescriptor())
}
} catch (t: Throwable) {
mapErrorHandler.report(RNMapErrorCode.MARKER_ICON_BUILD_FAILED, "markerId=${m.id} buildIconAsync failed", t)
withContext(Dispatchers.Main) {
ensureActive()
onReady(createFallbackDescriptor())
Expand Down Expand Up @@ -343,7 +349,7 @@ class MapMarkerBuilder(
.toInt()

if (wPx <= 0 || hPx <= 0) {
mapsLog("markerId=${markerTag.id} invalid svg size")
mapErrorHandler.report(RNMapErrorCode.INVALID_ARGUMENT, "markerId=${markerTag.id} invalid svg size")
return ImageView(context)
}

Expand All @@ -364,8 +370,8 @@ class MapMarkerBuilder(
}
val drawable = PictureDrawable(svg.renderToPicture())
svgView.setImageDrawable(drawable)
} catch (_: Exception) {
mapsLog("markerId=${markerTag.id} infoWindow: svg render failed")
} catch (e: Exception) {
mapErrorHandler.report(RNMapErrorCode.MARKER_ICON_BUILD_FAILED, "markerId=${markerTag.id} infoWindow: svg render failed", e)
return ImageView(context)
}

Expand Down Expand Up @@ -403,7 +409,7 @@ class MapMarkerBuilder(
.toInt()

if (wPx <= 0 || hPx <= 0) {
mapsLog("markerId=$markerId invalid svg size")
mapErrorHandler.report(RNMapErrorCode.INVALID_ARGUMENT, "markerId=$markerId invalid svg size")
return RenderBitmapResult(createFallbackBitmap(), true)
}

Expand All @@ -415,11 +421,11 @@ class MapMarkerBuilder(
documentWidth = wPx.toFloat()
documentHeight = hPx.toFloat()
}
} catch (_: SVGParseException) {
mapsLog("markerId=$markerId icon: svg parse failed")
} catch (e: SVGParseException) {
mapErrorHandler.report(RNMapErrorCode.INVALID_ARGUMENT, "markerId=$markerId icon: svg parse failed", e)
return RenderBitmapResult(createFallbackBitmap(), true)
} catch (_: IllegalArgumentException) {
mapsLog("markerId=$markerId icon: svg invalid")
} catch (e: IllegalArgumentException) {
mapErrorHandler.report(RNMapErrorCode.INVALID_ARGUMENT, "markerId=$markerId icon: svg invalid", e)
return RenderBitmapResult(createFallbackBitmap(), true)
}

Expand Down
Loading
Loading