الوحدة 4 · البيانات والجهاز
الأذونات: الطلب في اللحظة المناسبة
17 دقراءة 3 دبيئة تجريب الكود
الدرس في لمحة
اطلب الوصول إلى الموقع والكاميرا في اللحظة التي يكون فيها الطلب منطقيًا، وتعامل مع كل حالات الإذن بما فيها الرفض الدائم، واكتب أوصاف الاستخدام التي يشترطها iOS وتشترطها مراجعة المتاجر.
- 1الحالات التي يمكن أن يكون فيها الإذن
- 2اسأل في السياق، بعد أن تشرح
- 3أوصاف الاستخدام والـ config plugins
- 4المقدّمة أولًا، والخلفية فقط عند الضرورة
لا فيديو هنا، بل الشيء الحقيقي: درس مكتوب وأمثلة قابلة للتشغيل واختبار وتدريب.
أفضل ميزة في Trailhead هي التتبّع الحيّ: ابدأ رحلة، فيسجّل التطبيق مسارك. يحتاج ذلك إلى موقع الهاتف، والهاتف لن يعطيه دون أن يسأل المستخدم. وهذا الطلب أكثر لحظة هشاشة في التطبيق. اسأل بطريقة سيئة فتخسر الميزة لدى ذلك المستخدم، وأحيانًا إلى الأبد.
الحالات التي يمكن أن يكون فيها الإذن
كل وحدة في Expo تحتاج إلى إذن (الموقع، الكاميرا، مكتبة الوسائط، الإشعارات) تُرجع شكل الاستجابة نفسه:
{
status: 'undetermined' | 'granted' | 'denied',
granted: boolean,
canAskAgain: boolean,
expires: 'never' | number,
}- undetermined: لم تسأل قط. والطلب سيعرض نافذة النظام.
- granted: تابع. وفي إصدارات iOS وAndroid الحديثة، قد يمنح المستخدمون الوصول مرة واحدة فقط (لهذه الجلسة)، أو الموقع التقريبي فقط، فكن مستعدًا لأن تسأل مرة أخرى في يوم آخر، وأن تتلقّى مواقع أقل دقّة.
- denied مع
canAskAgain: true: Android بعد رفض واحد. يمكنك أن تسأل مرة أخرى، ويُفضَّل بعد شرح أفضل. - denied مع
canAskAgain: false: لن يعرض النظام النافذة مرة أخرى. على iOS هذه هي الحالة مباشرة بعد أول رفض؛ وعلى Android بعد أن يرفض المستخدم مرتين. ولا يستطيع تغييرها الآن إلا تطبيق الإعدادات.
اسأل في السياق، بعد أن تشرح
أسوأ نمط هو الذي تطلقه تطبيقات كثيرة: سلسلة من نوافذ النظام عند أول تشغيل، قبل أن يعرف المستخدم ما يفعله التطبيق. الناس يرفضون ما لا يفهمونه، وعلى iOS يكون ذلك الـ “Don’t Allow” الأول دائمًا ما لم ينقّبوا في الإعدادات.
يطلب Trailhead الموقع حين ينقر المستخدم على Start tracking، وهي أول لحظة يرتبط فيها الطلب بوضوح بشيء يريده. وقبل نافذة النظام، يعرض شرحه القصير الخاص: “Trailhead records your route while you hike. Your location stays on this phone unless you sync.” ثم يُطلق زرّ Continue الطلب الحقيقي. شاشتك أنت يمكن إغلاقها دون عواقب؛ أما نافذة النظام فلا.
والقاعدة نفسها تنطبق على كل إذن في التطبيق. اطلب الكاميرا حين ينقر المستخدم على Add photo، والإشعارات حين يفعّل التذكيرات، لا قبل ذلك. عندها يصل كل طلب وسببه ظاهر على الشاشة أصلًا، وترتفع نسب القبول بشكل ملحوظ.
import * as Location from 'expo-location';
import { useFocusEffect } from 'expo-router';
import { useCallback, useState } from 'react';
import { Linking } from 'react-native';
export function useLocationPermission() {
const [permission, setPermission] = useState<Location.LocationPermissionResponse | null>(null);
// Re-check whenever the screen regains focus: the user may have
// changed the setting in the Settings app while Trailhead was in the background.
useFocusEffect(
useCallback(() => {
Location.getForegroundPermissionsAsync().then(setPermission);
}, []),
);
const request = useCallback(async () => {
const result = await Location.requestForegroundPermissionsAsync();
setPermission(result);
return result.granted;
}, []);
return { permission, request, openSettings: () => Linking.openSettings() };
}ثم تقرأ شاشة التتبّع permission وتختار ما ترسمه: مؤشّر تحميل ما دامت null، والشرح مع زرّ Continue ما دام الطلب ممكنًا، ورسالة “Location is off for Trailhead” مع زرّ Open Settings بمجرد أن تصبح canAskAgain قيمتها false، والخريطة الحيّة بمجرد منح الإذن. وتحويل الاستجابة إلى واحد من هذه الأفعال الأربعة دالة نقية صغيرة، وهي تمرين هذا الدرس.
أوصاف الاستخدام والـ config plugins
يعرض iOS جملة من تطبيقك في كل نافذة إذن، ويرفض الـ App Store التطبيقات التي تكون أوصافها مفقودة أو مبهمة (“This app needs your location”). قل ما الذي تفعله الميزة بالبيانات. تضبط هذه النصوص عبر الـ config plugin الخاص بكل وحدة في app.json:
{
"expo": {
"plugins": [
[
"expo-location",
{ "locationWhenInUsePermission": "Trailhead records your route on the map while you track a hike." }
],
[
"expo-image-picker",
{
"photosPermission": "Trailhead lets you attach photos from your library to a hike.",
"cameraPermission": "Trailhead lets you take trail photos to attach to a hike."
}
]
]
}
}تكتب الـ config plugins الإعدادات الأصلية (ملف Info.plist في iOS، والـ manifest في Android) حين يُبنى التطبيق. وهذا يجعلها تغييرات أصلية: لن يطبّقها Fast Refresh، فأعِد بناء الـ development build بعد تعديلها. ويعرض Expo Go نصوصه العامة الخاصة، وهذا سبب إضافي لانتقال Trailhead الآن إلى development build.
المقدّمة أولًا، والخلفية فقط عند الضرورة
تتبّع رحلة والشاشة مطفأة يحتاج إلى موقع في الخلفية (background)، وهو إذن منفصل وأكثر حساسية، له متطلّبات إضافية من المنصّتين ومراجعة أدقّ في المتجرين. ابدأ بالوصول في المقدّمة (“while using the app”)، الذي يغطّي التتبّع ما دام Trailhead مفتوحًا. وأضف الوصول في الخلفية فقط حين يطلبه المستخدمون، مع شرح واضح للسبب، وتوقّع أن تطرح عليك المراجعة السؤال نفسه.
في الدرس التالي ستستخدم هذه الأذونات فعلًا: موقع المتنزّه، وصور الدروب، وإشعار تذكير.
أهم النقاط
- استجابات الأذونات لها
statusقيمتها undetermined أو granted أو denied، إضافةً إلىcanAskAgain، التي تخبرك هل ما زال يمكن أن تظهر نافذة النظام. - اطلب الإذن حين يبدأ المستخدم الميزة التي تحتاج إليه، بعد شرح قصير في واجهتك أنت، ولا تطلق سيلًا من النوافذ عند التشغيل أبدًا.
- بمجرد أن تصبح
canAskAgainقيمتها false، لا يستطيع تغيير الإجابة إلا تطبيق الإعدادات في النظام، فقدّم زرًّا يستدعيLinking.openSettings(). - يشترط iOS وصفًا للاستخدام لكل إذن؛ اضبطها عبر خيارات الـ config plugins في app.json وأعد البناء.
- أعِد التحقّق من الأذونات حين تستعيد الشاشة التركيز، لأن المستخدمين يغيّرونها في الإعدادات بينما تطبيقك في الخلفية.