@smarthivelabs-devs/geocore-expo
v1.6.5
Published
Geocore Expo/React Native SDK — mapping and geocoding for mobile
Downloads
1,159
Readme
@smarthivelabs-devs/geocore-expo
Expo / React Native SDK for Geocore. All 14 geo-operations, weather, and the SmartHive Pricing Engine — with hooks that match the React package's API.
Installation
npx expo install @smarthivelabs-devs/geocore-expo @smarthivelabs-devs/geocore-coreMap rendering (optional)
Pick one — or both. Install the one your project already uses:
# Option A — react-native-maps (classic, widely used)
npx expo install react-native-maps
# Option B — expo-maps (newer Expo SDK)
npx expo install expo-mapsNeither is required if you only need the data hooks (geocoding, weather, pricing, etc.) without an interactive map.
Setup
import { Geocore } from '@smarthivelabs-devs/geocore-core';
// Backend mode (recommended — API key only)
const geocore = new Geocore({
apiKey: 'your-geocore-api-key',
});
// Direct mode (provider keys in the app)
const geocore = new Geocore({
apiKeys: {
mapbox: { secretToken: 'sk.eyJ1...' },
google: 'AIza...',
},
});Reverse Geocoding — useReverseGeocode
The simplest way to turn coordinates into a display address. Supports auto-fetching the device location or accepting explicit coordinates (e.g. from a map pin).
import { useReverseGeocode, getDisplayAddress } from '@smarthivelabs-devs/geocore-expo';
// Auto-fetch device location on mount
function MyLocationLabel() {
const { address, components, loading } = useReverseGeocode({
geocore,
autoFetch: true,
});
if (loading) return <ActivityIndicator />;
return <Text>{address}</Text>;
// → "Adum, Kumasi, Ashanti Region, Ghana"
}
// Geocode a map pin
function MapPinAddress({ coords }) {
const { address, components, loading, refresh } = useReverseGeocode({
geocore,
coords, // re-runs whenever coords changes
});
return (
<View>
<Text>{address}</Text>
<Text>{components.city}, {components.state}</Text>
</View>
);
}Address Search — useAddressSearch
Debounced address search with autocomplete suggestions. Handles typing, shows results, and locks in coordinates once confirmed. Drop this into any address input field.
import { useAddressSearch } from '@smarthivelabs-devs/geocore-expo';
import * as Location from 'expo-location';
function AddressInput({ label, onConfirm }) {
const [proximity, setProximity] = useState(null);
useEffect(() => {
Location.getCurrentPositionAsync().then(loc => {
setProximity({ latitude: loc.coords.latitude, longitude: loc.coords.longitude });
});
}, []);
const search = useAddressSearch({ geocore, proximity, limit: 5 });
useEffect(() => {
if (search.confirmed && search.coords) {
onConfirm(search.value, search.coords, search.components);
}
}, [search.confirmed]);
return (
<View>
<TextInput
value={search.value}
onChangeText={search.onChange}
placeholder={`Enter ${label} address`}
/>
{search.loading && <ActivityIndicator />}
{search.suggestions.map(s => (
<TouchableOpacity key={s.address} onPress={() => search.onSelect(s)}>
<Text>{s.label}</Text>
<Text style={{ color: 'gray' }}>{s.sublabel}</Text>
</TouchableOpacity>
))}
</View>
);
}
// Confirm from a map picker instead of typing:
search.confirm('Adum, Kumasi', { latitude: 6.6885, longitude: -1.6244 });Geocoding (low-level)
import { useGeocoding } from '@smarthivelabs-devs/geocore-expo';
function Screen() {
const { geocodingResult, loading, geocode } = useGeocoding(geocore);
return (
<Button
title="Find Eiffel Tower"
onPress={() => geocode('Eiffel Tower, Paris')}
/>
);
}Places & Search
import { usePlaces, useNearbySearch, useAutocomplete } from '@smarthivelabs-devs/geocore-expo';
// Text search near a coordinate
const { result, loading, searchPlaces } = usePlaces(geocore);
await searchPlaces('pharmacy', { lat: 6.5244, lng: 3.3792 }, { radius: 1000 });
// result.data — [{ name, coordinates, address, rating }]
// Category-based nearby search
const { result, searchNearby } = useNearbySearch(geocore);
await searchNearby('hospital', { lat: 6.5244, lng: 3.3792 });
// Autocomplete as user types
const { predictions, loading, getSuggestions } = useAutocomplete(geocore);
await getSuggestions('Ikeja Ala');
// predictions — [{ mainText: 'Ikeja', secondaryText: 'Lagos, Nigeria', placeId }]Directions & Routing
import { useDirections } from '@smarthivelabs-devs/geocore-expo';
const { result, getDirections } = useDirections(geocore);
await getDirections(
{ lat: 6.5244, lng: 3.3792 },
{ lat: 9.0765, lng: 7.3986 },
{ mode: 'driving' }
);
// result.data.distance, result.data.duration, result.data.stepsSpatial Analysis
import { useElevation, useTimezone, useIsochrone } from '@smarthivelabs-devs/geocore-expo';
const { result, getElevation } = useElevation(geocore);
const { result, getTimezone } = useTimezone(geocore);
const { result, getIsochrone } = useIsochrone(geocore);
await getTimezone(51.5074, -0.1278);
console.log(result?.data.timeZoneId); // 'Europe/London'Utility Hooks
import { useTraffic, useDistanceMatrix, useSnapToRoad, useRouteOptimize, useStaticMap } from '@smarthivelabs-devs/geocore-expo';
// Traffic conditions at a location
const { result, getTraffic } = useTraffic(geocore);
await getTraffic(51.5074, -0.1278);
// result.data.condition: 'free' | 'slow' | 'heavy' | 'blocked'
// Origin → destination time/distance grid
const { result, getMatrix } = useDistanceMatrix(geocore);
await getMatrix(
['Lagos, Nigeria', 'Abuja, Nigeria'],
['Port Harcourt, Nigeria'],
{ mode: 'driving' }
);
// Snap recorded GPS points to the road network
const { result, snap } = useSnapToRoad(geocore);
await snap([
{ lat: 6.524, lng: 3.379 },
{ lat: 6.530, lng: 3.385 },
]);
// Optimise stop order for a multi-stop trip
const { result, optimise } = useRouteOptimize(geocore);
await optimise(['Lagos, Nigeria', 'Ibadan, Nigeria', 'Abeokuta, Nigeria']);
// Static map image URL
const { result, getMap } = useStaticMap(geocore);
await getMap(6.5244, 3.3792, { zoom: 14, width: 600, height: 400 });
// result.data.url — render in <Image source={{ uri: result.data.url }} />Weather
import { useWeather } from '@smarthivelabs-devs/geocore-expo';
import * as Location from 'expo-location';
function WeatherScreen() {
const { currentResult, forecastResult, loading, getWeather, getForecast } = useWeather(geocore);
useEffect(() => {
(async () => {
const loc = await Location.getCurrentPositionAsync();
const { latitude: lat, longitude: lng } = loc.coords;
getWeather(lat, lng);
getForecast(lat, lng, 7);
})();
}, []);
return (
<View>
<Text>{currentResult?.data.temperature}°C</Text>
<Text>{currentResult?.data.description}</Text>
{forecastResult?.data.days.map(day => (
<Text key={day.date}>
{day.date}: {day.tempMin}–{day.tempMax}°C
</Text>
))}
</View>
);
}SmartHive Pricing Engine
Show localised prices based on the user's GPS location. If the user manually selects their country, call localizePriceByCountry to recalculate instantly — no re-detection needed.
import { usePricing } from '@smarthivelabs-devs/geocore-expo';
import * as Location from 'expo-location';
function PricingScreen() {
const { result, loading, localizePrice, localizePriceByCountry } = usePricing(geocore);
// Auto-detect on mount
useEffect(() => {
(async () => {
const loc = await Location.getCurrentPositionAsync();
localizePrice(100, 'GHS', loc.coords.latitude, loc.coords.longitude);
})();
}, []);
return (
<View>
{loading && <ActivityIndicator />}
{result && (
<>
<Text style={styles.price}>
{result.data.symbol}{result.data.amount} {result.data.currency}/month
</Text>
<Text style={styles.country}>{result.data.countryName}</Text>
</>
)}
{/* Country switcher */}
<Picker onValueChange={(code) => localizePriceByCountry(100, 'GHS', code)}>
<Picker.Item label="Nigeria" value="NG" />
<Picker.Item label="Ghana" value="GH" />
<Picker.Item label="United States" value="US" />
<Picker.Item label="United Kingdom" value="GB" />
<Picker.Item label="Kenya" value="KE" />
</Picker>
</View>
);
}FX lookup
const { rateResult, getRate } = usePricing(geocore);
await getRate('USD', 'NGN');
console.log(rateResult?.data.rate); // e.g. 1580Interactive Map (render prop pattern)
Pass your preferred map library as a render prop. This avoids bundling native modules.
With react-native-maps
import { GeocodingMapScreen } from '@smarthivelabs-devs/geocore-expo';
import { ReactNativeMapsRenderer } from '@smarthivelabs-devs/geocore-expo/renderers';
<GeocodingMapScreen
geocore={geocore}
renderMap={(props) => <ReactNativeMapsRenderer {...props} />}
/>With expo-maps
import { ExpoMapsRenderer } from '@smarthivelabs-devs/geocore-expo/renderers';
<GeocodingMapScreen
geocore={geocore}
renderMap={(props) => <ExpoMapsRenderer {...props} />}
/>Bring your own map
<GeocodingMapScreen
geocore={geocore}
renderMap={({ region, markers, style }) => (
<MyCustomMap region={region} pins={markers} style={style} />
)}
/>All Hooks
| Hook | Returns / Actions | Notes |
|------|-------------------|-------|
| useReverseGeocode | address, components, coords, loading, error, refresh | New in 1.6 — auto-fetches device location or accepts map pin coords |
| useAddressSearch | value, suggestions, confirmed, coords, onChange, onSelect, confirm, clear | New in 1.6 — debounced search with autocomplete |
| useGeocoding | geocode, reverse | Low-level geocode/reverse |
| useDirections | getDirections | |
| usePlaces | searchPlaces | |
| useNearbySearch | searchNearby | |
| useAutocomplete | getSuggestions | |
| useElevation | getElevation | |
| useIsochrone | getIsochrone | |
| useTimezone | getTimezone | |
| useTraffic | getTraffic | |
| useDistanceMatrix | getMatrix | |
| useSnapToRoad | snap | |
| useRouteOptimize | optimise | |
| useStaticMap | getMap | |
| useWeather | getWeather, getForecast | |
| usePricing | localizePrice, localizePriceByCountry, getRate, getRates | |
Every hook returns { loading, error, reset } alongside its data and action callbacks.
Changelog
1.6.0
- New
useReverseGeocodehook — reverse-geocode with auto device-location support. Returns normalizedaddressstring and structuredcomponents. - New
useAddressSearchhook — debounced address input with autocomplete suggestions and coordinate confirmation. - Normalized address components —
componentsfrom all hooks now use consistent keys (city,state,area,street, etc.) regardless of which backend provider answered. Seegeocore-corechangelog for details. getDisplayAddressutility — re-exported from this package for convenience.
Error Handling
Every hook exposes error: Error | null. Errors are caught internally — they never crash your app. Use reset() to clear before a new action.
const { result, loading, error, geocode, reset } = useGeocoding(geocore);
const handleSearch = async () => {
reset();
await geocode('1 Infinite Loop, Cupertino');
};
return (
<View>
{loading && <ActivityIndicator />}
{error && <Text style={{ color: 'red' }}>{error.message}</Text>}
{result && <Text>{result.data.formattedAddress}</Text>}
<Button title="Search" onPress={handleSearch} />
</View>
);Platform Notes
- iOS — expo-maps uses Apple Maps by default
- Android — expo-maps uses Google Maps (requires Google Maps SDK key)
- react-native-maps — works on both platforms, uses Google Maps on Android and Apple Maps on iOS
- Weather and Pricing hooks are platform-agnostic (pure HTTP)
License
MIT — SmartHive Labs
