Апп зөвшөөрөл
Өмнөх хичээлүүдэд камер, байршил, notification тус тусдаа зөвшөөрөл хэрхэн авахыг сурсан. Гэвч жинхэнэ аппликейшн олон зөвшөөрөл удирдах шаардлагатай байдаг. Энэ хичээлд зөвшөөрлийн ерөнхий зарчим, expo-modules-core болон тус бүрийн package-н hook-уудыг нэгтгэн системтэй удирдах аргыг сурна.
Зөвшөөрлийн гурван байдал
iOS болон Android хоёулан зөвшөөрлийн гурван байдал байдаг:
| Байдал | Утга |
| -------------- | ------------------------------------------------------ |
| undetermined | Хэрэглэгч хариулаагүй — анх удаа асуух боломжтой |
| granted | Зөвшөөрсөн — feature ажиллуулж болно |
| denied | Татгалзсан — системийн Settings рүү чиглүүлэх хэрэгтэй |
Хамгийн чухал дүрэм: татгалзсан бол дахин асуух боломжгүй — хэрэглэгчийг Settings рүү гарна.
Зөвшөөрлийг системтэй удирдах
Бүх зөвшөөрлийн логикийг нэг custom hook-т цуглуулна:
import * as ImagePicker from "expo-image-picker";
import * as Location from "expo-location";
import * as Notifications from "expo-notifications";
import { Alert, Linking, Platform } from "react-native";
// Settings нээх — татгалзсан зөвшөөрлийн үед
const openSettings = () => {
if (Platform.OS === "ios") {
Linking.openURL("app-settings:");
} else {
Linking.openSettings();
}
};
// Зөвшөөрөл татгалзсан үед стандарт мэдэгдэл
const showDeniedAlert = (featureName) => {
Alert.alert(
"Зөвшөөрөл шаардлагатай",
`${featureName} ашиглахын тулд утасны тохиргоонд зөвшөөрнө үү.`,
[
{ text: "Болих", style: "cancel" },
{ text: "Тохиргоо нээх", onPress: openSettings },
],
);
};
// Камерын зөвшөөрөл
export async function requestCamera() {
const { status } = await ImagePicker.requestCameraPermissionsAsync();
if (status === "denied") {
showDeniedAlert("Камер");
return false;
}
return status === "granted";
}
// Галерейн зөвшөөрөл
export async function requestGallery() {
const { status } = await ImagePicker.requestMediaLibraryPermissionsAsync();
if (status === "denied") {
showDeniedAlert("Зургийн цомог");
return false;
}
return status === "granted";
}
// Байршлын зөвшөөрөл
export async function requestLocation() {
const { status } = await Location.requestForegroundPermissionsAsync();
if (status === "denied") {
showDeniedAlert("Байршил");
return false;
}
return status === "granted";
}
// Notification-н зөвшөөрөл
export async function requestNotifications() {
const { status } = await Notifications.requestPermissionsAsync();
if (status === "denied") {
showDeniedAlert("Мэдэгдэл");
return false;
}
return status === "granted";
}
Зөвшөөрлийн dashboard харуулах
Аппликейшн нь хэрэглэгчид ямар зөвшөөрлүүд идэвхтэй байгааг нэг дэлгэцэнд харуулах нь сайн UX:
import { useState, useEffect, useCallback } from "react";
import * as ImagePicker from "expo-image-picker";
import * as Location from "expo-location";
import * as Notifications from "expo-notifications";
import {
View,
Text,
TouchableOpacity,
ScrollView,
StyleSheet,
Linking,
Platform,
} from "react-native";
const PERMISSIONS = [
{
key: "camera",
label: "Камер",
icon: "📷",
description: "Зураг авах, QR код уншихад хэрэгтэй",
check: () => ImagePicker.getCameraPermissionsAsync(),
request: () => ImagePicker.requestCameraPermissionsAsync(),
},
{
key: "gallery",
label: "Зургийн цомог",
icon: "🖼️",
description: "Галерейгаас зураг сонгоход хэрэгтэй",
check: () => ImagePicker.getMediaLibraryPermissionsAsync(),
request: () => ImagePicker.requestMediaLibraryPermissionsAsync(),
},
{
key: "location",
label: "Байршил",
icon: "📍",
description: "Ойролцоох газруудыг харуулахад хэрэгтэй",
check: () => Location.getForegroundPermissionsAsync(),
request: () => Location.requestForegroundPermissionsAsync(),
},
{
key: "notifications",
label: "Мэдэгдэл",
icon: "🔔",
description: "Захиалга, мессеж мэдэгдлийг хүлээн авахад",
check: () => Notifications.getPermissionsAsync(),
request: () => Notifications.requestPermissionsAsync(),
},
];
function PermissionRow({ perm, status, onRequest }) {
const isGranted = status === "granted";
const isDenied = status === "denied";
return (
<View style={styles.row}>
<Text style={styles.icon}>{perm.icon}</Text>
<View style={styles.rowInfo}>
<Text style={styles.rowLabel}>{perm.label}</Text>
<Text style={styles.rowDesc}>{perm.description}</Text>
</View>
<TouchableOpacity
style={[
styles.badge,
isGranted ? styles.badgeGranted : styles.badgeDenied,
]}
onPress={isGranted ? undefined : onRequest}
>
<Text
style={[
styles.badgeText,
isGranted ? styles.badgeTextGranted : styles.badgeTextDenied,
]}
>
{isGranted ? "Зөвшөөрсөн" : isDenied ? "Татгалзсан" : "Асуух"}
</Text>
</TouchableOpacity>
</View>
);
}
export default function PermissionsScreen() {
const [statuses, setStatuses] = useState({});
const checkAll = useCallback(async () => {
const results = await Promise.all(
PERMISSIONS.map(async (p) => {
const { status } = await p.check();
return [p.key, status];
}),
);
setStatuses(Object.fromEntries(results));
}, []);
useEffect(() => {
checkAll();
}, []);
const handleRequest = async (perm) => {
if (statuses[perm.key] === "denied") {
Linking.openSettings();
return;
}
await perm.request();
await checkAll();
};
return (
<ScrollView style={styles.scroll} contentContainerStyle={styles.content}>
<Text style={styles.heading}>Апп зөвшөөрлүүд</Text>
<Text style={styles.subtitle}>
Аппликейшн бүрэн ажиллахын тулд дараах зөвшөөрлүүд шаардлагатай
</Text>
{PERMISSIONS.map((perm) => (
<PermissionRow
key={perm.key}
perm={perm}
status={statuses[perm.key]}
onRequest={() => handleRequest(perm)}
/>
))}
</ScrollView>
);
}
const styles = StyleSheet.create({
scroll: { flex: 1, backgroundColor: "#0b1120" },
content: { padding: 24, gap: 12 },
heading: {
fontSize: 24,
fontWeight: "bold",
color: "#f1f5f9",
marginBottom: 4,
},
subtitle: {
color: "#94a3b8",
fontSize: 14,
lineHeight: 20,
marginBottom: 12,
},
row: {
flexDirection: "row",
alignItems: "center",
backgroundColor: "#0f172a",
borderRadius: 12,
padding: 14,
gap: 12,
borderWidth: 1,
borderColor: "#1e293b",
},
icon: { fontSize: 24, width: 32, textAlign: "center" },
rowInfo: { flex: 1 },
rowLabel: { color: "#f1f5f9", fontSize: 15, fontWeight: "600" },
rowDesc: { color: "#475569", fontSize: 12, marginTop: 2, lineHeight: 16 },
badge: {
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: 6,
},
badgeGranted: { backgroundColor: "#052e16" },
badgeDenied: {
backgroundColor: "#1e293b",
borderWidth: 1,
borderColor: "#475569",
},
badgeText: { fontSize: 12, fontWeight: "600" },
badgeTextGranted: { color: "#4ade80" },
badgeTextDenied: { color: "#94a3b8" },
});
app.json дахь зөвшөөрлийн тайлбар
EAS Build-р App Store/Play Store-д нийтлэхэд зөвшөөрл бүрт тайлбар нэмэх шаардлагатай. iOS-д infoPlist, Android-д permissions ашиглана:
{
"expo": {
"ios": {
"infoPlist": {
"NSCameraUsageDescription": "Профайл зураг авахад камер ашиглана",
"NSPhotoLibraryUsageDescription": "Зургийн цомгоос профайл зураг сонгоход ашиглана",
"NSLocationWhenInUseUsageDescription": "Ойролцоох дэлгүүр харуулахад байршил ашиглана",
"NSMicrophoneUsageDescription": "Видео бичихэд микрофон ашиглана"
}
},
"android": {
"permissions": [
"android.permission.CAMERA",
"android.permission.READ_MEDIA_IMAGES",
"android.permission.ACCESS_FINE_LOCATION",
"android.permission.RECORD_AUDIO"
]
}
}
}
Зөвшөөрлийн тайлбарыг Монгол хэлээр бичихийг мартуузай. App Store шалгах үед энэ тайлбарыг уншиж баталгаажуулдаг — тодорхой, үнэн мэдээлэл бичих нь апп-г зөвшөөрүүлэхэд тусална.
Дараагийн хичээлд:
Supabase + React Native — бэлэн backend ашиглан өгөгдлийн сан, authentication, файл хадгалалтыг мобайл апп-тай холбоно.