From 5b1926ead1d4a6e4fdb7b76d124fb1a2c03dba23 Mon Sep 17 00:00:00 2001 From: pinpong Date: Wed, 12 Nov 2025 10:28:42 +0700 Subject: [PATCH] feat(ios): add location activity type support --- example/src/components/MapWrapper.tsx | 6 ++- .../components/maptConfigDialog/validator.ts | 2 + example/src/screens/LocationScreen.tsx | 51 ++++++++++++++++-- ios/GoogleMapViewImpl.swift | 12 +++-- ios/LocationHandler.swift | 52 ++++++++++++------- ...RNIOSLocationActivityType+Extensions.swift | 18 +++++++ src/types.ts | 9 ++++ 7 files changed, 118 insertions(+), 32 deletions(-) create mode 100644 ios/extensions/RNIOSLocationActivityType+Extensions.swift diff --git a/example/src/components/MapWrapper.tsx b/example/src/components/MapWrapper.tsx index b0fa0d3..20895e5 100644 --- a/example/src/components/MapWrapper.tsx +++ b/example/src/components/MapWrapper.tsx @@ -13,13 +13,14 @@ import type { RNMapUiSettings, RNMapZoomConfig, RNRegion, + RNIndoorBuilding, + RNIndoorLevel, } from 'react-native-google-maps-plus'; import { GoogleMapsView, RNAndroidLocationPriority, - type RNIndoorBuilding, - type RNIndoorLevel, RNIOSLocationAccuracy, + RNIOSLocationActivityType, RNLocationErrorCode, RNMapErrorCode, } from 'react-native-google-maps-plus'; @@ -103,6 +104,7 @@ export default function MapWrapper(props: Props) { ios: { desiredAccuracy: RNIOSLocationAccuracy.ACCURACY_BEST, distanceFilterMeters: 10, + activityType: RNIOSLocationActivityType.NAVIGATION, }, }), [] diff --git a/example/src/components/maptConfigDialog/validator.ts b/example/src/components/maptConfigDialog/validator.ts index e13e473..2998da4 100644 --- a/example/src/components/maptConfigDialog/validator.ts +++ b/example/src/components/maptConfigDialog/validator.ts @@ -15,6 +15,7 @@ import { RNAndroidLocationPermissionResult, RNAndroidLocationPriority, RNIOSLocationAccuracy, + RNIOSLocationActivityType, RNIOSPermissionResult, RNLocationErrorCode, RNMapErrorCode, @@ -272,6 +273,7 @@ const RNAndroidLocationConfigValidator = object({ export const RNIOSLocationConfigValidator = object({ desiredAccuracy: optional(enums(enumValues(RNIOSLocationAccuracy))), distanceFilterMeters: optional(number()), + activityType: optional(enums(enumValues(RNIOSLocationActivityType))), }); export const RNLocationConfigValidator = object({ diff --git a/example/src/screens/LocationScreen.tsx b/example/src/screens/LocationScreen.tsx index 7f5242e..e51c6a5 100644 --- a/example/src/screens/LocationScreen.tsx +++ b/example/src/screens/LocationScreen.tsx @@ -1,14 +1,55 @@ -import React, { useRef } from 'react'; +import React, { useRef, useState } from 'react'; import MapWrapper from '../components/MapWrapper'; import ControlPanel from '../components/ControlPanel'; -import type { GoogleMapsViewRef } from 'react-native-google-maps-plus'; +import { + type GoogleMapsViewRef, + RNAndroidLocationPriority, + RNIOSLocationAccuracy, + RNIOSLocationActivityType, + type RNLocationConfig, +} from 'react-native-google-maps-plus'; +import { RNLocationConfigValidator } from '../components/maptConfigDialog/validator'; +import MapConfigDialog from '../components/maptConfigDialog/MapConfigDialog'; +import { useHeaderButton } from '../hooks/useHeaderButton'; +import { useNavigation } from '@react-navigation/native'; export default function LocationScreen() { const mapRef = useRef(null); + const navigation = useNavigation(); + const [locationConfig, setLocationConfig] = useState({ + android: { + priority: RNAndroidLocationPriority.PRIORITY_HIGH_ACCURACY, + interval: 5000, + minUpdateInterval: 5000, + }, + ios: { + desiredAccuracy: RNIOSLocationAccuracy.ACCURACY_BEST, + distanceFilterMeters: 10, + activityType: RNIOSLocationActivityType.NAVIGATION, + }, + }); + const [dialogVisible, setDialogVisible] = useState(true); + + useHeaderButton(navigation, 'Edit', () => setDialogVisible(true)); return ( - - - + <> + + + + + + visible={dialogVisible} + title="Edit marker" + initialData={locationConfig} + validator={RNLocationConfigValidator} + onClose={() => setDialogVisible(false)} + onSave={(c) => setLocationConfig(c)} + /> + ); } diff --git a/ios/GoogleMapViewImpl.swift b/ios/GoogleMapViewImpl.swift index b553f12..2358f81 100644 --- a/ios/GoogleMapViewImpl.swift +++ b/ios/GoogleMapViewImpl.swift @@ -1,8 +1,8 @@ import CoreLocation import GoogleMaps import GoogleMapsUtils -import UIKit import NitroModules +import UIKit final class GoogleMapsViewImpl: UIView, GMSMapViewDelegate, GMSIndoorDisplayDelegate { @@ -273,10 +273,12 @@ GMSIndoorDisplayDelegate { @MainActor var locationConfig: RNLocationConfig? { didSet { - locationHandler.desiredAccuracy = - locationConfig?.ios?.desiredAccuracy?.toCLLocationAccuracy - locationHandler.distanceFilterMeters = - locationConfig?.ios?.distanceFilterMeters + locationHandler.updateConfig( + desiredAccuracy: locationConfig?.ios?.desiredAccuracy? + .toCLLocationAccuracy, + distanceFilterMeters: locationConfig?.ios?.distanceFilterMeters, + activityType: locationConfig?.ios?.activityType?.toCLActivityType, + ) } } diff --git a/ios/LocationHandler.swift b/ios/LocationHandler.swift index 25ccce1..50468a1 100644 --- a/ios/LocationHandler.swift +++ b/ios/LocationHandler.swift @@ -6,22 +6,19 @@ private let kCLLocationAccuracyDefault: CLLocationAccuracy = kCLLocationAccuracyBest private let kCLDistanceFilterNoneDefault: CLLocationDistance = kCLDistanceFilterNone +private let kCLActivityTypeDefault: CLActivityType = .other final class LocationHandler: NSObject, CLLocationManagerDelegate { private let manager = CLLocationManager() - var desiredAccuracy: CLLocationAccuracy? = kCLLocationAccuracyDefault { - didSet { - manager.desiredAccuracy = desiredAccuracy ?? kCLLocationAccuracyBest - } - } + private var isActive = false - var distanceFilterMeters: CLLocationDistance? = kCLDistanceFilterNoneDefault { - didSet { - manager.distanceFilter = distanceFilterMeters ?? kCLDistanceFilterNone - } - } + private var currentDesiredAccuracy: CLLocationAccuracy = + kCLLocationAccuracyDefault + private var currentDistanceFilter: CLLocationDistance = + kCLDistanceFilterNoneDefault + private var currentActivityType: CLActivityType = kCLActivityTypeDefault var onUpdate: ((CLLocation) -> Void)? var onError: ((_ error: RNLocationErrorCode) -> Void)? @@ -30,7 +27,21 @@ final class LocationHandler: NSObject, CLLocationManagerDelegate { super.init() manager.delegate = self manager.pausesLocationUpdatesAutomatically = true - manager.activityType = .other + } + + func updateConfig( + desiredAccuracy: CLLocationAccuracy?, + distanceFilterMeters: CLLocationDistance?, + activityType: CLActivityType? + ) { + currentDesiredAccuracy = desiredAccuracy ?? kCLLocationAccuracyDefault + manager.desiredAccuracy = currentDesiredAccuracy + + currentDistanceFilter = distanceFilterMeters ?? kCLDistanceFilterNoneDefault + manager.distanceFilter = currentDistanceFilter + + currentActivityType = activityType ?? kCLActivityTypeDefault + manager.activityType = currentActivityType } func showLocationDialog() { @@ -71,11 +82,19 @@ final class LocationHandler: NSObject, CLLocationManagerDelegate { } func start() { - stop() - startUpdates() + guard !isActive else { return } + isActive = true + + manager.location.map { + onUpdate?($0) + } + + manager.startUpdatingLocation() } func stop() { + guard isActive else { return } + isActive = false manager.stopUpdatingLocation() } @@ -104,13 +123,6 @@ final class LocationHandler: NSObject, CLLocationManagerDelegate { } } - private func startUpdates() { - manager.desiredAccuracy = desiredAccuracy ?? kCLLocationAccuracyDefault - manager.distanceFilter = - distanceFilterMeters ?? kCLDistanceFilterNoneDefault - manager.startUpdatingLocation() - } - func locationManager( _ manager: CLLocationManager, didFailWithError error: Error diff --git a/ios/extensions/RNIOSLocationActivityType+Extensions.swift b/ios/extensions/RNIOSLocationActivityType+Extensions.swift new file mode 100644 index 0000000..7802377 --- /dev/null +++ b/ios/extensions/RNIOSLocationActivityType+Extensions.swift @@ -0,0 +1,18 @@ +import CoreLocation + +extension RNIOSLocationActivityType { + var toCLActivityType: CLActivityType { + switch self { + case .other: + return .other + case .navigation: + return .otherNavigation + case .automotive: + return .automotiveNavigation + case .fitness: + return .fitness + case .airborne: + return .airborne + } + } +} diff --git a/src/types.ts b/src/types.ts index bc9a857..3f8cbc0 100644 --- a/src/types.ts +++ b/src/types.ts @@ -297,6 +297,7 @@ export enum RNAndroidLocationPriority { export type RNIOSLocationConfig = { desiredAccuracy?: RNIOSLocationAccuracy; distanceFilterMeters?: number; + activityType?: RNIOSLocationActivityType; }; export enum RNIOSLocationAccuracy { @@ -306,6 +307,14 @@ export enum RNIOSLocationAccuracy { ACCURACY_KILOMETER = 3, } +export enum RNIOSLocationActivityType { + OTHER = 0, + NAVIGATION = 1, + AUTOMOTIVE = 2, + FITNESS = 3, + AIRBORNE = 4, +} + export type RNLocationPermissionResult = { android?: RNAndroidLocationPermissionResult; ios?: RNIOSPermissionResult;