@natify/geolocation-rn
v1.0.2
Published
Geolocation adapter for Natify Framework using `@react-native-community/geolocation`.
Readme
@natify/geolocation-rn
Geolocation adapter for Natify Framework using @react-native-community/geolocation.
Installation
pnpm add @natify/geolocation-rn @react-native-community/geolocationiOS
Add permission in ios/YourApp/Info.plist:
<key>NSLocationWhenInUseUsageDescription</key>
<string>We need your location to show nearby places</string>
<!-- Optional: For background location -->
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>We need your location to track your location in real time</string>Then:
cd ios && pod install && cd ..Android
Add permission in android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />Usage
Provider Configuration
import { NatifyProvider } from "@natify/core";
import { RnGeolocationAdapter } from "@natify/geolocation-rn";
import { RnPermissionsAdapter } from "@natify/permissions-rn";
const config = {
geolocation: new RnGeolocationAdapter(),
permissions: new RnPermissionsAdapter(),
// ... other adapters
};
function App() {
return (
<NatifyProvider config={config}>
<MyApp />
</NatifyProvider>
);
}Get Current Location
Basic Usage
import { useAdapter, GeolocationPort } from "@natify/core";
function MyComponent() {
const geolocation = useAdapter<GeolocationPort>("geolocation");
const getCurrentLocation = async () => {
try {
const currentLocation = await geolocation.getCurrentPosition();
console.log("Latitude:", currentLocation.latitude);
console.log("Longitude:", currentLocation.longitude);
console.log("Accuracy:", currentLocation.accuracy, "meters");
} catch (error) {
console.error("Error getting location:", error);
}
};
return <Button onPress={getCurrentLocation} title="Get Location" />;
}With Accuracy Options
// High accuracy (GPS)
const location = await locationService.getCurrentPosition({
enableHighAccuracy: true,
timeout: 20000,
maximumAge: 0, // Always get new location
});
// Low accuracy (faster, less battery)
const location = await locationService.getCurrentPosition({
enableHighAccuracy: false,
timeout: 10000,
maximumAge: 60000, // Accept location up to 1 minute old
});With Permission Check
import { useAdapter, GeolocationPort, PermissionPort, PermissionStatus } from "@natify/core";
function LocationButton() {
const geolocation = useAdapter<GeolocationPort>("geolocation");
const permissions = useAdapter<PermissionPort>("permissions");
const requestLocation = async () => {
// Check permission
const status = await permissions.check("location");
if (status !== PermissionStatus.GRANTED) {
// Request permission
const newStatus = await permissions.request("location");
if (newStatus !== PermissionStatus.GRANTED) {
alert("Location permission required");
return;
}
}
// Check if service is enabled
const isEnabled = await geolocation.isLocationEnabled();
if (!isEnabled) {
alert("Please enable location services");
return;
}
// Get location
try {
const currentLocation = await geolocation.getCurrentPosition();
console.log("Location:", currentLocation);
} catch (error) {
console.error("Error:", error);
}
};
return <Button onPress={requestLocation} title="Get Location" />;
}Watch Location Changes
Simple Watch Position
function LocationTracker() {
const geolocation = useAdapter<GeolocationPort>("geolocation");
const [currentLocation, setCurrentLocation] = useState<Location | null>(null);
useEffect(() => {
const stopWatching = geolocation.watchPosition((newLocation) => {
setCurrentLocation(newLocation);
console.log("New location:", newLocation);
});
// Cleanup on unmount
return stopWatching;
}, []);
return (
<View>
{currentLocation && (
<Text>
Lat: {currentLocation.latitude}, Lng: {currentLocation.longitude}
</Text>
)}
</View>
);
}With Distance Filter
// Only update if device moves more than 10 meters
const stopWatching = geolocation.watchPosition(
(newLocation) => {
console.log("Location updated:", newLocation);
},
{
enableHighAccuracy: true,
distanceFilter: 10, // 10 meters
interval: 5000, // Minimum 5 seconds between updates
}
);For Real-Time Navigation
function NavigationScreen() {
const geolocation = useAdapter<GeolocationPort>("geolocation");
const [route, setRoute] = useState<Location[]>([]);
useEffect(() => {
const stopWatching = geolocation.watchPosition(
(newLocation) => {
// Add to route
setRoute((prev) => [...prev, newLocation]);
// Update map
updateMapMarker(newLocation);
},
{
enableHighAccuracy: true,
distanceFilter: 5, // Update every 5 meters
interval: 1000, // Maximum 1 update per second
}
);
return stopWatching;
}, []);
return <MapView route={route} />;
}Calculate Distance
Distance Between Two Points
const geolocation = useAdapter<GeolocationPort>("geolocation");
// Origin and destination coordinates
const origin: Coordinates = {
latitude: 40.7128,
longitude: -74.0060,
accuracy: 10,
};
const destination: Coordinates = {
latitude: 40.7589,
longitude: -73.9851,
accuracy: 10,
};
// Calculate distance in meters
const distance = geolocation.calculateDistance(origin, destination);
console.log(`Distance: ${distance.toFixed(2)} meters`);
console.log(`Distance: ${(distance / 1000).toFixed(2)} kilometers`);Distance to Multiple Points
const findNearestPlace = async (places: Array<{ name: string; coordinates: Coordinates }>) => {
const currentLocation = await geolocation.getCurrentPosition();
const distances = places.map((place) => ({
...place,
distance: geolocation.calculateDistance(currentLocation, place.coordinates),
}));
// Sort by distance
distances.sort((a, b) => a.distance - b.distance);
return distances[0]; // Nearest one
};Calculate Bearing (Direction)
const geolocation = useAdapter<GeolocationPort>("geolocation");
const origin: Coordinates = {
latitude: 40.7128,
longitude: -74.0060,
accuracy: 10,
};
const destination: Coordinates = {
latitude: 40.7589,
longitude: -73.9851,
accuracy: 10,
};
// Calculate direction in degrees (0-360)
const bearing = geolocation.calculateBearing(origin, destination);
console.log(`Bearing: ${bearing.toFixed(2)}°`);
// Convert to cardinal direction
const directions = ["N", "NE", "E", "SE", "S", "SW", "W", "NW"];
const direction = directions[Math.round(bearing / 45) % 8];
console.log(`Direction: ${direction}`);Common Use Cases
Check If User Is Near a Place
const checkIfNearPlace = async (
placeCoordinates: Coordinates,
radiusMeters: number
): Promise<boolean> => {
const currentLocation = await geolocation.getCurrentPosition();
const distance = geolocation.calculateDistance(currentLocation, placeCoordinates);
return distance <= radiusMeters;
};Route Tracking
function RouteTracker() {
const geolocation = useAdapter<GeolocationPort>("geolocation");
const [route, setRoute] = useState<Location[]>([]);
const [isTracking, setIsTracking] = useState(false);
const stopWatchingRef = useRef<(() => void) | null>(null);
const startTracking = () => {
setIsTracking(true);
setRoute([]);
stopWatchingRef.current = geolocation.watchPosition(
(newLocation) => {
setRoute((prev) => [...prev, newLocation]);
},
{
enableHighAccuracy: true,
distanceFilter: 5, // Record every 5 meters
interval: 2000,
}
);
};
const stopTracking = () => {
setIsTracking(false);
stopWatchingRef.current?.();
stopWatchingRef.current = null;
};
const calculateTotalDistance = () => {
if (route.length < 2) return 0;
let total = 0;
for (let i = 1; i < route.length; i++) {
total += geolocation.calculateDistance(route[i - 1], route[i]);
}
return total;
};
return (
<View>
<Button
title={isTracking ? "Stop Tracking" : "Start Tracking"}
onPress={isTracking ? stopTracking : startTracking}
/>
{route.length > 0 && (
<Text>Distance: {(calculateTotalDistance() / 1000).toFixed(2)} km</Text>
)}
</View>
);
}Geofencing (Zone Entry/Exit Detection)
function GeofenceMonitor({ center, radius }: { center: Coordinates; radius: number }) {
const geolocation = useAdapter<GeolocationPort>("geolocation");
const [isInside, setIsInside] = useState(false);
useEffect(() => {
const stopWatching = geolocation.watchPosition(
(newLocation) => {
const distance = geolocation.calculateDistance(newLocation, center);
const inside = distance <= radius;
if (inside !== isInside) {
setIsInside(inside);
if (inside) {
console.log("Entered geofence!");
onEnterGeofence();
} else {
console.log("Exited geofence!");
onExitGeofence();
}
}
},
{
enableHighAccuracy: true,
distanceFilter: 10, // Check every 10 meters
}
);
return stopWatching;
}, [center, radius]);
return null;
}Location UseCase
import { GeolocationPort } from "@natify/core";
export class GetNearbyPlacesUseCase {
constructor(private readonly geolocation: GeolocationPort) {}
async execute(radiusKm: number): Promise<Place[]> {
// Get current location
const currentLocation = await this.geolocation.getCurrentPosition({
enableHighAccuracy: true,
});
// Get places from API
const allPlaces = await this.fetchPlacesFromAPI();
// Filter by distance
const nearbyPlaces = allPlaces.filter((place) => {
const distance = this.geolocation.calculateDistance(
currentLocation,
place.coordinates
);
return distance <= radiusKm * 1000; // Convert km to meters
});
// Sort by distance
nearbyPlaces.sort((a, b) => {
const distA = this.geolocation.calculateDistance(currentLocation, a.coordinates);
const distB = this.geolocation.calculateDistance(currentLocation, b.coordinates);
return distA - distB;
});
return nearbyPlaces;
}
}API
GeolocationPort
| Method | Description |
|--------|-------------|
| getCurrentPosition(options?) | Gets current location |
| watchPosition(callback, options?) | Watches location changes |
| isLocationEnabled() | Checks if service is enabled |
| calculateDistance(from, to) | Calculates distance in meters |
| calculateBearing(from, to) | Calculates direction in degrees |
LocationOptions
interface LocationOptions {
enableHighAccuracy?: boolean; // default: true
timeout?: number; // default: 15000 (15 seconds)
maximumAge?: number; // default: 0 (always new)
}WatchLocationOptions
interface WatchLocationOptions extends LocationOptions {
distanceFilter?: number; // default: 0 (all updates)
interval?: number; // default: 1000 (1 second)
}Location
interface Location extends Coordinates {
timestamp: number;
}
interface Coordinates {
latitude: number;
longitude: number;
accuracy: number;
altitude?: number;
altitudeAccuracy?: number;
verticalAccuracy?: number;
heading?: number;
speed?: number;
}Notes
- Permissions: Always check and request permissions before using location
- Battery: Use
enableHighAccuracy: falseanddistanceFilterto save battery - Accuracy:
enableHighAccuracy: trueuses GPS (more accurate but consumes more battery) - Timeout: Set a reasonable timeout to avoid infinite waits
- Watch Position: Always clean up watchers in
useEffectcleanup - iOS: Requires configuration in
Info.plist - Android: Requires permissions in
AndroidManifest.xml
Module Integration
import { createModule } from "@natify/core";
import { GetNearbyPlacesUseCase } from "./usecases/GetNearbyPlacesUseCase";
export const PlacesModule = createModule("places", "Places")
.requires("geolocation", "permissions")
.useCase("getNearbyPlaces", (adapters) => {
return new GetNearbyPlacesUseCase(adapters.geolocation);
})
.build();