Expo Location (геолокаци)
Хэрэглэгчийн байршлыг мэдэх нь delivery апп, цаг агаар апп, газрын зураг — олон төрлийн аппликейшнд зайлшгүй шаардлагатай. expo-location package нь GPS координат авах, хаягт хөрвүүлэх (reverse geocoding), байршлын өөрчлөлтийг дагах зэрэг бүх функцийг агуулна.
expo-location суулгах
npx expo install expo-location
Суулгасны дараа iOS болон Android хоёулан байршлын зөвшөөрөл шаардана. Зөвшөөрлийг requestForegroundPermissionsAsync() функцээр авна — энэ нь апп ажиллаж байх үеийн байршил юм. Дэлгэрэнгүй зөвшөөрлийн талаар 35-р хичээлд сурна.
Одоогийн байршил авах
getCurrentPositionAsync() нь хэрэглэгчийн GPS координатыг нэг удаа авна. Энэ нь latitude (өргөрөг) ба longitude (уртраг) агуулсан объект буцаана:
import { useState } from "react";
import * as Location from "expo-location";
import {
View,
Text,
TouchableOpacity,
ActivityIndicator,
StyleSheet,
} from "react-native";
export default function MyLocation() {
const [location, setLocation] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const getLocation = async () => {
setLoading(true);
setError(null);
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") {
setError("Байршлын зөвшөөрөл олгогдоогүй");
return;
}
const loc = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.High,
});
setLocation(loc.coords);
} catch (err) {
setError("Байршил авахад алдаа гарлаа");
} finally {
setLoading(false);
}
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.btn}
onPress={getLocation}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#0b1120" />
) : (
<Text style={styles.btnText}>📍 Байршил авах</Text>
)}
</TouchableOpacity>
{error && <Text style={styles.error}>{error}</Text>}
{location && (
<View style={styles.card}>
<Text style={styles.cardLabel}>Өргөрөг (Latitude)</Text>
<Text style={styles.cardValue}>{location.latitude.toFixed(6)}</Text>
<Text style={styles.cardLabel}>Уртраг (Longitude)</Text>
<Text style={styles.cardValue}>{location.longitude.toFixed(6)}</Text>
<Text style={styles.cardLabel}>Нарийвчлал (Accuracy)</Text>
<Text style={styles.cardValue}>
±{location.accuracy?.toFixed(0)} м
</Text>
</View>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#0b1120",
padding: 24,
gap: 20,
},
btn: {
backgroundColor: "#22d3ee",
paddingHorizontal: 28,
paddingVertical: 14,
borderRadius: 12,
minWidth: 180,
alignItems: "center",
},
btnText: {
color: "#0b1120",
fontWeight: "700",
fontSize: 16,
},
error: {
color: "#fb7185",
fontSize: 14,
textAlign: "center",
},
card: {
backgroundColor: "#0f172a",
borderRadius: 14,
padding: 20,
width: "100%",
borderWidth: 1,
borderColor: "#1e293b",
gap: 4,
},
cardLabel: {
color: "#475569",
fontSize: 12,
marginTop: 10,
},
cardValue: {
color: "#22d3ee",
fontSize: 16,
fontWeight: "600",
fontVariant: ["tabular-nums"],
},
});
Accuracy.High нь GPS ашиглан хамгийн нарийн байршил авах тохиргоо. Батарей хэмнэх бол Accuracy.Balanced эсвэл Accuracy.Low ашиглаж болно.
Reverse geocoding — координатыг хаягт хөрвүүлэх
GPS координат хэрэглэгчид утгагүй харагддаг. reverseGeocodeAsync() функц нь координатыг уншигдахуйц хаягт хөрвүүлнэ:
import { useState } from "react";
import * as Location from "expo-location";
import {
View,
Text,
TouchableOpacity,
ActivityIndicator,
StyleSheet,
} from "react-native";
export default function AddressLookup() {
const [address, setAddress] = useState(null);
const [loading, setLoading] = useState(false);
const findAddress = async () => {
setLoading(true);
try {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted") return;
const loc = await Location.getCurrentPositionAsync({
accuracy: Location.Accuracy.Balanced,
});
const places = await Location.reverseGeocodeAsync({
latitude: loc.coords.latitude,
longitude: loc.coords.longitude,
});
if (places.length > 0) {
setAddress(places[0]);
}
} finally {
setLoading(false);
}
};
const formatAddress = (addr) => {
const parts = [
addr.name,
addr.street,
addr.district,
addr.city,
addr.country,
].filter(Boolean);
return parts.join(", ");
};
return (
<View style={styles.container}>
<TouchableOpacity
style={styles.btn}
onPress={findAddress}
disabled={loading}
>
{loading ? (
<ActivityIndicator color="#0b1120" />
) : (
<Text style={styles.btnText}>🗺️ Хаяг олох</Text>
)}
</TouchableOpacity>
{address && (
<View style={styles.result}>
<Text style={styles.resultTitle}>Таны байршил</Text>
<Text style={styles.resultAddress}>{formatAddress(address)}</Text>
<View style={styles.row}>
<Detail label="Хот" value={address.city} />
<Detail label="Улс" value={address.country} />
</View>
</View>
)}
</View>
);
}
function Detail({ label, value }) {
return (
<View style={styles.detail}>
<Text style={styles.detailLabel}>{label}</Text>
<Text style={styles.detailValue}>{value ?? "—"}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#0b1120",
padding: 24,
justifyContent: "center",
gap: 20,
},
btn: {
backgroundColor: "#22d3ee",
padding: 16,
borderRadius: 12,
alignItems: "center",
},
btnText: {
color: "#0b1120",
fontWeight: "700",
fontSize: 16,
},
result: {
backgroundColor: "#0f172a",
borderRadius: 14,
padding: 20,
borderWidth: 1,
borderColor: "#1e293b",
gap: 8,
},
resultTitle: {
color: "#475569",
fontSize: 12,
fontWeight: "500",
},
resultAddress: {
color: "#f1f5f9",
fontSize: 15,
lineHeight: 22,
},
row: {
flexDirection: "row",
gap: 12,
marginTop: 8,
},
detail: {
flex: 1,
backgroundColor: "#0b1120",
borderRadius: 8,
padding: 10,
},
detailLabel: {
color: "#475569",
fontSize: 11,
marginBottom: 2,
},
detailValue: {
color: "#22d3ee",
fontSize: 14,
fontWeight: "600",
},
});
Байршлын өөрчлөлтийг дагах
Хэрэглэгч хөдөлж байх үед байршлыг тасралтгүй шинэчлэх бол watchPositionAsync() ашиглана. Апп хаагдах үед subscription-г цэвэрлэхээ мартуузай:
import { useState, useEffect, useRef } from "react";
import * as Location from "expo-location";
import { View, Text, StyleSheet } from "react-native";
export default function LiveTracker() {
const [coords, setCoords] = useState(null);
const subscriptionRef = useRef(null);
useEffect(() => {
let active = true;
(async () => {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status !== "granted" || !active) return;
subscriptionRef.current = await Location.watchPositionAsync(
{
accuracy: Location.Accuracy.High,
timeInterval: 3000, // 3 секунд тутамд
distanceInterval: 10, // эсвэл 10 метр зайд
},
(loc) => setCoords(loc.coords),
);
})();
return () => {
active = false;
subscriptionRef.current?.remove();
};
}, []);
return (
<View style={styles.container}>
<Text style={styles.label}>Шууд байршил</Text>
{coords ? (
<>
<Text style={styles.coord}>
{coords.latitude.toFixed(5)}, {coords.longitude.toFixed(5)}
</Text>
<Text style={styles.speed}>
Хурд:{" "}
{coords.speed != null
? (coords.speed * 3.6).toFixed(1) + " км/ц"
: "—"}
</Text>
</>
) : (
<Text style={styles.waiting}>GPS сигнал хайж байна...</Text>
)}
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#0b1120",
gap: 12,
},
label: {
color: "#475569",
fontSize: 13,
fontWeight: "500",
},
coord: {
color: "#22d3ee",
fontSize: 20,
fontWeight: "700",
fontVariant: ["tabular-nums"],
},
speed: {
color: "#94a3b8",
fontSize: 14,
},
waiting: {
color: "#475569",
fontSize: 14,
},
});
coords.speed нь метр/секундаар ирдэг тул × 3.6 хийж км/цагт хөрвүүлнэ. Байршлыг Монголд ашиглах жишээ: delivery апп дагаж буй жолооч, hiking trail tracker, нэг газраас нөгөөд хугацаа тооцоолох.
Дараагийн хичээлд:
Push Notification — хэрэглэгч апп нээлгүй байхад мэдэгдэл явуулах үндсэн функц сурна.