Skip to content
Open
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
2 changes: 1 addition & 1 deletion android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@ dependencies {
//noinspection GradleDynamicVersion
implementation "com.facebook.react:react-native:+"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'com.google.android.libraries.places:places:3.5.0'
implementation 'com.google.android.libraries.places:places:4.0.0'
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.googleplacescompat;

import androidx.annotation.Nullable;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.turbomodule.core.interfaces.TurboModule;

public abstract class RNGooglePlacesCompatSpec extends ReactContextBaseJavaModule implements TurboModule {
public static final String NAME = "RNGooglePlacesCompat";

public RNGooglePlacesCompatSpec(ReactApplicationContext reactContext) {
super(reactContext);
}

@Override
public String getName() {
return NAME;
}

@ReactMethod
public abstract void initializePlaceClient(String apiKey, @Nullable Boolean sessionBasedAutocomplete);

@ReactMethod
public abstract void refreshSessionToken();

@ReactMethod
public abstract void setSessionBasedAutocomplete(boolean enabled);

@ReactMethod
public abstract void openAutocompleteModal(ReadableMap options, ReadableArray fields, Promise promise);

@ReactMethod
public abstract void getAutocompletePredictions(String query, ReadableMap options, Promise promise);

@ReactMethod
public abstract void lookUpPlaceByID(String placeID, ReadableArray fields, Promise promise);

@ReactMethod
public abstract void getCurrentPlace(ReadableArray fields, Promise promise);
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ import androidx.core.content.ContextCompat
import com.facebook.react.bridge.ActivityEventListener
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.Promise
import androidx.annotation.Nullable
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.WritableMap
Expand All @@ -36,26 +35,26 @@ import com.google.android.libraries.places.widget.model.AutocompleteActivityMode
import com.googleplacescompat.RNGooglePlacesPlaceFieldEnum.Companion.findByFieldKey
import com.googleplacescompat.RNGooglePlacesPlaceTypeEnum.Companion.findByTypeId

// Changed to extend RNGooglePlacesCompatSpec and remove ReactContextBaseJavaModule
class RNGooglePlacesModule(private val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext), ActivityEventListener {
RNGooglePlacesCompatSpec(reactContext), ActivityEventListener {

private var pendingPromise: Promise? = null
private var lastSelectedFields: List<Place.Field>? = null
private lateinit var placesClient: PlacesClient
private var sessionToken: AutocompleteSessionToken? = null
private var sessionBasedAutoCompleteEnabled = false

override fun getName(): String {
return REACT_CLASS
}
// getName is now inherited from RNGooglePlacesCompatSpec

@ReactMethod
fun initializePlaceClient(apiKey: String, sessionBasedAutocomplete: Boolean = false) {
// @ReactMethod annotation removed, @Nullable added for sessionBasedAutocomplete
override fun initializePlaceClient(apiKey: String, @Nullable sessionBasedAutocomplete: Boolean?) {
val enableSessionBasedAutocomplete = sessionBasedAutocomplete ?: false
if (!Places.isInitialized() && apiKey.isNotBlank()) {
Places.initializeWithNewPlacesApiEnabled(reactContext.applicationContext, apiKey)
placesClient = Places.createClient(reactContext.applicationContext)
reactContext.addActivityEventListener(this)
sessionBasedAutoCompleteEnabled = sessionBasedAutocomplete
this.sessionBasedAutoCompleteEnabled = enableSessionBasedAutocomplete // Use the resolved value
refreshSessionToken()
}
}
Expand Down Expand Up @@ -95,20 +94,20 @@ class RNGooglePlacesModule(private val reactContext: ReactApplicationContext) :
* Exposed React's methods
*/

@ReactMethod
fun refreshSessionToken() {
// @ReactMethod annotation removed
override fun refreshSessionToken() {
if (sessionBasedAutoCompleteEnabled) {
sessionToken = AutocompleteSessionToken.newInstance()
}
}

@ReactMethod
fun setSessionBasedAutocomplete(enabled: Boolean) {
// @ReactMethod annotation removed
override fun setSessionBasedAutocomplete(enabled: Boolean) {
sessionBasedAutoCompleteEnabled = enabled
}

@ReactMethod
fun openAutocompleteModal(options: ReadableMap, fields: ReadableArray, promise: Promise) {
// @ReactMethod annotation removed
override fun openAutocompleteModal(options: ReadableMap, fields: ReadableArray, promise: Promise) {
if (!Places.isInitialized()) {
promise.reject(
"E_API_KEY_ERROR",
Expand Down Expand Up @@ -153,8 +152,8 @@ class RNGooglePlacesModule(private val reactContext: ReactApplicationContext) :
)
}

@ReactMethod
fun getAutocompletePredictions(query: String?, options: ReadableMap, promise: Promise) {
// @ReactMethod annotation removed, query marked as @Nullable based on typical spec generation
override fun getAutocompletePredictions(query: @Nullable String?, options: ReadableMap, promise: Promise) {
pendingPromise = promise
if (!Places.isInitialized()) {
promise.reject(
Expand Down Expand Up @@ -213,8 +212,8 @@ class RNGooglePlacesModule(private val reactContext: ReactApplicationContext) :
}
}

@ReactMethod
fun lookUpPlaceByID(placeID: String?, fields: ReadableArray, promise: Promise) {
// @ReactMethod annotation removed, placeID marked as @Nullable
override fun lookUpPlaceByID(placeID: @Nullable String?, fields: ReadableArray, promise: Promise) {
pendingPromise = promise
if (!Places.isInitialized()) {
promise.reject(
Expand Down Expand Up @@ -253,9 +252,9 @@ class RNGooglePlacesModule(private val reactContext: ReactApplicationContext) :
}
}

@ReactMethod
// @ReactMethod annotation removed
@RequiresPermission(allOf = [permission.ACCESS_FINE_LOCATION, permission.ACCESS_WIFI_STATE])
fun getCurrentPlace(fields: ReadableArray, promise: Promise) {
override fun getCurrentPlace(fields: ReadableArray, promise: Promise) {
if (ContextCompat.checkSelfPermission(
reactContext.applicationContext,
permission.ACCESS_WIFI_STATE
Expand Down Expand Up @@ -504,6 +503,6 @@ class RNGooglePlacesModule(private val reactContext: ReactApplicationContext) :
companion object {
const val TAG = "RNGooglePlaces"
var AUTOCOMPLETE_REQUEST_CODE = 360
var REACT_CLASS = "RNGooglePlaces"
// REACT_CLASS constant is no longer needed here as getName() uses RNGooglePlacesCompatSpec.NAME
}
}
Original file line number Diff line number Diff line change
@@ -1,24 +1,51 @@
package com.googleplacescompat

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.JavaScriptModule
import com.facebook.react.TurboReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo
import com.facebook.react.module.model.ReactModuleInfoProvider
import com.facebook.react.uimanager.ViewManager

class RNGooglePlacesPackage : ReactPackage {
// Deprecated RN 0.47
fun createJSModules(): List<Class<out JavaScriptModule?>> {
return emptyList()
class RNGooglePlacesPackage : TurboReactPackage() {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
return if (name == RNGooglePlacesCompatSpec.NAME) {
RNGooglePlacesModule(reactContext)
} else {
null
}
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider {
return ReactModuleInfoProvider {
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap()
val isTurboModule = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
moduleInfos[RNGooglePlacesCompatSpec.NAME] = ReactModuleInfo(
RNGooglePlacesCompatSpec.NAME,
RNGooglePlacesCompatSpec.NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
true, // hasConstants
false, // isCxxModule
isTurboModule // isTurboModule
)
moduleInfos
}
}

// Below methods are for backward compatibility with the old architecture.
// They are called only if the New Architecture is not enabled.
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
val modules: MutableList<NativeModule> = ArrayList()
modules.add(RNGooglePlacesModule(reactContext))
return modules
// For backward compatibility, when NewArch is disabled.
// Otherwise, modules are provided via getModule().
// Check if newArchEnabled is false if possible, or rely on RN to call this appropriately.
// For now, providing the module here for old arch.
// In a pure New Arch setup, this might return emptyList() if getModule handles all cases.
return listOf(RNGooglePlacesModule(reactContext))
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList()
}
}
2 changes: 1 addition & 1 deletion example/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ buildscript {
targetSdkVersion = 34

ndkVersion = "25.1.8937393"
kotlinVersion = "1.9.20"
kotlinVersion = "1.9.22"
}
repositories {
google()
Expand Down
2 changes: 1 addition & 1 deletion example/android/gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
newArchEnabled=false
newArchEnabled=true

# Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead.
Expand Down
10 changes: 5 additions & 5 deletions example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,16 @@
"build:ios": "cd ios && xcodebuild -workspace GooglePlacesCompatExample.xcworkspace -scheme GooglePlacesCompatExample -configuration Debug -sdk iphonesimulator CC=clang CPLUSPLUS=clang++ LD=clang LDPLUSPLUS=clang++ GCC_OPTIMIZATION_LEVEL=0 GCC_PRECOMPILE_PREFIX_HEADER=YES ASSETCATALOG_COMPILER_OPTIMIZATION=time DEBUG_INFORMATION_FORMAT=dwarf COMPILER_INDEX_STORE_ENABLE=NO"
},
"dependencies": {
"react": "18.2.0",
"react-native": "0.73.9"
"react": "19.0.0-rc.1",
"react-native": "0.79.0"
},
"devDependencies": {
"@babel/core": "7.23.9",
"@babel/preset-env": "7.23.9",
"@babel/runtime": "7.23.9",
"@react-native/babel-preset": "0.73.21",
"@react-native/metro-config": "0.73.5",
"@react-native/typescript-config": "0.73.1",
"@react-native/babel-preset": "0.79.0",
"@react-native/metro-config": "0.79.0",
"@react-native/typescript-config": "0.79.0",
"babel-plugin-module-resolver": "5.0.0"
},
"engines": {
Expand Down
104 changes: 80 additions & 24 deletions ios/GooglePlacesCompat.mm
Original file line number Diff line number Diff line change
@@ -1,38 +1,94 @@
#import <React/RCTBridgeModule.h>
#import "RNGooglePlacesCompatSpec.h" // Import the generated spec header
#import <React/RCTLog.h>
#import <React/RCTUtils.h> // For RCTPromise*

@interface RCT_EXTERN_MODULE(RNGooglePlaces, NSObject)
// Import the Swift bridging header. The name is typically <ProductName>-Swift.h
// Assuming the product name for the library is "GooglePlacesCompat"
// If the example app is the one building, it might be "GooglePlacesCompatExample-Swift.h"
// For a library, it's common to have its own bridging header if Swift calls ObjC,
// but for ObjC calling Swift, this import in the .mm file is key.
// Let's assume the library's product module name is GooglePlacesCompat.
#import "GooglePlacesCompat-Swift.h"

RCT_EXTERN_METHOD(initializePlaceClient:(NSString *)apiKey
sessionBasedAutocomplete:(nonnull NSNumber *)enabled)

NS_ASSUME_NONNULL_BEGIN

RCT_EXTERN_METHOD(openAutocompleteModal:(NSDictionary *)options
withFields:(NSArray<NSString *> *)fields
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
// Use the same name as in package.json's codegenConfig and the JS spec.
@implementation RNGooglePlaces (RNGooglePlacesCompatSpec)

RCT_EXTERN_METHOD(getAutocompletePredictions:(NSString *)query
filterOptions:(NSDictionary *)filterOptions
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
RCT_EXPORT_MODULE(RNGooglePlacesCompat)

RCT_EXTERN_METHOD(lookUpPlaceByID:(NSString *)placeID
withFields:(NSArray<NSString *> *)fields
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
// Make sure the Swift class is also named RNGooglePlaces
// Or get a handle to the Swift instance.
// The Swift class has `static var instance: RNGooglePlaces?`
// We'll rely on the Swift class to manage its instance.

RCT_EXTERN_METHOD(getCurrentPlace:(NSArray<NSString *> *)fields
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
(const facebook::react::ObjCTurboModule::InitParams &)params
{
// This is a simplified way to get the Swift instance.
// In a more complex setup, you might manage the instance differently.
// The key is that the Swift class `RNGooglePlaces` should handle the actual logic.
// We assume the Swift `RNGooglePlaces.instance` is initialized appropriately (e.g. in its init or via an app delegate call).
// If `RNGooglePlaces.instance` is nil, this will fail.
// A more robust approach might involve ensuring the instance is created if nil,
// or that the module is initialized in a way that guarantees the instance exists.

// For TurboModules, the native module instance is often created here.
// Let's ensure we have a Swift instance. The Swift `init` creates one.
RNGooglePlaces *swiftModule = [RNGooglePlaces new]; // This will set RNGooglePlaces.instance via Swift's init()

return std::make_shared<facebook::react::CommTurboModule>(swiftModule, params.jsCallInvoker);
}

RCT_EXTERN_METHOD(setSessionBasedAutocomplete:(nonnull NSNumber *)enabled)
- (void)initializePlaceClient:(NSString *)apiKey sessionBasedAutocomplete:(@Nullable NSNumber *)sessionBasedAutocomplete {
[[RNGooglePlaces instance] initializePlaceClient:apiKey sessionBasedAutocomplete:sessionBasedAutocomplete ?: @(NO)];
}

RCT_EXTERN_METHOD(refreshSessionToken)
- (void)refreshSessionToken {
[[RNGooglePlaces instance] refreshSessionToken];
}

- (void)setSessionBasedAutocomplete:(BOOL)enabled {
[[RNGooglePlaces instance] setSessionBasedAutocomplete:@(enabled)];
}

+ (BOOL)requiresMainQueueSetup
{
return NO;
- (void)openAutocompleteModal:(NSDictionary *)options
fields:(NSArray<NSString *> *)fields
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
[[RNGooglePlaces instance] openAutocompleteModal:options withFields:fields resolver:resolve rejecter:reject];
}

- (void)getAutocompletePredictions:(NSString *)query
options:(NSDictionary *)options
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
[[RNGooglePlaces instance] getAutocompletePredictions:query filterOptions:options resolver:resolve rejecter:reject];
}

- (void)lookUpPlaceByID:(NSString *)placeID
fields:(NSArray<NSString *> *)fields
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
[[RNGooglePlaces instance] lookUpPlaceByID:placeID withFields:fields resolver:resolve rejecter:reject];
}

- (void)getCurrentPlace:(NSArray<NSString *> *)fields
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject {
[[RNGooglePlaces instance] getCurrentPlace:fields resolver:resolve rejecter:reject];
}

// Required for main queue setup if not overridden in Swift.
// The Swift class has `static func requiresMainQueueSetup() -> Bool { return false }`
// and `func methodQueue() -> DispatchQueue { return DispatchQueue.main }`
// For TurboModules, methodQueue is usually respected.
// If specific queue setup is needed beyond what RCTCxxModule/CommTurboModule provides by default
// (which is background for native methods, main for view managers), it's handled differently.
// For now, relying on the Swift implementation and default TurboModule behavior.
// The `requiresMainQueueSetup` from the old arch is less relevant here.

@end

NS_ASSUME_NONNULL_END
Loading
Loading