({
40 | presentationStyle: "fullscreen",
41 | contrastColor: "#FFFFFF",
42 | color: "#FF0000",
43 | searchPlaceholder: "Search...",
44 | title: "Choose Place",
45 | enableGeocoding: true,
46 | enableSearch: true,
47 | enableUserLocation: true,
48 | enableLargeTitle: true,
49 | rejectOnCancel: true,
50 | locale: "en-US",
51 | initialCoordinates: {
52 | latitude: 25.2048,
53 | longitude: 55.2708,
54 | },
55 | });
56 | const pressHandlerWithOptions = () => {
57 | pickPlace(options)
58 | .then(setResults)
59 | .catch((error) => {
60 | console.log(error);
61 | setResults(undefined);
62 | });
63 | };
64 |
65 | const pressHandler = () => {
66 | pickPlace()
67 | .then(setResults)
68 | .catch((error) => {
69 | console.log(error);
70 | setResults(undefined);
71 | });
72 | };
73 |
74 | return (
75 |
81 | Place Picker Playground
82 | (Click to edit)
83 | {
90 | setOptions((prev) => ({
91 | ...prev,
92 | presentationStyle:
93 | prev.presentationStyle === "modal" ? "fullscreen" : "modal",
94 | }));
95 | }}
96 | />
97 | {
103 | Alert.prompt(
104 | "Contrast Color",
105 | "Enter color hex value with #",
106 | (text) => {
107 | setOptions((prev) => ({
108 | ...prev,
109 | contrastColor: text,
110 | }));
111 | },
112 | "plain-text",
113 | options.contrastColor,
114 | );
115 | }}
116 | />
117 | {
123 | Alert.prompt(
124 | "Color",
125 | "Enter color hex value with #",
126 | (text) => {
127 | setOptions((prev) => ({
128 | ...prev,
129 | color: text,
130 | }));
131 | },
132 | "plain-text",
133 | options.color,
134 | );
135 | }}
136 | />
137 | {
141 | Alert.prompt(
142 | "Title",
143 | "Enter a new title",
144 | (text) => {
145 | setOptions((prev) => ({
146 | ...prev,
147 | title: text,
148 | }));
149 | },
150 | "plain-text",
151 | options.title,
152 | );
153 | }}
154 | />
155 | {
159 | Alert.prompt(
160 | "Search Placeholder",
161 | "Enter a new search placeholder",
162 | (text) => {
163 | setOptions((prev) => ({
164 | ...prev,
165 | searchPlaceholder: text,
166 | }));
167 | },
168 | "plain-text",
169 | options.searchPlaceholder,
170 | );
171 | }}
172 | />
173 | {
177 | Alert.prompt(
178 | "Locale",
179 | "Enter a new locale",
180 | (text) => {
181 | setOptions((prev) => ({
182 | ...prev,
183 | locale: text,
184 | }));
185 | },
186 | "plain-text",
187 | options.locale,
188 | );
189 | }}
190 | />
191 | {
197 | pickPlace({
198 | presentationStyle: "modal",
199 | searchPlaceholder: "Search...",
200 | title: "Set initial coordinates",
201 | enableLargeTitle: false,
202 | enableSearch: false,
203 | enableGeocoding: false,
204 | })
205 | .then((r) => {
206 | setOptions((prev) => ({
207 | ...prev,
208 | initialCoordinates: r.coordinate,
209 | }));
210 | })
211 | .catch(console.log);
212 | }}
213 | />
214 | {
218 | setOptions((prev) => ({
219 | ...prev,
220 | enableGeocoding: !prev.enableGeocoding,
221 | }));
222 | }}
223 | />
224 | {
228 | setOptions((prev) => ({
229 | ...prev,
230 | enableSearch: !prev.enableSearch,
231 | }));
232 | }}
233 | />
234 | {
238 | setOptions((prev) => ({
239 | ...prev,
240 | enableUserLocation: !prev.enableUserLocation,
241 | }));
242 | }}
243 | />
244 | {
248 | setOptions((prev) => ({
249 | ...prev,
250 | enableLargeTitle: !prev.enableLargeTitle,
251 | }));
252 | }}
253 | />
254 | {
258 | setOptions((prev) => ({
259 | ...prev,
260 | rejectOnCancel: !prev.rejectOnCancel,
261 | }));
262 | }}
263 | />
264 |
268 |
269 | {results && (
270 | <>
271 | Results:
272 | {JSON.stringify(results, null, "\t")}
273 | >
274 | )}
275 |
276 | Debug Info:
277 |
278 |
283 |
284 |
285 |
289 |
290 | );
291 | }
292 |
293 | const styles = StyleSheet.create({
294 | scrollView: {
295 | flex: 1,
296 | },
297 | container: {
298 | padding: 20,
299 | paddingTop: 70,
300 | },
301 | title: {
302 | fontSize: 25,
303 | fontWeight: "900",
304 | marginBottom: 5,
305 | textAlign: "left",
306 | },
307 | subtitle: {
308 | fontSize: 15,
309 | fontWeight: "300",
310 | marginBottom: 20,
311 | textAlign: "left",
312 | },
313 | code: {
314 | fontSize: 12,
315 | fontWeight: "300",
316 | marginBottom: 20,
317 | textAlign: "left",
318 | color: "#666",
319 | fontFamily: "Courier",
320 | },
321 | });
322 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | ### Features
25 |
26 | - [x] 🎨 Theme customization.
27 | - [x] 📱 UI written natively.
28 | - [x] 🗺️ Location reverse-geocoding (coordinate -> address).
29 | - [x] 🔍 Searchable (users can search for location).
30 | - [x] 📍 Device location.
31 | - [x] ⚙️ Fully configurable.
32 | - [x] 🏗️ Supporting Turbo Modules (New Arch) with backward compatibility.
33 | - [x] ⚡ Renders on top of the app (Blazing Fast).
34 | - [x] 📐 Well typed.
35 | - [x] 📦 Significantly small package.
36 | - [x] 🔗 No peer dependencies except React and React-Native [[1]](#extra).
37 |
38 | ### How is it working?
39 |
40 | > This plugin is built only by create native page `UIViewController` for iOS or `Activity` for Android. and present the page in front of React Native Application without any special dependencies just native code
41 |
42 | ## Installation
43 |
44 | ```sh
45 | npm install react-native-place-picker
46 | # or
47 | yarn add react-native-place-picker
48 | # or
49 | pnpm add react-native-place-picker
50 | # or
51 | bun add react-native-place-picker
52 | ```
53 |
54 | ### Expo
55 |
56 | - You need to add `expo-dev-client` and run `expo run:ios` or `expo run:android`
57 |
58 | > **Info** Expo managed app not supported 🚧
59 |
60 | ### iOS
61 |
62 | - If you want to enable user current location button you have to add this to your `Info.plist`
63 |
64 | ```xml
65 | NSLocationWhenInUseUsageDescription
66 | YOUR_PURPOSE_HERE
67 | ```
68 |
69 | ### Android ⚠️
70 |
71 | - Add to your `AndroidManifest.xml` you Google Map API Key or your application will crash
72 |
73 | ```xml
74 |
77 | ```
78 |
79 | ### Request
80 |
81 | ```js
82 | import { pickPlace } from "react-native-place-picker";
83 |
84 | pickPlace({
85 | enableUserLocation: true,
86 | enableGeocoding: true,
87 | color: "#FF00FF",
88 | // Range selection (optional)
89 | enableRangeSelection: true,
90 | initialRadius: 2000,
91 | minRadius: 250,
92 | maxRadius: 10000,
93 | radiusColor: '#FF00FF33',
94 | radiusStrokeColor: '#FF00FF',
95 | radiusStrokeWidth: 2,
96 | //...etc
97 | })
98 | .then(console.log)
99 | .catch(console.log);
100 |
101 | // or
102 |
103 | pickPlace().then(console.log).catch(console.log);
104 | ```
105 |
106 | ### Result
107 |
108 | ```ts
109 |
110 | {
111 | /**
112 | * @description Selected coordinate.
113 | */
114 | coordinate: PlacePickerCoordinate;
115 | /**
116 | * @description Geocoded address for selected location.
117 | * @if `enableGeocoding: true`
118 | */
119 | address?: PlacePickerAddress;
120 | /**
121 | * @description Did cancel the place picker window without selecting.
122 | */
123 | didCancel: boolean;
124 | }
125 |
126 | ```
127 |
128 | ### PlacePickerOptions
129 |
130 | | Property | Type | Description | Default |
131 | | -------------------- | ---------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------- |
132 | | `presentationStyle` | `PlacePickerPresentationStyle` \| string | Presentation style of the place picker window. **iOS only** | `'fullscreen'` |
133 | | `title` | `string` | The title of the place picker window. | `'Choose Place'` |
134 | | `searchPlaceholder` | `string` | Placeholder for the search bar in the place picker window. | `'Search...'` |
135 | | `color` | `string` | Primary color of the theme (map pin, shadow, etc.). | `'#FF0000'` |
136 | | `contrastColor` | `string` | Contrast color for the primary color. | `'#FFFFFF'` |
137 | | `locale` | `string` | Locale for the returned address. | `'en-US'` |
138 | | `initialCoordinates` | `PlacePickerCoordinate` | Initial map position. | `{ latitude: 25.2048, longitude: 55.2708 }` |
139 | | `enableGeocoding` | `boolean` | geocoding for the selected address. | `true` |
140 | | `enableSearch` | `boolean` | search bar for searching specific positions. | `true` |
141 | | `enableUserLocation` | `boolean` | current user position button. Requires setup. | `true` |
142 | | `enableLargeTitle` | `boolean` | large navigation bar title of the UIViewController. **iOS only** | `true` |
143 | | `rejectOnCancel` | `boolean` | Reject and return nothing if the user dismisses the window without selecting a place. | `true` |
144 | | `enableRangeSelection` | `boolean` | Enable draggable radius selection overlay. | `false` |
145 | | `initialRadius` | `number` | Initial radius in meters when range selection is enabled. | `1000` |
146 | | `minRadius` | `number` | Minimum allowed radius in meters. | `100` |
147 | | `maxRadius` | `number` | Maximum allowed radius in meters. | `10000` |
148 | | `radiusColor` | `string` | Fill color of radius circle (falls back to `color` with alpha). | `''` |
149 | | `radiusStrokeColor` | `string` | Stroke color of radius circle (falls back to `color`). | `''` |
150 | | `radiusStrokeWidth` | `number` | Stroke width of radius circle in pixels. | `2` |
151 |
152 | ### PlacePickerPresentationStyle
153 |
154 | | Enum Value | Description |
155 | | ------------ | -------------------------------------- |
156 | | `modal` | Presentation style as a modal window. |
157 | | `fullscreen` | Presentation style in fullscreen mode. |
158 |
159 | ### PlacePickerAddress
160 |
161 | | Property | Type | Description |
162 | | ------------ | -------- | --------------------------- |
163 | | `name` | `string` | Name of the location. |
164 | | `streetName` | `string` | Street name of the address. |
165 | | `city` | `string` | City of the address. |
166 | | `state` | `string` | State of the address. |
167 | | `zipCode` | `string` | Zip code of the address. |
168 | | `country` | `string` | Country of the address. |
169 |
170 | ### PlacePickerCoordinate
171 |
172 | | Property | Type | Description |
173 | | ----------- | -------- | -------------------------- |
174 | | `latitude` | `number` | Latitude of the location. |
175 | | `longitude` | `number` | Longitude of the location. |
176 |
177 | ### PlacePickerResults
178 |
179 | | Property | Type | Description |
180 | | ------------ | ----------------------- | -------------------------------------------------------------- |
181 | | `coordinate` | `PlacePickerCoordinate` | Selected coordinate. |
182 | | `address` | `PlacePickerAddress` | Geocoded address for selected location (if `enableGeocoding`). |
183 | | `didCancel` | `boolean` | Indicates if the place picker was canceled without selecting. |
184 | | `radius` | `number` | Selected radius in meters (if range selection enabled). |
185 | | `radiusCoordinates` | `{ center, bounds }` | Center and bounds of the selected area. |
186 |
187 | ## Contributing
188 |
189 | See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow.
190 |
191 | ## License
192 |
193 | MIT
194 |
--------------------------------------------------------------------------------
/android/src/main/java/expo/modules/placepicker/PlacePickerActivity.kt:
--------------------------------------------------------------------------------
1 | package expo.modules.placepicker
2 |
3 |
4 | import android.Manifest
5 | import android.animation.ObjectAnimator
6 | import android.animation.PropertyValuesHolder
7 | import android.content.pm.PackageManager
8 | import android.graphics.Color
9 | import android.location.Address
10 | import android.location.Geocoder
11 | import android.os.Bundle
12 | import android.view.Menu
13 | import android.view.MenuInflater
14 | import android.view.MenuItem
15 | import android.view.View
16 | import android.widget.SearchView
17 | import android.widget.Toast
18 | import androidx.appcompat.app.AppCompatActivity
19 | import androidx.core.app.ActivityCompat
20 | import androidx.core.graphics.toColorInt
21 | import androidx.core.view.WindowCompat
22 | import com.google.android.gms.location.FusedLocationProviderClient
23 | import com.google.android.gms.location.LocationServices
24 | import com.google.android.gms.maps.CameraUpdateFactory
25 | import com.google.android.gms.maps.GoogleMap
26 | import com.google.android.gms.maps.OnMapReadyCallback
27 | import com.google.android.gms.maps.SupportMapFragment
28 | import com.google.android.gms.maps.model.LatLng
29 | import com.google.android.gms.maps.model.Circle
30 | import com.google.android.gms.maps.model.CircleOptions
31 | import java.util.Locale
32 | import java.util.concurrent.Executors
33 | import java.util.concurrent.ScheduledFuture
34 | import java.util.concurrent.TimeUnit
35 |
36 |
37 | class PlacePickerActivity : AppCompatActivity(), OnMapReadyCallback,
38 | GoogleMap.OnCameraMoveStartedListener, GoogleMap.OnCameraIdleListener {
39 |
40 | private lateinit var pinView: View
41 | private lateinit var mMap: GoogleMap
42 | private lateinit var mMenu: Menu
43 | private lateinit var pinViewAnimation: ObjectAnimator
44 | private var mLocationProvider: FusedLocationProviderClient? = null
45 | private lateinit var geocoder: Geocoder
46 | private var radiusCircle: Circle? = null
47 | private var currentRadius: Double = 1000.0
48 | private var radiusHandle: View? = null
49 | private var isDragging: Boolean = false
50 |
51 | private fun getLocationProvider(): FusedLocationProviderClient {
52 | if (mLocationProvider == null) {
53 | mLocationProvider = LocationServices.getFusedLocationProviderClient(this)
54 | }
55 | return mLocationProvider!!
56 | }
57 |
58 | override fun onCreate(savedInstanceState: Bundle?) {
59 | super.onCreate(savedInstanceState)
60 | setUpMapLocale()
61 | setContentView(R.layout.activity_place_picker)
62 | if (supportActionBar == null) {
63 | val toolbar = findViewById(R.id.toolbar)
64 | setSupportActionBar(toolbar)
65 | }
66 | WindowCompat.setDecorFitsSystemWindows(window, false)
67 | gatherViews()
68 | geocoder =
69 | Geocoder(
70 | applicationContext, Locale.forLanguageTag(PlacePickerState.globalOptions.locale) ?: Locale.getDefault()
71 | )
72 | this.title = PlacePickerState.globalOptions.title
73 | supportActionBar?.subtitle = ""
74 | if (PlacePickerState.globalOptions.enableRangeSelection) {
75 | currentRadius = PlacePickerState.globalOptions.initialRadius
76 | .coerceIn(PlacePickerState.globalOptions.minRadius, PlacePickerState.globalOptions.maxRadius)
77 | }
78 | }
79 |
80 | private fun gatherViews() {
81 | pinView = findViewById(R.id.pinView)
82 | pinView.background.setTint(PlacePickerState.globalOptions.color.toColorInt())
83 | val scaleXVal: PropertyValuesHolder = PropertyValuesHolder.ofFloat("scaleX", 1.5F)
84 | val scaleYVal: PropertyValuesHolder = PropertyValuesHolder.ofFloat("scaleY", 1.5F)
85 | val transVal: PropertyValuesHolder = PropertyValuesHolder.ofFloat("translationY", -50F)
86 | pinViewAnimation =
87 | ObjectAnimator.ofPropertyValuesHolder(pinView, scaleXVal, scaleYVal, transVal).apply {
88 | duration = 300
89 | }
90 | val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment
91 | mapFragment.getMapAsync(this)
92 | }
93 |
94 | private fun setUpMapLocale() {
95 | val locale = Locale(PlacePickerState.globalOptions.locale)
96 | Locale.setDefault(locale)
97 | baseContext.resources.configuration.setLocale(locale)
98 | }
99 |
100 | override fun onMapReady(googleMap: GoogleMap) {
101 | mMap = googleMap
102 | mMap.setOnCameraIdleListener(this)
103 | mMap.setOnCameraMoveStartedListener(this)
104 | mMap.uiSettings.apply {
105 | isCompassEnabled = true
106 | isMapToolbarEnabled = true
107 | isMyLocationButtonEnabled = true
108 |
109 | }
110 | mMap.moveCamera(
111 | CameraUpdateFactory.newLatLngZoom(
112 | LatLng(
113 | PlacePickerState.globalOptions.initialCoordinates?.latitude ?: 25.2048,
114 | PlacePickerState.globalOptions.initialCoordinates?.longitude ?: 55.2708
115 | ), 15F
116 | )
117 | )
118 | if (PlacePickerState.globalOptions.enableRangeSelection) {
119 | setupRadiusSelection()
120 | }
121 | }
122 |
123 | override fun onCameraMoveStarted(reason: Int) {
124 | if (!animationIsUp) {
125 | pinViewAnimation.start()
126 | animationIsUp = true
127 | }
128 | }
129 |
130 | private var animationIsUp = false
131 | private var mapMoveTask: ScheduledFuture<*>? = null
132 | private var lastAddress: Address? = null
133 | override fun onCameraIdle() {
134 | if (!PlacePickerState.globalOptions.enableGeocoding) {
135 | pinViewAnimation.reverse()
136 | animationIsUp = false
137 | if (PlacePickerState.globalOptions.enableRangeSelection) {
138 | updateRadiusHandlePosition()
139 | updateCircle()
140 | }
141 | return
142 | }
143 | mapMoveTask?.cancel(true)
144 | lastAddress = null
145 | val lat = mMap.cameraPosition.target.latitude
146 | val long = mMap.cameraPosition.target.longitude
147 | mapMoveTask = Executors.newSingleThreadScheduledExecutor().schedule({
148 | val address = geocoder.getFromLocation(lat, long, 1)
149 | lastAddress = address?.first()
150 | runOnUiThread {
151 | try {
152 | supportActionBar?.subtitle = lastAddress?.featureName ?: "Unknown location"
153 | pinViewAnimation.reverse()
154 | animationIsUp = false
155 | if (PlacePickerState.globalOptions.enableRangeSelection) {
156 | updateRadiusHandlePosition()
157 | updateCircle()
158 | }
159 | } catch (e: Exception) {
160 | supportActionBar?.subtitle = ""
161 | pinViewAnimation.reverse()
162 | animationIsUp = false
163 | if (PlacePickerState.globalOptions.enableRangeSelection) {
164 | updateRadiusHandlePosition()
165 | updateCircle()
166 | }
167 | }
168 | }
169 | }, 1, TimeUnit.SECONDS)
170 | }
171 |
172 | override fun onRequestPermissionsResult(
173 | requestCode: Int,
174 | permissions: Array,
175 | grantResults: IntArray
176 | ) {
177 | super.onRequestPermissionsResult(requestCode, permissions, grantResults)
178 | when (requestCode) {
179 | 1 -> {
180 | if (grantResults.isNotEmpty()
181 | && grantResults[0] == PackageManager.PERMISSION_GRANTED
182 | ) {
183 | val findMenu = mMenu.findItem(R.id.findMe)
184 | findMenu.isVisible = true
185 | }
186 | return
187 | }
188 | }
189 | }
190 |
191 | private fun isLocationPermissionGranted(): Boolean {
192 | return if (ActivityCompat.checkSelfPermission(
193 | this,
194 | Manifest.permission.ACCESS_COARSE_LOCATION
195 | ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
196 | this,
197 | Manifest.permission.ACCESS_FINE_LOCATION
198 | ) != PackageManager.PERMISSION_GRANTED
199 | ) {
200 | ActivityCompat.requestPermissions(
201 | this,
202 | arrayOf(
203 | Manifest.permission.ACCESS_FINE_LOCATION,
204 | Manifest.permission.ACCESS_COARSE_LOCATION
205 | ),
206 | 1
207 | )
208 | false
209 | } else {
210 | true
211 | }
212 | }
213 |
214 | // MENU SECTION
215 | private fun setupSearch(menu: Menu) {
216 | val searchItem = menu.findItem(R.id.search)
217 | if (!PlacePickerState.globalOptions.enableSearch) {
218 | searchItem.isVisible = false
219 | return
220 | }
221 | val searchView = searchItem.actionView as SearchView
222 | searchView.queryHint = PlacePickerState.globalOptions.searchPlaceholder
223 | val that = this
224 |
225 | searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
226 | override fun onQueryTextSubmit(query: String): Boolean {
227 | try {
228 | val location = geocoder.getFromLocationName(query, 1)
229 | if (location?.isEmpty()!!) {
230 | Toast.makeText(that, "No location was founded", Toast.LENGTH_SHORT).show()
231 | return true
232 | }
233 | val address = location.first()
234 | if (address != null) {
235 | mMap.animateCamera(
236 | CameraUpdateFactory.newLatLngZoom(
237 | LatLng(
238 | address.latitude,
239 | address.longitude
240 | ), 15F
241 | )
242 | )
243 | }
244 | searchView.onActionViewCollapsed()
245 | } catch (error: Exception) {
246 | Toast.makeText(that, error.message, Toast.LENGTH_SHORT).show()
247 | }
248 | return true
249 | }
250 |
251 | override fun onQueryTextChange(p0: String?): Boolean {
252 | return true
253 | }
254 | })
255 | }
256 |
257 | override fun onCreateOptionsMenu(menu: Menu): Boolean {
258 | lg("onCreateOptionsMenu")
259 | val inflater: MenuInflater = menuInflater
260 | inflater.inflate(R.menu.barbuttonitems, menu)
261 | mMenu = menu
262 | setupSearch(menu)
263 | if (!PlacePickerState.globalOptions.enableUserLocation || !isLocationPermissionGranted()) {
264 | val findMe = menu.findItem(R.id.findMe)
265 | findMe.isVisible = false
266 | }
267 | return true
268 | }
269 |
270 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
271 | when (item.itemId) {
272 | R.id.findMe -> {
273 | if (ActivityCompat.checkSelfPermission(
274 | this,
275 | Manifest.permission.ACCESS_FINE_LOCATION
276 | ) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(
277 | this,
278 | Manifest.permission.ACCESS_COARSE_LOCATION
279 | ) != PackageManager.PERMISSION_GRANTED
280 | ) {
281 | // TODO: Consider calling
282 | // ActivityCompat#requestPermissions
283 | // here to request the missing permissions, and then overriding
284 | // public void onRequestPermissionsResult(int requestCode, String[] permissions,
285 | // int[] grantResults)
286 | // to handle the case where the user grants the permission. See the documentation
287 | // for ActivityCompat#requestPermissions for more details.
288 | return false
289 | }
290 | getLocationProvider().lastLocation.addOnSuccessListener { task ->
291 | if (task == null) {
292 | Toast.makeText(this, "Unable to get location", Toast.LENGTH_SHORT).show()
293 | } else {
294 | mMap.animateCamera(
295 | CameraUpdateFactory.newLatLng(
296 | LatLng(
297 | task.latitude,
298 | task.longitude
299 | )
300 | )
301 | )
302 | }
303 | }
304 | }
305 |
306 | R.id.action_done, R.id.action_close -> {
307 | PlacePickerState.globalResult.coordinate = PlacePickerCoordinate().apply {
308 | latitude = mMap.cameraPosition.target.latitude
309 | longitude = mMap.cameraPosition.target.longitude
310 | }
311 | if (lastAddress != null && PlacePickerState.globalOptions.enableGeocoding) {
312 | PlacePickerState.globalResult.address = PlacePickerAddress()
313 | val add = lastAddress
314 | PlacePickerState.globalResult.address?.apply {
315 | name = add?.featureName ?: ""
316 | streetName = add?.thoroughfare ?: ""
317 | city = add?.locality ?: ""
318 | state = add?.adminArea ?: ""
319 | zipCode = add?.postalCode ?: ""
320 | country = add?.countryName ?: ""
321 | }
322 | }
323 | if (PlacePickerState.globalOptions.enableRangeSelection) {
324 | appendRadiusData()
325 | }
326 | if (item.itemId == R.id.action_done) {
327 | PlacePickerState.globalResult.didCancel = false
328 | PlacePickerState.globalPromise?.resolve(PlacePickerState.globalResult)
329 | } else {
330 | if (PlacePickerState.globalOptions.rejectOnCancel) {
331 | PlacePickerState.globalPromise?.reject(
332 | "cancel",
333 | "User cancel the operation and `rejectOnCancel` is enabled",
334 | null
335 | )
336 | }
337 | }
338 |
339 | finish()
340 | }
341 |
342 | }
343 | return true
344 | }
345 |
346 | // Radius helpers
347 | private fun setupRadiusSelection() {
348 | setupRadiusHandle()
349 | updateCircle()
350 | updateRadiusHandlePosition()
351 | }
352 |
353 | private fun updateCircle() {
354 | radiusCircle?.remove()
355 | radiusCircle = mMap.addCircle(
356 | CircleOptions()
357 | .center(mMap.cameraPosition.target)
358 | .radius(currentRadius)
359 | .fillColor(parseFillColor())
360 | .strokeColor(parseStrokeColor())
361 | .strokeWidth(PlacePickerState.globalOptions.radiusStrokeWidth.toFloat())
362 | )
363 | }
364 |
365 | private fun setupRadiusHandle() {
366 | if (radiusHandle != null) return
367 | val handle = View(this)
368 | val size = 56
369 | handle.layoutParams = android.view.ViewGroup.LayoutParams(size, size)
370 | handle.background = pinView.background.mutate()
371 | handle.background.setTint(Color.parseColor(PlacePickerState.globalOptions.color))
372 | handle.setOnTouchListener { _, event ->
373 | when (event.action) {
374 | android.view.MotionEvent.ACTION_DOWN -> {
375 | isDragging = true
376 | true
377 | }
378 | android.view.MotionEvent.ACTION_MOVE -> {
379 | if (!isDragging) return@setOnTouchListener true
380 | val centerScreen = mMap.projection.toScreenLocation(mMap.cameraPosition.target)
381 | val dx = event.rawX - centerScreen.x
382 | val dy = event.rawY - centerScreen.y
383 | val distancePx = kotlin.math.sqrt(dx*dx + dy*dy)
384 | val metersPerPixel = metersPerPixelAtLatitude(mMap.cameraPosition.target.latitude)
385 | val newRadius = (distancePx * metersPerPixel).toDouble()
386 | .coerceIn(PlacePickerState.globalOptions.minRadius, PlacePickerState.globalOptions.maxRadius)
387 | currentRadius = newRadius
388 | updateRadiusHandlePosition()
389 | true
390 | }
391 | android.view.MotionEvent.ACTION_UP, android.view.MotionEvent.ACTION_CANCEL -> {
392 | isDragging = false
393 | updateCircle()
394 | true
395 | }
396 | else -> false
397 | }
398 | }
399 | addContentView(handle, handle.layoutParams)
400 | radiusHandle = handle
401 | }
402 |
403 | private fun updateRadiusHandlePosition() {
404 | val handle = radiusHandle ?: return
405 | val centerGeo = mMap.cameraPosition.target
406 | val eastGeo = offsetCoordinate(centerGeo.latitude, centerGeo.longitude, currentRadius, 0.0)
407 | val edgeScreen = mMap.projection.toScreenLocation(eastGeo)
408 | handle.x = edgeScreen.x - handle.width / 2f
409 | handle.y = edgeScreen.y - handle.height / 2f
410 | }
411 |
412 | private fun metersPerPixelAtLatitude(lat: Double): Float {
413 | val earthCircumference = 40075016.686
414 | val zoom = mMap.cameraPosition.zoom.toDouble()
415 | val scale = Math.pow(2.0, zoom)
416 | val metersPerPixel = (Math.cos(Math.toRadians(lat)) * earthCircumference) / (256.0 * scale)
417 | return metersPerPixel.toFloat()
418 | }
419 |
420 | private fun parseFillColor(): Int {
421 | val color = if (PlacePickerState.globalOptions.radiusColor.isNotEmpty()) PlacePickerState.globalOptions.radiusColor else PlacePickerState.globalOptions.color
422 | val base = Color.parseColor(color)
423 | return Color.argb(64, Color.red(base), Color.green(base), Color.blue(base))
424 | }
425 |
426 | private fun parseStrokeColor(): Int {
427 | val color = if (PlacePickerState.globalOptions.radiusStrokeColor.isNotEmpty()) PlacePickerState.globalOptions.radiusStrokeColor else PlacePickerState.globalOptions.color
428 | return Color.parseColor(color)
429 | }
430 |
431 | private fun appendRadiusData() {
432 | PlacePickerState.globalResult.radius = currentRadius
433 | val center = PlacePickerCoordinate().apply {
434 | latitude = mMap.cameraPosition.target.latitude
435 | longitude = mMap.cameraPosition.target.longitude
436 | }
437 | val ne = offsetCoordinate(center.latitude, center.longitude, currentRadius, currentRadius)
438 | val sw = offsetCoordinate(center.latitude, center.longitude, -currentRadius, -currentRadius)
439 | val bounds = BoundsCoordinates().apply {
440 | northeast = PlacePickerCoordinate().apply {
441 | latitude = ne.latitude
442 | longitude = ne.longitude
443 | }
444 | southwest = PlacePickerCoordinate().apply {
445 | latitude = sw.latitude
446 | longitude = sw.longitude
447 | }
448 | }
449 | PlacePickerState.globalResult.radiusCoordinates = RadiusCoordinates().apply {
450 | this.center = center
451 | this.bounds = bounds
452 | }
453 | }
454 |
455 | private fun offsetCoordinate(lat: Double, lon: Double, metersEast: Double, metersNorth: Double): LatLng {
456 | val earthRadius = 6378137.0
457 | val dLat = metersNorth / earthRadius
458 | val dLon = metersEast / (earthRadius * kotlin.math.cos(Math.toRadians(lat)))
459 | val newLat = lat + Math.toDegrees(dLat)
460 | val newLon = lon + Math.toDegrees(dLon)
461 | return LatLng(newLat, newLon)
462 | }
463 | }
464 |
--------------------------------------------------------------------------------
/example/ios/reactnativeplacepickerexample.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
11 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
12 | 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
13 | 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */; };
14 | 4AB3F63224CA4EDC80F0DFC8 /* noop-file.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF657DF806444C790770784 /* noop-file.swift */; };
15 | 96905EF65AED1B983A6B3ABC /* libPods-reactnativeplacepickerexample.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-reactnativeplacepickerexample.a */; };
16 | B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */; };
17 | BB2F792D24A3F905000567C9 /* Expo.plist in Resources */ = {isa = PBXBuildFile; fileRef = BB2F792C24A3F905000567C9 /* Expo.plist */; };
18 | ED72ED3896F1AF765CB1AD86 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 52D29DCE011DCAAAD765EC2A /* PrivacyInfo.xcprivacy */; };
19 | /* End PBXBuildFile section */
20 |
21 | /* Begin PBXFileReference section */
22 | 13B07F961A680F5B00A75B9A /* reactnativeplacepickerexample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = reactnativeplacepickerexample.app; sourceTree = BUILT_PRODUCTS_DIR; };
23 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = reactnativeplacepickerexample/AppDelegate.h; sourceTree = ""; };
24 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = reactnativeplacepickerexample/AppDelegate.mm; sourceTree = ""; };
25 | 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = reactnativeplacepickerexample/Images.xcassets; sourceTree = ""; };
26 | 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = reactnativeplacepickerexample/Info.plist; sourceTree = ""; };
27 | 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = reactnativeplacepickerexample/main.m; sourceTree = ""; };
28 | 52D29DCE011DCAAAD765EC2A /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = reactnativeplacepickerexample/PrivacyInfo.xcprivacy; sourceTree = ""; };
29 | 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-reactnativeplacepickerexample.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-reactnativeplacepickerexample.a"; sourceTree = BUILT_PRODUCTS_DIR; };
30 | 6C2E3173556A471DD304B334 /* Pods-reactnativeplacepickerexample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-reactnativeplacepickerexample.debug.xcconfig"; path = "Target Support Files/Pods-reactnativeplacepickerexample/Pods-reactnativeplacepickerexample.debug.xcconfig"; sourceTree = ""; };
31 | 6CF657DF806444C790770784 /* noop-file.swift */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.swift; name = "noop-file.swift"; path = "reactnativeplacepickerexample/noop-file.swift"; sourceTree = ""; };
32 | 7A4D352CD337FB3A3BF06240 /* Pods-reactnativeplacepickerexample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-reactnativeplacepickerexample.release.xcconfig"; path = "Target Support Files/Pods-reactnativeplacepickerexample/Pods-reactnativeplacepickerexample.release.xcconfig"; sourceTree = ""; };
33 | AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = reactnativeplacepickerexample/SplashScreen.storyboard; sourceTree = ""; };
34 | B95615CDE6CD45379B1AA57E /* reactnativeplacepickerexample-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "reactnativeplacepickerexample-Bridging-Header.h"; path = "reactnativeplacepickerexample/reactnativeplacepickerexample-Bridging-Header.h"; sourceTree = ""; };
35 | BB2F792C24A3F905000567C9 /* Expo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Expo.plist; sourceTree = ""; };
36 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
37 | FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.swift; name = ExpoModulesProvider.swift; path = "Pods/Target Support Files/Pods-reactnativeplacepickerexample/ExpoModulesProvider.swift"; sourceTree = ""; };
38 | /* End PBXFileReference section */
39 |
40 | /* Begin PBXFrameworksBuildPhase section */
41 | 13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
42 | isa = PBXFrameworksBuildPhase;
43 | buildActionMask = 2147483647;
44 | files = (
45 | 96905EF65AED1B983A6B3ABC /* libPods-reactnativeplacepickerexample.a in Frameworks */,
46 | );
47 | runOnlyForDeploymentPostprocessing = 0;
48 | };
49 | /* End PBXFrameworksBuildPhase section */
50 |
51 | /* Begin PBXGroup section */
52 | 13B07FAE1A68108700A75B9A /* reactnativeplacepickerexample */ = {
53 | isa = PBXGroup;
54 | children = (
55 | BB2F792B24A3F905000567C9 /* Supporting */,
56 | 13B07FAF1A68108700A75B9A /* AppDelegate.h */,
57 | 13B07FB01A68108700A75B9A /* AppDelegate.mm */,
58 | 13B07FB51A68108700A75B9A /* Images.xcassets */,
59 | 13B07FB61A68108700A75B9A /* Info.plist */,
60 | 13B07FB71A68108700A75B9A /* main.m */,
61 | AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */,
62 | 6CF657DF806444C790770784 /* noop-file.swift */,
63 | B95615CDE6CD45379B1AA57E /* reactnativeplacepickerexample-Bridging-Header.h */,
64 | 52D29DCE011DCAAAD765EC2A /* PrivacyInfo.xcprivacy */,
65 | );
66 | name = reactnativeplacepickerexample;
67 | sourceTree = "";
68 | };
69 | 2D16E6871FA4F8E400B85C8A /* Frameworks */ = {
70 | isa = PBXGroup;
71 | children = (
72 | ED297162215061F000B7C4FE /* JavaScriptCore.framework */,
73 | 58EEBF8E8E6FB1BC6CAF49B5 /* libPods-reactnativeplacepickerexample.a */,
74 | );
75 | name = Frameworks;
76 | sourceTree = "";
77 | };
78 | 832341AE1AAA6A7D00B99B32 /* Libraries */ = {
79 | isa = PBXGroup;
80 | children = (
81 | );
82 | name = Libraries;
83 | sourceTree = "";
84 | };
85 | 83CBB9F61A601CBA00E9B192 = {
86 | isa = PBXGroup;
87 | children = (
88 | 13B07FAE1A68108700A75B9A /* reactnativeplacepickerexample */,
89 | 832341AE1AAA6A7D00B99B32 /* Libraries */,
90 | 83CBBA001A601CBA00E9B192 /* Products */,
91 | 2D16E6871FA4F8E400B85C8A /* Frameworks */,
92 | D65327D7A22EEC0BE12398D9 /* Pods */,
93 | D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */,
94 | );
95 | indentWidth = 2;
96 | sourceTree = "";
97 | tabWidth = 2;
98 | usesTabs = 0;
99 | };
100 | 83CBBA001A601CBA00E9B192 /* Products */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 13B07F961A680F5B00A75B9A /* reactnativeplacepickerexample.app */,
104 | );
105 | name = Products;
106 | sourceTree = "";
107 | };
108 | 92DBD88DE9BF7D494EA9DA96 /* reactnativeplacepickerexample */ = {
109 | isa = PBXGroup;
110 | children = (
111 | FAC715A2D49A985799AEE119 /* ExpoModulesProvider.swift */,
112 | );
113 | name = reactnativeplacepickerexample;
114 | sourceTree = "";
115 | };
116 | BB2F792B24A3F905000567C9 /* Supporting */ = {
117 | isa = PBXGroup;
118 | children = (
119 | BB2F792C24A3F905000567C9 /* Expo.plist */,
120 | );
121 | name = Supporting;
122 | path = reactnativeplacepickerexample/Supporting;
123 | sourceTree = "";
124 | };
125 | D65327D7A22EEC0BE12398D9 /* Pods */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 6C2E3173556A471DD304B334 /* Pods-reactnativeplacepickerexample.debug.xcconfig */,
129 | 7A4D352CD337FB3A3BF06240 /* Pods-reactnativeplacepickerexample.release.xcconfig */,
130 | );
131 | path = Pods;
132 | sourceTree = "";
133 | };
134 | D7E4C46ADA2E9064B798F356 /* ExpoModulesProviders */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 92DBD88DE9BF7D494EA9DA96 /* reactnativeplacepickerexample */,
138 | );
139 | name = ExpoModulesProviders;
140 | sourceTree = "";
141 | };
142 | /* End PBXGroup section */
143 |
144 | /* Begin PBXNativeTarget section */
145 | 13B07F861A680F5B00A75B9A /* reactnativeplacepickerexample */ = {
146 | isa = PBXNativeTarget;
147 | buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "reactnativeplacepickerexample" */;
148 | buildPhases = (
149 | 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */,
150 | CFC1AED40AC859987CBE70E1 /* [Expo] Configure project */,
151 | 13B07F871A680F5B00A75B9A /* Sources */,
152 | 13B07F8C1A680F5B00A75B9A /* Frameworks */,
153 | 13B07F8E1A680F5B00A75B9A /* Resources */,
154 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
155 | 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */,
156 | 5A61D349B34BFAE7258FA9E7 /* [CP] Embed Pods Frameworks */,
157 | );
158 | buildRules = (
159 | );
160 | dependencies = (
161 | );
162 | name = reactnativeplacepickerexample;
163 | productName = reactnativeplacepickerexample;
164 | productReference = 13B07F961A680F5B00A75B9A /* reactnativeplacepickerexample.app */;
165 | productType = "com.apple.product-type.application";
166 | };
167 | /* End PBXNativeTarget section */
168 |
169 | /* Begin PBXProject section */
170 | 83CBB9F71A601CBA00E9B192 /* Project object */ = {
171 | isa = PBXProject;
172 | attributes = {
173 | LastUpgradeCheck = 1130;
174 | TargetAttributes = {
175 | 13B07F861A680F5B00A75B9A = {
176 | LastSwiftMigration = 1250;
177 | };
178 | };
179 | };
180 | buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "reactnativeplacepickerexample" */;
181 | compatibilityVersion = "Xcode 3.2";
182 | developmentRegion = en;
183 | hasScannedForEncodings = 0;
184 | knownRegions = (
185 | en,
186 | Base,
187 | );
188 | mainGroup = 83CBB9F61A601CBA00E9B192;
189 | productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
190 | projectDirPath = "";
191 | projectRoot = "";
192 | targets = (
193 | 13B07F861A680F5B00A75B9A /* reactnativeplacepickerexample */,
194 | );
195 | };
196 | /* End PBXProject section */
197 |
198 | /* Begin PBXResourcesBuildPhase section */
199 | 13B07F8E1A680F5B00A75B9A /* Resources */ = {
200 | isa = PBXResourcesBuildPhase;
201 | buildActionMask = 2147483647;
202 | files = (
203 | BB2F792D24A3F905000567C9 /* Expo.plist in Resources */,
204 | 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
205 | 3E461D99554A48A4959DE609 /* SplashScreen.storyboard in Resources */,
206 | ED72ED3896F1AF765CB1AD86 /* PrivacyInfo.xcprivacy in Resources */,
207 | );
208 | runOnlyForDeploymentPostprocessing = 0;
209 | };
210 | /* End PBXResourcesBuildPhase section */
211 |
212 | /* Begin PBXShellScriptBuildPhase section */
213 | 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = {
214 | isa = PBXShellScriptBuildPhase;
215 | alwaysOutOfDate = 1;
216 | buildActionMask = 2147483647;
217 | files = (
218 | );
219 | inputPaths = (
220 | );
221 | name = "Bundle React Native code and images";
222 | outputPaths = (
223 | );
224 | runOnlyForDeploymentPostprocessing = 0;
225 | shellPath = /bin/sh;
226 | shellScript = "if [[ -f \"$PODS_ROOT/../.xcode.env\" ]]; then\n source \"$PODS_ROOT/../.xcode.env\"\nfi\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n# The project root by default is one level up from the ios directory\nexport PROJECT_ROOT=\"$PROJECT_DIR\"/..\n\nif [[ \"$CONFIGURATION\" = *Debug* ]]; then\n export SKIP_BUNDLING=1\nfi\nif [[ -z \"$ENTRY_FILE\" ]]; then\n # Set the entry JS file using the bundler's entry resolution.\n export ENTRY_FILE=\"$(\"$NODE_BINARY\" -e \"require('expo/scripts/resolveAppEntry')\" \"$PROJECT_ROOT\" ios absolute | tail -n 1)\"\nfi\n\nif [[ -z \"$CLI_PATH\" ]]; then\n # Use Expo CLI\n export CLI_PATH=\"$(\"$NODE_BINARY\" --print \"require.resolve('@expo/cli', { paths: [require.resolve('expo/package.json')] })\")\"\nfi\nif [[ -z \"$BUNDLE_COMMAND\" ]]; then\n # Default Expo CLI command for bundling\n export BUNDLE_COMMAND=\"export:embed\"\nfi\n\n# Source .xcode.env.updates if it exists to allow\n# SKIP_BUNDLING to be unset if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.updates\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.updates\"\nfi\n# Source local changes to allow overrides\n# if needed\nif [[ -f \"$PODS_ROOT/../.xcode.env.local\" ]]; then\n source \"$PODS_ROOT/../.xcode.env.local\"\nfi\n\n`\"$NODE_BINARY\" --print \"require('path').dirname(require.resolve('react-native/package.json')) + '/scripts/react-native-xcode.sh'\"`\n\n";
227 | };
228 | 08A4A3CD28434E44B6B9DE2E /* [CP] Check Pods Manifest.lock */ = {
229 | isa = PBXShellScriptBuildPhase;
230 | buildActionMask = 2147483647;
231 | files = (
232 | );
233 | inputFileListPaths = (
234 | );
235 | inputPaths = (
236 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
237 | "${PODS_ROOT}/Manifest.lock",
238 | );
239 | name = "[CP] Check Pods Manifest.lock";
240 | outputFileListPaths = (
241 | );
242 | outputPaths = (
243 | "$(DERIVED_FILE_DIR)/Pods-reactnativeplacepickerexample-checkManifestLockResult.txt",
244 | );
245 | runOnlyForDeploymentPostprocessing = 0;
246 | shellPath = /bin/sh;
247 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
248 | showEnvVarsInLog = 0;
249 | };
250 | 5A61D349B34BFAE7258FA9E7 /* [CP] Embed Pods Frameworks */ = {
251 | isa = PBXShellScriptBuildPhase;
252 | buildActionMask = 2147483647;
253 | files = (
254 | );
255 | inputPaths = (
256 | "${PODS_ROOT}/Target Support Files/Pods-reactnativeplacepickerexample/Pods-reactnativeplacepickerexample-frameworks.sh",
257 | "${PODS_XCFRAMEWORKS_BUILD_DIR}/hermes-engine/Pre-built/hermes.framework/hermes",
258 | );
259 | name = "[CP] Embed Pods Frameworks";
260 | outputPaths = (
261 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/hermes.framework",
262 | );
263 | runOnlyForDeploymentPostprocessing = 0;
264 | shellPath = /bin/sh;
265 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-reactnativeplacepickerexample/Pods-reactnativeplacepickerexample-frameworks.sh\"\n";
266 | showEnvVarsInLog = 0;
267 | };
268 | 800E24972A6A228C8D4807E9 /* [CP] Copy Pods Resources */ = {
269 | isa = PBXShellScriptBuildPhase;
270 | buildActionMask = 2147483647;
271 | files = (
272 | );
273 | inputPaths = (
274 | "${PODS_ROOT}/Target Support Files/Pods-reactnativeplacepickerexample/Pods-reactnativeplacepickerexample-resources.sh",
275 | "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle",
276 | "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle",
277 | "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle",
278 | "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle",
279 | );
280 | name = "[CP] Copy Pods Resources";
281 | outputPaths = (
282 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle",
283 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle",
284 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle",
285 | "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle",
286 | );
287 | runOnlyForDeploymentPostprocessing = 0;
288 | shellPath = /bin/sh;
289 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-reactnativeplacepickerexample/Pods-reactnativeplacepickerexample-resources.sh\"\n";
290 | showEnvVarsInLog = 0;
291 | };
292 | CFC1AED40AC859987CBE70E1 /* [Expo] Configure project */ = {
293 | isa = PBXShellScriptBuildPhase;
294 | alwaysOutOfDate = 1;
295 | buildActionMask = 2147483647;
296 | files = (
297 | );
298 | inputFileListPaths = (
299 | );
300 | inputPaths = (
301 | );
302 | name = "[Expo] Configure project";
303 | outputFileListPaths = (
304 | );
305 | outputPaths = (
306 | );
307 | runOnlyForDeploymentPostprocessing = 0;
308 | shellPath = /bin/sh;
309 | shellScript = "# This script configures Expo modules and generates the modules provider file.\nbash -l -c \"./Pods/Target\\ Support\\ Files/Pods-reactnativeplacepickerexample/expo-configure-project.sh\"\n";
310 | };
311 | /* End PBXShellScriptBuildPhase section */
312 |
313 | /* Begin PBXSourcesBuildPhase section */
314 | 13B07F871A680F5B00A75B9A /* Sources */ = {
315 | isa = PBXSourcesBuildPhase;
316 | buildActionMask = 2147483647;
317 | files = (
318 | 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */,
319 | 13B07FC11A68108700A75B9A /* main.m in Sources */,
320 | B18059E884C0ABDD17F3DC3D /* ExpoModulesProvider.swift in Sources */,
321 | 4AB3F63224CA4EDC80F0DFC8 /* noop-file.swift in Sources */,
322 | );
323 | runOnlyForDeploymentPostprocessing = 0;
324 | };
325 | /* End PBXSourcesBuildPhase section */
326 |
327 | /* Begin XCBuildConfiguration section */
328 | 13B07F941A680F5B00A75B9A /* Debug */ = {
329 | isa = XCBuildConfiguration;
330 | baseConfigurationReference = 6C2E3173556A471DD304B334 /* Pods-reactnativeplacepickerexample.debug.xcconfig */;
331 | buildSettings = {
332 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
333 | CLANG_ENABLE_MODULES = YES;
334 | CODE_SIGN_ENTITLEMENTS = reactnativeplacepickerexample/reactnativeplacepickerexample.entitlements;
335 | CURRENT_PROJECT_VERSION = 1;
336 | ENABLE_BITCODE = NO;
337 | GCC_PREPROCESSOR_DEFINITIONS = (
338 | "$(inherited)",
339 | "FB_SONARKIT_ENABLED=1",
340 | );
341 | INFOPLIST_FILE = reactnativeplacepickerexample/Info.plist;
342 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
343 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
344 | MARKETING_VERSION = 1.0;
345 | OTHER_LDFLAGS = (
346 | "$(inherited)",
347 | "-ObjC",
348 | "-lc++",
349 | );
350 | OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_DEBUG";
351 | PRODUCT_BUNDLE_IDENTIFIER = expo.modules.placepicker.example;
352 | PRODUCT_NAME = "reactnativeplacepickerexample";
353 | SWIFT_OBJC_BRIDGING_HEADER = "reactnativeplacepickerexample/reactnativeplacepickerexample-Bridging-Header.h";
354 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
355 | SWIFT_VERSION = 5.0;
356 | TARGETED_DEVICE_FAMILY = "1,2";
357 | VERSIONING_SYSTEM = "apple-generic";
358 | };
359 | name = Debug;
360 | };
361 | 13B07F951A680F5B00A75B9A /* Release */ = {
362 | isa = XCBuildConfiguration;
363 | baseConfigurationReference = 7A4D352CD337FB3A3BF06240 /* Pods-reactnativeplacepickerexample.release.xcconfig */;
364 | buildSettings = {
365 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
366 | CLANG_ENABLE_MODULES = YES;
367 | CODE_SIGN_ENTITLEMENTS = reactnativeplacepickerexample/reactnativeplacepickerexample.entitlements;
368 | CURRENT_PROJECT_VERSION = 1;
369 | INFOPLIST_FILE = reactnativeplacepickerexample/Info.plist;
370 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
371 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
372 | MARKETING_VERSION = 1.0;
373 | OTHER_LDFLAGS = (
374 | "$(inherited)",
375 | "-ObjC",
376 | "-lc++",
377 | );
378 | OTHER_SWIFT_FLAGS = "$(inherited) -D EXPO_CONFIGURATION_RELEASE";
379 | PRODUCT_BUNDLE_IDENTIFIER = expo.modules.placepicker.example;
380 | PRODUCT_NAME = "reactnativeplacepickerexample";
381 | SWIFT_OBJC_BRIDGING_HEADER = "reactnativeplacepickerexample/reactnativeplacepickerexample-Bridging-Header.h";
382 | SWIFT_VERSION = 5.0;
383 | TARGETED_DEVICE_FAMILY = "1,2";
384 | VERSIONING_SYSTEM = "apple-generic";
385 | };
386 | name = Release;
387 | };
388 | 83CBBA201A601CBA00E9B192 /* Debug */ = {
389 | isa = XCBuildConfiguration;
390 | buildSettings = {
391 | ALWAYS_SEARCH_USER_PATHS = NO;
392 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
393 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
394 | CLANG_CXX_LIBRARY = "libc++";
395 | CLANG_ENABLE_MODULES = YES;
396 | CLANG_ENABLE_OBJC_ARC = YES;
397 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
398 | CLANG_WARN_BOOL_CONVERSION = YES;
399 | CLANG_WARN_COMMA = YES;
400 | CLANG_WARN_CONSTANT_CONVERSION = YES;
401 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
402 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
403 | CLANG_WARN_EMPTY_BODY = YES;
404 | CLANG_WARN_ENUM_CONVERSION = YES;
405 | CLANG_WARN_INFINITE_RECURSION = YES;
406 | CLANG_WARN_INT_CONVERSION = YES;
407 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
408 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
409 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
410 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
411 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
412 | CLANG_WARN_STRICT_PROTOTYPES = YES;
413 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
414 | CLANG_WARN_UNREACHABLE_CODE = YES;
415 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
416 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
417 | COPY_PHASE_STRIP = NO;
418 | ENABLE_STRICT_OBJC_MSGSEND = YES;
419 | ENABLE_TESTABILITY = YES;
420 | GCC_C_LANGUAGE_STANDARD = gnu99;
421 | GCC_DYNAMIC_NO_PIC = NO;
422 | GCC_NO_COMMON_BLOCKS = YES;
423 | GCC_OPTIMIZATION_LEVEL = 0;
424 | GCC_PREPROCESSOR_DEFINITIONS = (
425 | "DEBUG=1",
426 | "$(inherited)",
427 | );
428 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
429 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
430 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
431 | GCC_WARN_UNDECLARED_SELECTOR = YES;
432 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
433 | GCC_WARN_UNUSED_FUNCTION = YES;
434 | GCC_WARN_UNUSED_VARIABLE = YES;
435 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
436 | LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
437 | LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
438 | MTL_ENABLE_DEBUG_INFO = YES;
439 | ONLY_ACTIVE_ARCH = YES;
440 | OTHER_LDFLAGS = (
441 | "$(inherited)",
442 | " ",
443 | );
444 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
445 | SDKROOT = iphoneos;
446 | USE_HERMES = true;
447 | };
448 | name = Debug;
449 | };
450 | 83CBBA211A601CBA00E9B192 /* Release */ = {
451 | isa = XCBuildConfiguration;
452 | buildSettings = {
453 | ALWAYS_SEARCH_USER_PATHS = NO;
454 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
455 | CLANG_CXX_LANGUAGE_STANDARD = "c++20";
456 | CLANG_CXX_LIBRARY = "libc++";
457 | CLANG_ENABLE_MODULES = YES;
458 | CLANG_ENABLE_OBJC_ARC = YES;
459 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
460 | CLANG_WARN_BOOL_CONVERSION = YES;
461 | CLANG_WARN_COMMA = YES;
462 | CLANG_WARN_CONSTANT_CONVERSION = YES;
463 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
464 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
465 | CLANG_WARN_EMPTY_BODY = YES;
466 | CLANG_WARN_ENUM_CONVERSION = YES;
467 | CLANG_WARN_INFINITE_RECURSION = YES;
468 | CLANG_WARN_INT_CONVERSION = YES;
469 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
470 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
471 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
472 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
473 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
474 | CLANG_WARN_STRICT_PROTOTYPES = YES;
475 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
476 | CLANG_WARN_UNREACHABLE_CODE = YES;
477 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
478 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
479 | COPY_PHASE_STRIP = YES;
480 | ENABLE_NS_ASSERTIONS = NO;
481 | ENABLE_STRICT_OBJC_MSGSEND = YES;
482 | GCC_C_LANGUAGE_STANDARD = gnu99;
483 | GCC_NO_COMMON_BLOCKS = YES;
484 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
485 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
486 | GCC_WARN_UNDECLARED_SELECTOR = YES;
487 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
488 | GCC_WARN_UNUSED_FUNCTION = YES;
489 | GCC_WARN_UNUSED_VARIABLE = YES;
490 | IPHONEOS_DEPLOYMENT_TARGET = 13.4;
491 | LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)";
492 | LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\"";
493 | MTL_ENABLE_DEBUG_INFO = NO;
494 | OTHER_LDFLAGS = (
495 | "$(inherited)",
496 | " ",
497 | );
498 | REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
499 | SDKROOT = iphoneos;
500 | USE_HERMES = true;
501 | VALIDATE_PRODUCT = YES;
502 | };
503 | name = Release;
504 | };
505 | /* End XCBuildConfiguration section */
506 |
507 | /* Begin XCConfigurationList section */
508 | 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "reactnativeplacepickerexample" */ = {
509 | isa = XCConfigurationList;
510 | buildConfigurations = (
511 | 13B07F941A680F5B00A75B9A /* Debug */,
512 | 13B07F951A680F5B00A75B9A /* Release */,
513 | );
514 | defaultConfigurationIsVisible = 0;
515 | defaultConfigurationName = Release;
516 | };
517 | 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "reactnativeplacepickerexample" */ = {
518 | isa = XCConfigurationList;
519 | buildConfigurations = (
520 | 83CBBA201A601CBA00E9B192 /* Debug */,
521 | 83CBBA211A601CBA00E9B192 /* Release */,
522 | );
523 | defaultConfigurationIsVisible = 0;
524 | defaultConfigurationName = Release;
525 | };
526 | /* End XCConfigurationList section */
527 | };
528 | rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */;
529 | }
530 |
--------------------------------------------------------------------------------